// dwgimport — SPIKE: beweist, dass acadrust (MPL-2.0) aus einem Byte-Buffer // ein DXF parst und nach wasm32-unknown-unknown kompiliert. // // Aufbau: // - `parse_dxf_summary`: headless, feature-frei testbar. // - `#[cfg(feature="web")]` wasm-bindgen-Fassade darueber. // // API-Stuetzpunkt acadrust: // `DxfReader::from_reader(reader: R)` // → std::io::Cursor<&[u8]> implementiert Read + Seek → WASM-tauglich. use std::collections::HashMap; use std::io::Cursor; use acadrust::DxfReader; // serde_json nur bei Feature "web" als optionale Dep, aber serde ist immer da. // Fuer den headless-Kern bauen wir das JSON mit einem einfachen Format-String, // damit keine Compile-Time-Dep auf serde_json im Default-Feature noetig ist. /// Parst ein DXF aus einem Byte-Slice und gibt ein JSON-Summary zurueck. /// /// Rueckgabe-JSON-Schema: /// ```json /// { /// "version": "AC1015", /// "entityCount": 5, /// "entityTypeCounts": { "LINE": 3, "CIRCLE": 2 }, /// "layerNames": ["0", "Wand"] /// } /// ``` /// /// Fehler: String mit Fehlerbeschreibung. pub fn parse_dxf_summary(bytes: &[u8]) -> Result { // Cursor> implementiert Read + Seek + 'static — kein std::fs noetig. // (from_reader verlangt R: 'static; Cursor<&[u8]> wuerde die Lifetime verletzen.) let cursor = Cursor::new(bytes.to_vec()); let reader = DxfReader::from_reader(cursor) .map_err(|e| format!("DxfReader::from_reader fehlgeschlagen: {e}"))?; let doc = reader .read() .map_err(|e| format!("DxfReader::read fehlgeschlagen: {e}"))?; // Versions-String aus dem Enum (Debug-Format, z. B. "AC1015"). let version = format!("{:?}", doc.version); // Entitaeten zaehlen und nach Typ aufschluesseln. let mut type_counts: HashMap<&str, usize> = HashMap::new(); for entity in doc.entities() { let typ = entity.as_entity().entity_type(); *type_counts.entry(typ).or_insert(0) += 1; } let entity_count = type_counts.values().sum::(); // Layer-Namen aus der Layer-Tabelle. let mut layer_names: Vec = doc .layers .iter() .map(|l| l.name.clone()) .collect(); layer_names.sort(); // JSON-Serialisierung ohne optionale serde_json-Dep im Default-Feature. // Fuer den headless-Test reicht ein sauberer Format-String. let type_counts_json = { let mut pairs: Vec = type_counts .iter() .map(|(k, v)| format!(" \"{k}\": {v}")) .collect(); pairs.sort(); // deterministisch fuer Tests format!("{{\n{}\n }}", pairs.join(",\n")) }; let layer_names_json = { let quoted: Vec = layer_names .iter() .map(|n| format!("\"{}\"", n.replace('"', "\\\""))) .collect(); format!("[{}]", quoted.join(", ")) }; Ok(format!( "{{\n \"version\": \"{version}\",\n \"entityCount\": {entity_count},\n \"entityTypeCounts\": {type_counts_json},\n \"layerNames\": {layer_names_json}\n}}" )) } // --------------------------------------------------------------------------- // WASM-Fassade (nur bei Feature "web" compiliert) // --------------------------------------------------------------------------- #[cfg(feature = "web")] mod wasm { use super::parse_dxf_summary; use wasm_bindgen::prelude::*; /// Initialisiert den Panic-Hook fuer bessere Fehlermeldungen im Browser. #[wasm_bindgen(start)] pub fn init_panic_hook() { console_error_panic_hook::set_once(); } /// WASM-Fassade: nimmt einen Byte-Buffer (Uint8Array aus JS) und gibt ein /// JSON-String zurueck, das das DXF-Summary beschreibt. /// /// JS-Aufruf: /// ```js /// import init, { parse_dxf_summary_json } from './pkgDwgImport/dwgimport.js'; /// await init(); /// const json = parse_dxf_summary_json(new Uint8Array(buffer)); /// ``` #[wasm_bindgen] pub fn parse_dxf_summary_json(bytes: &[u8]) -> Result { parse_dxf_summary(bytes) .map_err(|e| JsValue::from_str(&e)) } } // --------------------------------------------------------------------------- // Tests // --------------------------------------------------------------------------- #[cfg(test)] mod tests { use super::*; /// Minimales ASCII-DXF mit 3 LINE- und 2 CIRCLE-Entitaeten auf Layer "WAND" /// und Layer "0". Wird direkt als Byte-Slice eingebettet — kein Datei-IO. const MINI_DXF: &str = r#" 0 SECTION 2 HEADER 9 $ACADVER 1 AC1015 0 ENDSEC 0 SECTION 2 TABLES 0 TABLE 2 LAYER 70 2 0 LAYER 2 0 70 0 62 7 6 Continuous 0 LAYER 2 WAND 70 0 62 3 6 Continuous 0 ENDTAB 0 ENDSEC 0 SECTION 2 ENTITIES 0 LINE 8 WAND 10 0.0 20 0.0 30 0.0 11 100.0 21 0.0 31 0.0 0 LINE 8 WAND 10 100.0 20 0.0 30 0.0 11 100.0 21 50.0 31 0.0 0 LINE 8 0 10 100.0 20 50.0 30 0.0 11 0.0 21 0.0 31 0.0 0 CIRCLE 8 WAND 10 50.0 20 25.0 30 0.0 40 10.0 0 CIRCLE 8 0 10 20.0 20 10.0 30 0.0 40 5.0 0 ENDSEC 0 EOF "#; #[test] fn test_parse_mini_dxf() { let result = parse_dxf_summary(MINI_DXF.as_bytes()); assert!(result.is_ok(), "parse_dxf_summary schlug fehl: {:?}", result); let json = result.unwrap(); println!("DXF-Summary:\n{json}"); // Version muss AC1015 sein. assert!(json.contains("AC1015"), "Version AC1015 nicht im JSON: {json}"); // Genau 5 Entitaeten: 3 LINE + 2 CIRCLE. assert!( json.contains("\"entityCount\": 5"), "entityCount soll 5 sein: {json}" ); assert!( json.contains("\"LINE\": 3"), "LINE-Zaehler soll 3 sein: {json}" ); assert!( json.contains("\"CIRCLE\": 2"), "CIRCLE-Zaehler soll 2 sein: {json}" ); // Layer "WAND" und "0" muessen erscheinen. assert!(json.contains("\"WAND\""), "Layer WAND fehlt: {json}"); assert!(json.contains("\"0\""), "Layer 0 fehlt: {json}"); } }