pub mod annotations;
pub mod arc_wall;
pub mod category;
pub mod ceiling;
pub mod circulation;
pub mod curtain_wall;
pub mod drafting;
pub mod family;
pub mod floor;
pub mod foundation_and_furnishings;
pub mod generic;
pub mod grid;
pub mod level;
pub mod mep;
pub mod openings;
pub mod parameters;
pub mod project_organization;
pub mod reference_planes;
pub mod reference_points;
pub mod roof;
pub mod structural;
pub mod styling;
pub mod typed_json;
pub mod wall;
pub mod zones;
use crate::Result;
use crate::formats;
use crate::walker::{DecodedElement, ElementDecoder, HandleIndex};
pub const MVP_TYPED_CLASSES: &[&str] = &[
"Level", "Wall", "Floor", "Door", "Window", "Room", "Material",
];
pub fn all_decoders() -> Vec<Box<dyn ElementDecoder>> {
vec![
Box::new(level::LevelDecoder),
Box::new(category::CategoryDecoder),
Box::new(category::SubcategoryDecoder),
Box::new(styling::MaterialDecoder),
Box::new(styling::FillPatternDecoder),
Box::new(styling::LinePatternDecoder),
Box::new(styling::LineStyleDecoder),
Box::new(reference_points::BasePointDecoder),
Box::new(reference_points::SurveyPointDecoder),
Box::new(reference_points::ProjectPositionDecoder),
Box::new(grid::GridDecoder),
Box::new(grid::GridTypeDecoder),
Box::new(reference_planes::ReferencePlaneDecoder),
Box::new(wall::WallDecoder),
Box::new(wall::WallTypeDecoder),
Box::new(floor::FloorDecoder),
Box::new(floor::FloorTypeDecoder),
Box::new(roof::RoofDecoder),
Box::new(roof::RoofTypeDecoder),
Box::new(ceiling::CeilingDecoder),
Box::new(ceiling::CeilingTypeDecoder),
Box::new(openings::DoorDecoder),
Box::new(openings::WindowDecoder),
Box::new(structural::ColumnDecoder),
Box::new(structural::StructuralColumnDecoder),
Box::new(structural::BeamDecoder),
Box::new(structural::StructuralFramingDecoder),
Box::new(circulation::StairDecoder),
Box::new(circulation::StairTypeDecoder),
Box::new(circulation::RailingDecoder),
Box::new(circulation::RailingTypeDecoder),
Box::new(zones::RoomDecoder),
Box::new(zones::AreaDecoder),
Box::new(zones::SpaceDecoder),
Box::new(foundation_and_furnishings::StructuralFoundationDecoder),
Box::new(foundation_and_furnishings::FurnitureDecoder),
Box::new(foundation_and_furnishings::FurnitureSystemDecoder),
Box::new(foundation_and_furnishings::CaseworkDecoder),
Box::new(foundation_and_furnishings::RebarDecoder),
Box::new(project_organization::PhaseDecoder),
Box::new(project_organization::DesignOptionDecoder),
Box::new(project_organization::WorksetDecoder),
Box::new(generic::GenericModelDecoder),
Box::new(generic::MassDecoder),
Box::new(curtain_wall::CurtainWallDecoder),
Box::new(curtain_wall::CurtainGridDecoder),
Box::new(curtain_wall::CurtainMullionDecoder),
Box::new(curtain_wall::CurtainPanelDecoder),
Box::new(family::SymbolDecoder),
Box::new(family::FamilyInstanceDecoder),
Box::new(drafting::ViewDecoder),
Box::new(drafting::SheetDecoder),
Box::new(drafting::ScheduleDecoder),
Box::new(drafting::ScheduleViewDecoder),
Box::new(annotations::DimensionDecoder),
Box::new(annotations::TagDecoder),
Box::new(annotations::TextNoteDecoder),
Box::new(annotations::AnnotationDecoder),
Box::new(project_organization::RevisionDecoder),
Box::new(parameters::ParameterElementDecoder),
Box::new(parameters::SharedParameterDecoder),
Box::new(mep::ElectricalEquipmentDecoder),
Box::new(mep::ElectricalFixtureDecoder),
Box::new(mep::LightingFixtureDecoder),
Box::new(mep::LightingDeviceDecoder),
Box::new(mep::DuctDecoder),
Box::new(mep::DuctFittingDecoder),
Box::new(mep::MechanicalEquipmentDecoder),
Box::new(mep::PipeDecoder),
Box::new(mep::PipeFittingDecoder),
Box::new(mep::PlumbingFixtureDecoder),
Box::new(mep::SpecialtyEquipmentDecoder),
Box::new(parameters::APropertyDecoder),
Box::new(parameters::APropertyBooleanDecoder),
Box::new(parameters::APropertyIntegerDecoder),
Box::new(parameters::APropertyEnumDecoder),
Box::new(parameters::APropertyDouble1Decoder),
Box::new(parameters::APropertyDouble3Decoder),
Box::new(parameters::APropertyFloatDecoder),
Box::new(parameters::APropertyFloat3Decoder),
Box::new(parameters::APropertyStringDecoder),
]
}
pub fn decoder_for_class(name: &str) -> Option<Box<dyn ElementDecoder>> {
all_decoders().into_iter().find(|d| d.class_name() == name)
}
pub fn decode_typed(
bytes: &[u8],
schema: &formats::ClassEntry,
index: &HandleIndex,
) -> Result<DecodedElement> {
let decoder = decoder_for_class(&schema.name).ok_or_else(|| {
crate::Error::BasicFileInfo(format!(
"no typed decoder registered for class {}",
schema.name
))
})?;
decoder.decode(bytes, schema, index)
}
pub fn decode_instance_prefer_typed(
bytes: &[u8],
start: usize,
schema: &formats::ClassEntry,
) -> Option<DecodedElement> {
decode_instance_prefer_typed_with_limits(
bytes,
start,
schema,
crate::walker::WalkerLimits::default(),
)
}
pub fn decode_instance_prefer_typed_with_limits(
bytes: &[u8],
start: usize,
schema: &formats::ClassEntry,
limits: crate::walker::WalkerLimits,
) -> Option<DecodedElement> {
use typed_json::is_mvp_typed_class;
if is_mvp_typed_class(&schema.name) {
let slice = bytes.get(start..)?;
match decode_typed(slice, schema, &HandleIndex::new()) {
Ok(mut decoded) => {
decoded.byte_range.start = decoded.byte_range.start.saturating_add(start);
decoded.byte_range.end = decoded.byte_range.end.saturating_add(start);
let decoder_name = format!("{}Decoder", schema.name);
decoded.provenance = crate::walker::ElementProvenance::schema_typed(
"Global/Latest",
start,
&decoder_name,
);
Some(decoded)
}
Err(_) => None,
}
} else {
Some(crate::walker::decode_instance_with_limits(
bytes, start, schema, limits,
))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn all_decoders_len_is_eighty_one() {
assert_eq!(all_decoders().len(), 81);
}
#[test]
fn all_decoders_includes_level() {
let decoders = all_decoders();
let names: Vec<&str> = decoders.iter().map(|d| d.class_name()).collect();
assert!(names.contains(&"Level"));
}
#[test]
fn all_decoders_includes_mvp_set() {
let names: std::collections::BTreeSet<&str> =
all_decoders().iter().map(|d| d.class_name()).collect();
for class in MVP_TYPED_CLASSES {
assert!(
names.contains(class),
"MVP class {class} missing from all_decoders()"
);
}
}
#[test]
fn all_decoders_includes_category_and_subcategory() {
let decoders = all_decoders();
let names: Vec<&str> = decoders.iter().map(|d| d.class_name()).collect();
assert!(names.contains(&"Category"));
assert!(names.contains(&"Subcategory"));
}
#[test]
fn decoder_class_names_are_unique() {
let decoders = all_decoders();
let mut seen = std::collections::BTreeSet::new();
for d in &decoders {
assert!(
seen.insert(d.class_name()),
"duplicate decoder for class {}",
d.class_name()
);
}
}
#[test]
fn decoder_for_class_roundtrip() {
assert_eq!(
decoder_for_class("Wall").map(|d| d.class_name()),
Some("Wall")
);
assert!(decoder_for_class("ArcWall").is_none());
assert!(decoder_for_class("NoSuchClass").is_none());
}
#[test]
fn decode_typed_rejects_unregistered_class() {
let schema = formats::ClassEntry {
name: "NoSuchClass".into(),
offset: 0,
fields: vec![],
tag: None,
parent: None,
declared_field_count: None,
was_parent_only: false,
ancestor_tag: None,
};
let err = decode_typed(&[], &schema, &HandleIndex::new()).unwrap_err();
assert!(err.to_string().contains("no typed decoder"));
}
}