1use std::collections::BTreeMap;
5
6use schemars::JsonSchema;
7use serde::{Deserialize, Deserializer, Serialize};
8
9use crate::appearance::{Appearance, AppearanceBinding};
10use crate::attributes::SourceAttribute;
11use crate::drawings::Drawing;
12use crate::features::{DesignConfiguration, DesignParameter, Feature, FeatureInputTopology};
13use crate::geometry::{Curve, Pcurve, ProceduralCurve, ProceduralSurface, Surface};
14use crate::native::Native;
15use crate::presentation::{PresentationDocument, ViewPresentation};
16use crate::products::{AssemblyJoint, Component, Occurrence};
17use crate::semantic_annotations::SemanticAnnotation;
18use crate::sketches::{
19 Sketch, SketchConstraint, SketchEntity, SpatialSketch, SpatialSketchConstraint,
20 SpatialSketchEntity,
21};
22use crate::spreadsheets::Spreadsheet;
23use crate::subd::SubdSurface;
24use crate::tessellation::Tessellation;
25use crate::topology::{Body, Coedge, Edge, Face, Loop, Point, Region, Shell, Vertex};
26use crate::units::{Tolerances, Units};
27use crate::unknown::{NativeUnknownRecord, UnknownRecord};
28
29macro_rules! arena_registry {
30 ($macro:ident) => {
31 $macro! {
32 bodies: Body, "Body arena.", [] => |e| e.id.0.clone();
33 regions: Region, "Region arena.", [] => |e| e.id.0.clone();
34 shells: Shell, "Shell arena.", [] => |e| e.id.0.clone();
35 faces: Face, "Face arena.", [] => |e| e.id.0.clone();
36 loops: Loop, "Loop arena.", [] => |e| e.id.0.clone();
37 coedges: Coedge, "Coedge arena.", [] => |e| e.id.0.clone();
38 edges: Edge, "Edge arena.", [] => |e| e.id.0.clone();
39 vertices: Vertex, "Vertex arena.", [] => |e| e.id.0.clone();
40 points: Point, "Point arena.", [] => |e| e.id.0.clone();
41 surfaces: Surface, "Surface arena.", [] => |e| e.id.0.clone();
42 curves: Curve, "Curve arena.", [] => |e| e.id.0.clone();
43 subds: SubdSurface, "Subdivision surface arena.", [] => |e| e.id.0.clone();
44 pcurves: Pcurve, "Pcurve arena.", [] => |e| e.id.0.clone();
45 procedural_surfaces: ProceduralSurface, "Procedural surface arena.", [] => |e| e.id.0.clone();
46 procedural_curves: ProceduralCurve, "Procedural curve arena.", [] => |e| e.id.0.clone();
47 features: Feature, "Feature arena.", [] => |e| e.id.0.clone();
48 feature_input_topologies: FeatureInputTopology, "Feature input-topology arena.", [serde(default, skip_serializing_if = "Vec::is_empty")] => |e| e.id.0.clone();
49 configurations: DesignConfiguration, "Design configuration arena.", [serde(default)] => |e| e.id.0.clone();
50 parameters: DesignParameter, "Design parameter arena.", [serde(default)] => |e| e.id.0.clone();
51 sketches: Sketch, "Planar sketch arena.", [serde(default)] => |e| e.id.0.clone();
52 sketch_entities: SketchEntity, "Solved sketch entity arena.", [serde(default)] => |e| e.id.0.clone();
53 sketch_constraints: SketchConstraint, "Sketch constraint arena.", [serde(default)] => |e| e.id.0.clone();
54 spatial_sketches: SpatialSketch, "Spatial sketch arena.", [serde(default)] => |e| e.id.0.clone();
55 spatial_sketch_entities: SpatialSketchEntity, "Solved spatial sketch entity arena.", [serde(default)] => |e| e.id.0.clone();
56 spatial_sketch_constraints: SpatialSketchConstraint, "Spatial sketch constraint arena.", [serde(default, skip_serializing_if = "Vec::is_empty")] => |e| e.id.0.clone();
57 spreadsheets: Spreadsheet, "Spreadsheet arena.", [serde(default)] => |e| e.id.0.clone();
58 components: Component, "Product component arena.", [serde(default)] => |e| e.id.0.clone();
59 occurrences: Occurrence, "Product occurrence arena.", [serde(default)] => |e| e.id.0.clone();
60 assembly_joints: AssemblyJoint, "Assembly joint arena.", [serde(default)] => |e| e.id.0.clone();
61 drawings: Drawing, "Drawing page, resource, view, and annotation arena.", [serde(default)] => |e| e.id.0.clone();
62 semantic_annotations: SemanticAnnotation, "Semantic dimension, note, symbol, and callout arena.", [serde(default)] => |e| e.id.0.clone();
63 presentation_documents: PresentationDocument, "Document presentation arena.", [serde(default)] => |e| e.id.0.clone();
64 view_presentations: ViewPresentation, "View-provider presentation arena.", [serde(default)] => |e| e.id.0.clone();
65 tessellations: Tessellation, "Tessellation arena.", [] => |e| e.id.clone();
66 appearances: Appearance, "Appearance arena.", [] => |e| e.id.0.clone();
67 appearance_bindings: AppearanceBinding, "Appearance binding arena.", [] => |e| e.id.clone();
68 attributes: SourceAttribute, "Attribute arena.", [] => |e| e.id.0.clone();
69 products: crate::product::Product, "Product prototype arena.", [serde(default)] => |e| e.id.0.clone();
70 product_occurrences: crate::product::ProductOccurrence, "Placed product occurrence arena.", [serde(default)] => |e| e.id.0.clone();
71 pmi: crate::pmi::PmiAnnotation, "Product-manufacturing information arena.", [serde(default)] => |e| e.id.0.clone();
72 presentation_layers: crate::presentation::PresentationLayer, "Presentation layer arena.", [serde(default)] => |e| e.id.0.clone();
73 }
74 };
75}
76pub(crate) use arena_registry;
77
78macro_rules! declare_model {
79 ($($field:ident: $ty:ty, $doc:literal, [$($attribute:meta),*] => $key:expr;)*) => {
80 #[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
82 pub struct Model {
83 $(
84 $(#[$attribute])*
85 #[doc = $doc]
86 pub $field: Vec<$ty>,
87 )*
88 }
89
90 impl Model {
91 pub fn arena_names() -> &'static [&'static str] {
93 &[$(stringify!($field)),*]
94 }
95
96 pub fn contains_id(&self, id: &str) -> bool {
102 $({
103 let key = ($key) as fn(&$ty) -> String;
104 if self.$field.iter().any(|e| key(e) == id) { return true; }
105 })*
106 false
107 }
108
109 pub fn entity_ids(&self) -> Vec<String> {
114 let mut ids = Vec::new();
115 $( ids.extend(self.$field.iter().map($key)); )*
116 ids
117 }
118
119 pub fn finalize(&mut self) {
121 $(self.$field.sort_by_key($key);)*
122 }
123 }
124 };
125}
126
127pub const IR_VERSION: &str = "4";
129
130arena_registry!(declare_model);
131
132fn deserialize_ir_version<'de, D>(deserializer: D) -> Result<String, D::Error>
133where
134 D: Deserializer<'de>,
135{
136 let version = String::deserialize(deserializer)?;
137 if version != IR_VERSION {
138 return Err(serde::de::Error::custom(format!(
139 "unsupported ir_version {version:?}; expected {IR_VERSION}"
140 )));
141 }
142 Ok(version)
143}
144
145fn ir_version_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
146 schemars::json_schema!({
147 "type": "string",
148 "const": IR_VERSION
149 })
150}
151
152#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
158pub struct CadIr {
159 #[serde(deserialize_with = "deserialize_ir_version")]
161 #[schemars(schema_with = "ir_version_schema")]
162 pub ir_version: String,
163 #[serde(default, skip_serializing_if = "Option::is_none")]
165 pub source: Option<SourceMeta>,
166 pub units: Units,
168 pub tolerances: Tolerances,
170 pub model: Model,
172 #[serde(default)]
174 pub native: Native,
175}
176
177impl CadIr {
178 pub fn native_unknowns(
180 &self,
181 format: &str,
182 ) -> Result<Vec<NativeUnknownRecord>, crate::native::NativeConvertError> {
183 self.native.namespace(format).map_or_else(
184 || Ok(Vec::new()),
185 |namespace| namespace.arena_as("unknowns"),
186 )
187 }
188
189 pub fn all_native_unknowns(
191 &self,
192 ) -> Result<Vec<NativeUnknownRecord>, crate::native::NativeConvertError> {
193 self.native
194 .0
195 .values()
196 .filter(|namespace| namespace.arenas.contains_key("unknowns"))
197 .try_fold(Vec::new(), |mut records, namespace| {
198 records.extend(namespace.arena_as::<NativeUnknownRecord>("unknowns")?);
199 Ok(records)
200 })
201 }
202
203 pub fn set_native_unknowns(
205 &mut self,
206 format: &str,
207 records: &[NativeUnknownRecord],
208 ) -> Result<(), crate::native::NativeConvertError> {
209 let namespace = self.native.namespace_mut(format);
210 if namespace.version == 0 {
211 namespace.version = 1;
212 }
213 namespace.set_arena("unknowns", records)
214 }
215
216 pub fn set_native_unknowns_owned(&mut self, format: &str, records: Vec<UnknownRecord>) {
221 let namespace = self.native.namespace_mut(format);
222 if namespace.version == 0 {
223 namespace.version = 1;
224 }
225 let mut converted = records
226 .into_iter()
227 .map(UnknownRecord::into_native_record)
228 .collect::<Vec<_>>();
229 converted.sort_by(|left, right| left.id.cmp(&right.id));
230 namespace.arenas.insert("unknowns".into(), converted);
231 }
232
233 pub fn push_native_unknown(
235 &mut self,
236 format: &str,
237 record: NativeUnknownRecord,
238 ) -> Result<(), crate::native::NativeConvertError> {
239 let mut records = self.native_unknowns(format)?;
240 records.retain(|existing| existing.id != record.id);
241 records.push(record);
242 self.set_native_unknowns(format, &records)
243 }
244
245 pub fn empty(units: Units) -> Self {
247 Self {
248 ir_version: IR_VERSION.to_owned(),
249 source: None,
250 units,
251 tolerances: Tolerances::default(),
252 model: Model::default(),
253 native: Native::default(),
254 }
255 }
256
257 pub fn to_canonical_json(&self) -> Result<String, serde_json::Error> {
261 serde_json::to_string_pretty(self)
262 }
263
264 pub fn from_json(s: &str) -> Result<Self, serde_json::Error> {
266 let value: serde_json::Value = serde_json::from_str(s)?;
267 let version = value.get("ir_version").and_then(serde_json::Value::as_str);
268 if version != Some(IR_VERSION) {
269 return Err(<serde_json::Error as serde::de::Error>::custom(format!(
270 "unsupported ir_version {version:?}; expected {IR_VERSION}"
271 )));
272 }
273 serde_json::from_value(value)
274 }
275
276 pub fn finalize(&mut self) {
278 self.model.finalize();
279 self.native.finalize();
280 }
281}
282
283#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
285pub struct SourceMeta {
286 pub format: String,
288 #[serde(default)]
290 pub attributes: BTreeMap<String, String>,
291}