// `GroupDefn` is processed.
#[derive(Debug,Deserialize)]
pub struct GroupDefn {
- /// `files` is a multi-line string, each line of which has three
- /// fields (the first two terminated by whitespace). The fields
- /// are those in [`FileData`]. `#` comments are supported.
+ /// `files` is a multi-line string, each line of which has
+ /// (normally) three fields (the leading ones terminated by
+ /// whitespace). The fields are those in [`FileData`]. `#`
+ /// comments are supported.
///
/// Each non-empty non-comment line in `files` specifies a single
/// SVG to be made aavailable as a piece.
/// Item names are conventionally structured using a hierarchical
/// name with `-` between the components. Do not put `/` or `_` in
/// item names.
+ ///
+ /// It is also possible to specify additional data for each item by
+ /// adding fields to each line. This is done by adding a line at
+ /// the start starting with `:` and then additing one additional
+ /// whitespace separated value on each data line. Unknown
+ /// `fieldname` values are ignored.
+ ///
+ /// The values for these extra fields come just before the
+ /// dwscription, after the other whitespace-delimited fields, in the
+ /// same order as specified in the `:` heading line.
pub files: FileList,
/// See the discussioin of the item name.
/// Contents of each line in [`files`](GroupDefn::files)
///
-/// This is not a key value list. The first two fields are found by
+/// This is not a key value list. The leading fields are found by
/// splitting on whitespace, and the final field is the rest of the
/// line.
#[derive(Deserialize,Debug)]
#[cfg(doc)] pub r_file_spec: String,
#[cfg(not(doc))] pub r_file_spec: (),
+ /// Extra fields, normally not present.
+ pub extra_fields: HashMap<String, String>,
+
/// Desscription. (Shown hn the game log, for example.)
/// Will be HTML-escaped.
pub desc: String,
#[error("{:?}",&self)]
FilesListLineMissingWhitespace(usize),
#[error("{:?}",&self)]
+ FilesListFieldsMustBeAtStart(usize),
+ #[error("{:?}",&self)]
MissingSubstituionToken(&'static str),
#[error("{:?}",&self)]
RepeatedSubstituionToken(&'static str),
// #[throws(LLE)]
fn try_from(s: String) -> Result<FileList,LLE> {
let mut o = Vec::new();
+ let mut xfields = Vec::new();
for (lno,l) in s.lines().enumerate() {
let l = l.trim();
if l=="" || l.starts_with("#") { continue }
+ if let Some(xfields_spec) = l.strip_prefix(':') {
+ if ! (o.is_empty() && xfields.is_empty()) {
+ throw!(LLE::FilesListFieldsMustBeAtStart(lno));
+ }
+ xfields = xfields_spec.split_ascii_whitespace()
+ .filter(|s| !s.is_empty())
+ .map(|s| s.to_owned())
+ .collect::<Vec<_>>();
+ continue;
+ }
let mut remain = &*l;
let mut n = ||{
let ws = remain.find(char::is_whitespace)
};
let item_spec = n()?;
let _r_file_spec = n()?;
+ let extra_fields = xfields.iter()
+ .map(|field| Ok::<_,LLE>((field.to_owned(), n()?.to_owned())))
+ .collect::<Result<_,_>>()?;
let desc = remain.to_owned();
- o.push(FileData{ item_spec, r_file_spec: (), desc });
+ o.push(FileData{ item_spec, r_file_spec: (), extra_fields, desc });
}
Ok(FileList(o))
}