use crate::ir::NonEmptyStringList;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum SchemaClass {
Ap203,
Ap214Cd,
Ap214Dis,
#[default]
Ap214Is,
Ap242Dis,
Ap242Is,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StepSchema {
Known {
class: SchemaClass,
raw: Option<NonEmptyStringList>,
},
Unknown { raw: NonEmptyStringList },
}
impl StepSchema {
#[must_use]
pub fn canonical(class: SchemaClass) -> Self {
StepSchema::Known { class, raw: None }
}
#[must_use]
pub fn preserved(class: SchemaClass, raw: NonEmptyStringList) -> Self {
StepSchema::Known {
class,
raw: Some(raw),
}
}
#[must_use]
pub fn class(&self) -> Option<SchemaClass> {
match self {
StepSchema::Known { class, .. } => Some(*class),
StepSchema::Unknown { .. } => None,
}
}
#[must_use]
pub fn raw(&self) -> Option<&NonEmptyStringList> {
match self {
StepSchema::Known { raw, .. } => raw.as_ref(),
StepSchema::Unknown { raw } => Some(raw),
}
}
}
impl Default for StepSchema {
fn default() -> Self {
Self::canonical(SchemaClass::default())
}
}
#[must_use]
pub fn identify_schema(file_schema: &[String]) -> StepSchema {
let raw_opt = NonEmptyStringList::try_from_vec(file_schema.to_vec());
let class_opt = classify(file_schema);
match (class_opt, raw_opt) {
(Some(class), Some(raw)) => StepSchema::preserved(class, raw),
(Some(class), None) => StepSchema::canonical(class),
(None, Some(raw)) => StepSchema::Unknown { raw },
(None, None) => StepSchema::default(),
}
}
fn classify(file_schema: &[String]) -> Option<SchemaClass> {
for s in file_schema {
let upper = s.to_uppercase();
if upper.contains("CONFIG_CONTROL_DESIGN")
|| upper.contains("CONFIGURATION_CONTROLLED_3D_DESIGN")
{
return Some(SchemaClass::Ap203);
}
}
for s in file_schema {
let upper = s.to_uppercase();
if upper.contains("AUTOMOTIVE_DESIGN_CC2") {
return Some(SchemaClass::Ap214Cd);
}
}
for s in file_schema {
let upper = s.to_uppercase();
if upper.contains("AUTOMOTIVE_DESIGN") {
if upper.contains("{ 1 2 10303 214 0") {
return Some(SchemaClass::Ap214Dis);
}
if upper.contains("{ 1 0 10303 214 1") {
return Some(SchemaClass::Ap214Is);
}
}
}
for s in file_schema {
let upper = s.to_uppercase();
if upper.contains("AP242_MANAGED_MODEL_BASED_3D_ENGINEERING_MIM_LF") {
return Some(SchemaClass::Ap242Dis);
}
}
None
}
#[cfg(test)]
mod tests {
use super::*;
fn preserved_single(class: SchemaClass, s: &str) -> StepSchema {
StepSchema::preserved(class, NonEmptyStringList::single(s.into()))
}
#[test]
fn identify_ap203() {
assert_eq!(
identify_schema(&["CONFIG_CONTROL_DESIGN".into()]),
preserved_single(SchemaClass::Ap203, "CONFIG_CONTROL_DESIGN"),
);
}
#[test]
fn identify_ap203_with_extra_mim() {
let raw = NonEmptyStringList::try_from_vec(vec![
"CONFIG_CONTROL_DESIGN".into(),
"SHAPE_APPEARANCE_LAYER_MIM".into(),
])
.expect("non-empty");
assert_eq!(
identify_schema(&[
"CONFIG_CONTROL_DESIGN".into(),
"SHAPE_APPEARANCE_LAYER_MIM".into(),
]),
StepSchema::preserved(SchemaClass::Ap203, raw),
);
}
#[test]
fn identify_ap203_ed2_full_name() {
let input = "AP203_CONFIGURATION_CONTROLLED_3D_DESIGN_OF_MECHANICAL_PARTS_AND_ASSEMBLIES_MIM_LF { 1 0 10303 403 2 1 2 }";
assert_eq!(
identify_schema(&[input.into()]),
preserved_single(SchemaClass::Ap203, input),
);
}
#[test]
fn identify_ap203_ed2_double_space_variant() {
let input = "AP203_CONFIGURATION_CONTROLLED_3D_DESIGN_OF_MECHANICAL_PARTS_AND_ASSEMBLIES_MIM_LF { 1 0 10303 403 3 1 4}";
assert_eq!(
identify_schema(&[input.into()]),
preserved_single(SchemaClass::Ap203, input),
);
}
#[test]
fn identify_ap203_ed1_full_express_name() {
let input = "CONFIGURATION_CONTROLLED_3D_DESIGN_OF_MECHANICAL_PARTS_AND_ASSEMBLIES";
assert_eq!(
identify_schema(&[input.into()]),
preserved_single(SchemaClass::Ap203, input),
);
}
#[test]
fn identify_ap214_cd() {
let input = "AUTOMOTIVE_DESIGN_CC2 { 1 2 10303 214 -1 1 5 4 }";
assert_eq!(
identify_schema(&[input.into()]),
preserved_single(SchemaClass::Ap214Cd, input),
);
}
#[test]
fn identify_ap214_dis() {
let input = "AUTOMOTIVE_DESIGN { 1 2 10303 214 0 1 1 1 }";
assert_eq!(
identify_schema(&[input.into()]),
preserved_single(SchemaClass::Ap214Dis, input),
);
}
#[test]
fn identify_ap214_is() {
let input = "AUTOMOTIVE_DESIGN { 1 0 10303 214 1 1 1 1 }";
assert_eq!(
identify_schema(&[input.into()]),
preserved_single(SchemaClass::Ap214Is, input),
);
}
#[test]
fn identify_ap242_dis_with_trailing_dot() {
let input = "AP242_MANAGED_MODEL_BASED_3D_ENGINEERING_MIM_LF. {1 0 10303 442 1 1 4 }";
assert_eq!(
identify_schema(&[input.into()]),
preserved_single(SchemaClass::Ap242Dis, input),
);
}
#[test]
fn identify_unknown_preserves_raw() {
assert_eq!(
identify_schema(&["SOMETHING_ELSE".into()]),
StepSchema::Unknown {
raw: NonEmptyStringList::single("SOMETHING_ELSE".into()),
},
);
}
#[test]
fn identify_empty_schema_list_falls_back_to_default() {
assert_eq!(identify_schema(&[]), StepSchema::default());
}
#[test]
fn default_is_canonical_ap214_is() {
assert_eq!(
StepSchema::default(),
StepSchema::canonical(SchemaClass::Ap214Is),
);
}
#[test]
fn canonical_raw_is_none() {
let s = StepSchema::canonical(SchemaClass::Ap203);
assert_eq!(s.class(), Some(SchemaClass::Ap203));
assert!(s.raw().is_none());
}
#[test]
fn preserved_exposes_raw_and_class() {
let raw = NonEmptyStringList::single("X".into());
let s = StepSchema::preserved(SchemaClass::Ap214Is, raw.clone());
assert_eq!(s.class(), Some(SchemaClass::Ap214Is));
assert_eq!(s.raw(), Some(&raw));
}
#[test]
fn unknown_has_no_class_but_has_raw() {
let raw = NonEmptyStringList::single("X".into());
let s = StepSchema::Unknown { raw: raw.clone() };
assert!(s.class().is_none());
assert_eq!(s.raw(), Some(&raw));
}
}