dictionary.file_label = Some(file_label);
}
- for attributes in headers.file_attributes.drain(..) {
- dictionary.attributes.extend(attributes.0 .0.into_iter())
+ for mut attributes in headers.file_attributes.drain(..) {
+ dictionary.attributes.append(&mut attributes.0)
}
// Concatenate all the document records (really there should only be one)
}
}
- for (k, v) in headers
- .file_attributes
- .iter()
- .flat_map(|map| map.0 .0.iter())
- {
- dictionary.attributes.insert(k.clone(), v.clone());
- }
-
- for attr_set in headers
+ for mut attr_set in headers
.variable_attributes
- .iter()
- .flat_map(|record| record.0.iter())
+ .drain(..)
+ .flat_map(|record| record.0.into_iter())
{
if let Some((_, variable)) = dictionary
.variables
.get_full_mut2(&attr_set.long_var_name.0)
{
- for (k, v) in attr_set.attributes.0.iter() {
- variable.attributes.insert(k.clone(), v.clone());
- }
+ variable.attributes.append(&mut attr_set.attributes);
} else {
warn(Error::TBD);
}
}
+ // Assign variable roles.
+ for index in 0..dictionary.variables.len() {
+ let mut variable = dictionary.variables.get_index_mut2(index).unwrap();
+ match variable.attributes.role() {
+ Ok(role) => variable.role = role,
+ Err(()) => warn(Error::TBD),
+ }
+ }
+
+ // Long string value labels.
+ for record in headers
+ .long_string_value_labels
+ .drain(..)
+ .flat_map(|record| record.0.into_iter())
+ {
+ let Some((_, variable)) = dictionary.variables.get_full_mut2(&record.var_name.0) else {
+ warn(Error::TBD);
+ continue;
+ };
+ for (value, label) in record.labels.into_iter() {
+ let value = Value::
+ variable.value_labels.insert(value)
+ }
+ }
+
let metadata = Metadata::decode(&headers, warn);
Ok((dictionary, metadata))
}
pub file_label: Option<String>,
pub documents: Vec<String>,
pub vectors: HashSet<ByIdentifier<Vector>>,
- pub attributes: HashMap<Identifier, Vec<String>>,
+ pub attributes: Attributes,
pub mrsets: HashSet<ByIdentifier<MultipleResponseSet>>,
pub variable_sets: HashSet<ByIdentifier<VariableSet>>,
pub encoding: &'static Encoding,
file_label: None,
documents: Vec::new(),
vectors: HashSet::new(),
- attributes: HashMap::new(),
+ attributes: Attributes::new(),
mrsets: HashSet::new(),
variable_sets: HashSet::new(),
encoding,
Input,
Target,
Both,
- None,
Partition,
Split,
}
+impl Role {
+ fn try_from_str(input: &str) -> Result<Option<Role>, ()> {
+ for (string, value) in [
+ ("input", Some(Role::Input)),
+ ("target", Some(Role::Target)),
+ ("both", Some(Role::Both)),
+ ("partition", Some(Role::Partition)),
+ ("split", Some(Role::Split)),
+ ("none", None),
+ ] {
+ if string.eq_ignore_ascii_case(input) {
+ return Ok(value);
+ }
+ }
+ Err(())
+ }
+}
+
+#[derive(Clone, Debug, Default, PartialEq, Eq)]
+pub struct Attributes(pub HashMap<Identifier, Vec<String>>);
+
+impl Attributes {
+ pub fn new() -> Self {
+ Self(HashMap::new())
+ }
+
+ pub fn append(&mut self, other: &mut Self) {
+ self.0.extend(other.0.drain())
+ }
+
+ pub fn role(&self) -> Result<Option<Role>, ()> {
+ self.try_into()
+ }
+}
+
+impl TryFrom<&Attributes> for Option<Role> {
+ type Error = ();
+
+ fn try_from(value: &Attributes) -> Result<Self, Self::Error> {
+ let role = Identifier::new("$@Role").unwrap();
+ value.0.get(&role).map_or(Ok(None), |attribute| {
+ if let Ok([string]) = <&[String; 1]>::try_from(attribute.as_slice()) {
+ Role::try_from_str(string)
+ } else {
+ Err(())
+ }
+ })
+ }
+}
+
#[derive(Clone, Debug)]
pub struct Variable {
pub name: Identifier,
pub value_labels: HashMap<Value, String>,
pub label: Option<String>,
pub measure: Option<Measure>,
- pub role: Role,
+ pub role: Option<Role>,
pub display_width: u32,
pub alignment: Alignment,
pub leave: bool,
pub short_names: Vec<Identifier>,
- pub attributes: HashMap<Identifier, Vec<String>>,
+ pub attributes: Attributes,
}
impl Variable {
value_labels: HashMap::new(),
label: None,
measure: Measure::default_for_type(var_type),
- role: Role::default(),
+ role: None,
display_width: width.default_display_width(),
alignment: Alignment::default_for_type(var_type),
leave,
short_names: Vec::new(),
- attributes: HashMap::new(),
+ attributes: Attributes::new(),
}
}
return None;
}
- pub fn expand<F>(&self, mode: Syntax, call_loc: Location, output: &mut Vec<MacroToken>, error: F)
- where
+ pub fn expand<F>(
+ &self,
+ mode: Syntax,
+ call_loc: Location,
+ output: &mut Vec<MacroToken>,
+ error: F,
+ ) where
F: Fn(MacroError) + 'a,
{
let error: Box<dyn Fn(MacroError) + 'a> = Box::new(error);
self.0.n_tokens
}
}
-
use crate::{
- dictionary::VarWidth,
+ dictionary::{Attributes, VarWidth},
encoding::{default_encoding, get_encoding, Error as EncodingError},
endian::{Endian, Parse, ToBytes},
identifier::{Error as IdError, Identifier},
}
}
-#[derive(Clone, Debug, Default)]
-pub struct AttributeSet(pub HashMap<Identifier, Vec<String>>);
-
-impl AttributeSet {
+impl Attributes {
fn parse<'a>(
decoder: &Decoder,
mut input: &'a str,
sentinel: Option<char>,
- ) -> Result<(AttributeSet, &'a str), Warning> {
+ ) -> Result<(Attributes, &'a str), Warning> {
let mut attributes = HashMap::new();
let rest = loop {
match input.chars().next() {
}
}
};
- Ok((AttributeSet(attributes), rest))
+ Ok((Attributes(attributes), rest))
}
}
#[derive(Clone, Debug, Default)]
-pub struct FileAttributeRecord(pub AttributeSet);
+pub struct FileAttributeRecord(pub Attributes);
impl FileAttributeRecord {
fn decode(source: &TextRecord, decoder: &Decoder) -> Self {
let input = decoder.decode(&source.text);
- match AttributeSet::parse(decoder, &input, None).issue_warning(&decoder.warn) {
+ match Attributes::parse(decoder, &input, None).issue_warning(&decoder.warn) {
Some((set, rest)) => {
if !rest.is_empty() {
decoder.warn(Warning::TBD);
}
#[derive(Clone, Debug)]
-pub struct VarAttributeSet {
+pub struct VarAttributes {
pub long_var_name: Identifier,
- pub attributes: AttributeSet,
+ pub attributes: Attributes,
}
-impl VarAttributeSet {
- fn parse<'a>(decoder: &Decoder, input: &'a str) -> Result<(VarAttributeSet, &'a str), Warning> {
+impl VarAttributes {
+ fn parse<'a>(decoder: &Decoder, input: &'a str) -> Result<(VarAttributes, &'a str), Warning> {
let Some((long_var_name, rest)) = input.split_once(':') else {
return Err(Warning::TBD);
};
.new_identifier(long_var_name)
.and_then(Identifier::must_be_ordinary)
.map_err(Warning::InvalidAttributeVariableName)?;
- let (attributes, rest) = AttributeSet::parse(decoder, rest, Some('/'))?;
- let var_attribute = VarAttributeSet {
+ let (attributes, rest) = Attributes::parse(decoder, rest, Some('/'))?;
+ let var_attribute = VarAttributes {
long_var_name,
attributes,
};
}
#[derive(Clone, Debug)]
-pub struct VariableAttributeRecord(pub Vec<VarAttributeSet>);
+pub struct VariableAttributeRecord(pub Vec<VarAttributes>);
impl VariableAttributeRecord {
fn decode(source: &TextRecord, decoder: &Decoder) -> Self {
let mut var_attribute_sets = Vec::new();
while !input.is_empty() {
let Some((var_attribute, rest)) =
- VarAttributeSet::parse(decoder, input).issue_warning(&decoder.warn)
+ VarAttributes::parse(decoder, input).issue_warning(&decoder.warn)
else {
break;
};
pub width: u32,
/// `(value, label)` pairs, where each value is `width` bytes.
- pub labels: Vec<(S, S)>,
+ pub labels: Vec<(RawString, S)>,
}
impl LongStringValueLabels<RawString, RawString> {
let mut labels = Vec::with_capacity(self.labels.len());
for (value, label) in self.labels.iter() {
- let value = decoder.decode_exact_length(&value.0).to_string();
let label = decoder.decode(label).to_string();
- labels.push((value, label));
+ labels.push((value.clone(), label));
}
Ok(LongStringValueLabels {