use step_p21::{
ast::{EntityInstance, Parameter},
parser::{exchange, parse},
};
const STEP: &str = r#"ISO-10303-21;
HEADER;
FILE_DESCRIPTION((''),'2;1');
FILE_NAME('1/2'' NPT coupling','2026-08-04T00:00:00',(''),(''),'','','');
FILE_SCHEMA(('AUTOMOTIVE_DESIGN'));
ENDSEC;
DATA;
#1 = ADVANCED_FACE('',(),#2,.T.);
ENDSEC;
END-ISO-10303-21;
"#;
#[test]
fn doubled_apostrophe_is_one_apostrophe() {
let (residual, parsed) =
exchange::parameter("'1/2'' NPT coupling'").unwrap();
assert_eq!(residual, "", "the whole literal must be consumed");
assert_eq!(parsed, Parameter::String("1/2' NPT coupling".to_string()));
}
#[test]
fn empty_aggregate_is_an_empty_list() {
let (residual, parsed) = exchange::parameter("()").unwrap();
assert_eq!(residual, "");
assert_eq!(parsed, Parameter::List(vec![]));
assert_ne!(parsed, Parameter::NotProvided);
assert_ne!(parsed, Parameter::Omitted);
}
#[test]
fn a_file_using_both_parses_whole() {
let exchange = parse(STEP)
.expect("a file with an escaped string and an empty aggregate");
let file_name = exchange
.header
.iter()
.find(|record| record.name == "FILE_NAME")
.expect("FILE_NAME is present");
let Parameter::List(args) = &file_name.parameter else {
panic!(
"FILE_NAME's parameter is an argument list, got {:?}",
file_name.parameter
);
};
assert_eq!(
args.first(),
Some(&Parameter::String("1/2' NPT coupling".to_string())),
"the doubled apostrophe must survive a full-file parse, not just the sub-parser",
);
let data = exchange.data.first().expect("one DATA section");
let entity = data.entities.first().expect("one entity");
let EntityInstance::Simple { id, record } = entity else {
panic!("expected a simple entity instance, got {entity:?}");
};
assert_eq!(*id, 1);
assert_eq!(record.name, "ADVANCED_FACE");
let Parameter::List(args) = &record.parameter else {
panic!(
"ADVANCED_FACE's parameter is an argument list, got {:?}",
record.parameter
);
};
assert_eq!(
args.get(1),
Some(&Parameter::List(vec![])),
"the empty aggregate must survive as an empty list",
);
}