use crate::error::XmlError;
use crate::XmlCodec;
use ifc_model::Value;
pub(crate) fn attribute_names(codec: &XmlCodec, type_name: &str, count: usize) -> Vec<String> {
let names = schema_names(codec, type_name);
if names.len() >= count {
return names.into_iter().take(count).collect();
}
(0..count).map(|slot| format!("a{slot}")).collect()
}
pub(crate) fn order(
codec: &XmlCodec,
type_name: &str,
named: Vec<(String, Value)>,
) -> Result<Vec<Value>, XmlError> {
let names = schema_names(codec, type_name);
let mut positioned: Vec<(usize, String, Value)> = Vec::new();
let mut unknown = Vec::new();
for (name, value) in named {
let slot = names
.iter()
.position(|candidate| *candidate == name)
.or_else(|| positional_index(&name));
match slot {
Some(slot) => positioned.push((slot, name, value)),
None => unknown.push(value),
}
}
positioned.sort_by_key(|(slot, _, _)| *slot);
let mut values = Vec::with_capacity(positioned.len() + unknown.len());
for (slot, name, value) in positioned {
if slot < values.len() {
return Err(XmlError::DuplicateSlot { name, slot });
}
if slot > values.len() && slot > names.len() {
return Err(XmlError::MissingSlot {
name,
slot,
missing: values.len(),
});
}
values.resize(slot, Value::Null);
values.push(value);
}
values.extend(unknown);
Ok(values)
}
#[cfg(feature = "schema")]
fn schema_names(codec: &XmlCodec, type_name: &str) -> Vec<String> {
let Some(schema) = codec.schema() else {
return Vec::new();
};
let names = schema.attribute_names(type_name);
let mut seen = std::collections::HashSet::new();
let usable = names
.iter()
.all(|name| *name != "id" && positional_index(name).is_none() && seen.insert(*name));
if usable {
names.into_iter().map(str::to_string).collect()
} else {
Vec::new()
}
}
#[cfg(not(feature = "schema"))]
fn schema_names(codec: &XmlCodec, type_name: &str) -> Vec<String> {
let _ = (codec, type_name);
Vec::new()
}
fn positional_index(name: &str) -> Option<usize> {
name.strip_prefix('a')?.parse().ok()
}