}
pub struct IntegerInfo {
- version: (i32, i32, i32),
- machine_code: i32,
- floating_point_rep: i32,
- compression_code: i32,
- endianness: i32,
- character_code: i32,
+ pub version: (i32, i32, i32),
+ pub machine_code: i32,
+ pub floating_point_rep: i32,
+ pub compression_code: i32,
+ pub endianness: i32,
+ pub character_code: i32,
}
impl ExtensionRecord for IntegerInfo {
}
pub struct FloatInfo {
- sysmis: f64,
- highest: f64,
- lowest: f64,
+ pub sysmis: f64,
+ pub highest: f64,
+ pub lowest: f64,
}
impl ExtensionRecord for FloatInfo {
}
pub struct VeryLongString {
- short_name: String,
- length: usize,
+ pub short_name: String,
+ pub length: usize,
}
impl VeryLongString {
}
}
+pub struct Attribute {
+ pub name: String,
+ pub values: Vec<String>,
+}
+
+impl Attribute {
+ fn parse<'a>(input: &'a str, warn: &impl Fn(Error)) -> Result<(Attribute, &'a str), Error> {
+ let Some((name, mut input)) = input.split_once('(') else {
+ return Err(Error::TBD);
+ };
+ let mut values = Vec::new();
+ loop {
+ let Some((value, rest)) = input.split_once('\n') else {
+ return Err(Error::TBD);
+ };
+ if let Some(stripped) = value
+ .strip_prefix('\'')
+ .and_then(|value| value.strip_suffix('\''))
+ {
+ values.push(stripped.into());
+ } else {
+ warn(Error::TBD);
+ values.push(value.into());
+ }
+ if let Some(rest) = rest.strip_prefix(')') {
+ return Ok((
+ Attribute {
+ name: name.into(),
+ values,
+ },
+ rest,
+ ));
+ }
+ input = rest;
+ }
+ }
+}
+
+pub struct AttributeSet(pub Vec<Attribute>);
+
+impl AttributeSet {
+ fn parse<'a>(
+ mut input: &'a str,
+ sentinel: Option<char>,
+ warn: &impl Fn(Error),
+ ) -> Result<(AttributeSet, &'a str), Error> {
+ let mut attributes = Vec::new();
+ let rest = loop {
+ match input.chars().next() {
+ None => break input,
+ c if c == sentinel => break &input[1..],
+ _ => {
+ let (attribute, rest) = Attribute::parse(input, &warn)?;
+ attributes.push(attribute);
+ input = rest;
+ }
+ }
+ };
+ Ok((AttributeSet(attributes), rest))
+ }
+}
+
+pub struct FileAttributeRecord(AttributeSet);
+
+impl TextRecord for FileAttributeRecord {
+ const NAME: &'static str = "data file attributes";
+ fn parse(input: &str, warn: impl Fn(Error)) -> Result<Self, Error> {
+ let (set, rest) = AttributeSet::parse(input, None, &warn)?;
+ if !rest.is_empty() {
+ warn(Error::TBD);
+ }
+ Ok(FileAttributeRecord(set))
+ }
+}
+
+pub struct VarAttributeSet {
+ pub long_var_name: String,
+ pub attributes: AttributeSet,
+}
+
+impl VarAttributeSet {
+ fn parse<'a>(
+ input: &'a str,
+ warn: &impl Fn(Error),
+ ) -> Result<(VarAttributeSet, &'a str), Error> {
+ let Some((long_var_name, rest)) = input.split_once(':') else {
+ return Err(Error::TBD);
+ };
+ let (attributes, rest) = AttributeSet::parse(rest, Some('/'), warn)?;
+ Ok((
+ VarAttributeSet {
+ long_var_name: long_var_name.into(),
+ attributes,
+ },
+ rest,
+ ))
+ }
+}
+
+pub struct VariableAttributeRecord(Vec<VarAttributeSet>);
+
+impl TextRecord for VariableAttributeRecord {
+ const NAME: &'static str = "variable attributes";
+ fn parse(mut input: &str, warn: impl Fn(Error)) -> Result<Self, Error> {
+ let mut var_attribute_sets = Vec::new();
+ while !input.is_empty() {
+ match VarAttributeSet::parse(input, &warn) {
+ Ok((var_attribute, rest)) => {
+ var_attribute_sets.push(var_attribute);
+ input = rest;
+ }
+ Err(error) => {
+ warn(error);
+ break;
+ }
+ }
+ }
+ Ok(VariableAttributeRecord(var_attribute_sets))
+ }
+}
+
pub struct NumberOfCasesRecord {
/// Always observed as 1.
- one: u64,
+ pub one: u64,
/// Number of cases.
- n_cases: u64,
+ pub n_cases: u64,
}
impl ExtensionRecord for NumberOfCasesRecord {