compose_json/
compose-json.rs1use std::io::{BufWriter, Write};
22use std::process::ExitCode;
23
24use openbim_ifcx::{compose, flatten_owned, ComposeError, ComposedNode, IfcxFile};
25use serde::ser::{Serialize, SerializeMap, Serializer};
26
27struct Tree<'a>(&'a ComposedNode);
30
31struct Attributes<'a>(&'a ComposedNode);
32
33struct Children<'a>(&'a ComposedNode);
34
35impl Serialize for Tree<'_> {
36 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
37 let mut map = serializer.serialize_map(Some(3))?;
38 map.serialize_entry("path", &self.0.path)?;
39 map.serialize_entry("attributes", &Attributes(self.0))?;
40 map.serialize_entry("children", &Children(self.0))?;
41 map.end()
42 }
43}
44
45impl Serialize for Attributes<'_> {
46 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
47 let mut map = serializer.serialize_map(Some(self.0.attributes.len()))?;
48 for (id, value) in &self.0.attributes {
49 map.serialize_entry(id, &**value)?;
50 }
51 map.end()
52 }
53}
54
55impl Serialize for Children<'_> {
56 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
57 let mut map = serializer.serialize_map(Some(self.0.children.len()))?;
58 for (name, child) in &self.0.children {
59 map.serialize_entry(name, &Tree(child))?;
60 }
61 map.end()
62 }
63}
64
65fn main() -> ExitCode {
66 let paths: Vec<String> = std::env::args().skip(1).collect();
67 if paths.is_empty() {
68 eprintln!("usage: compose-json <file.ifcx>...");
69 return ExitCode::FAILURE;
70 }
71 let mut data = Vec::new();
72 for path in &paths {
73 let file = std::fs::read(path)
74 .map_err(|e| e.to_string())
75 .and_then(|bytes| IfcxFile::from_json_slice(&bytes).map_err(|e| e.to_string()));
76 match file {
77 Ok(file) => data.extend(file.data),
78 Err(e) => {
79 eprintln!("{path}: {e}");
80 return ExitCode::FAILURE;
81 }
82 }
83 }
84
85 let stdout = std::io::stdout().lock();
86 let mut out = BufWriter::new(stdout);
87 let written = match compose(&flatten_owned(data)) {
88 Ok(composed) => serde_json::to_writer(&mut out, &Tree(&composed.root())),
89 Err(e) => {
90 let kind = match e {
91 ComposeError::Cycle { .. } => "cycle",
92 ComposeError::UnknownReference { .. } => "unknown-reference",
93 _ => "other",
94 };
95 let error = serde_json::json!({"error": {"kind": kind, "message": e.to_string()}});
96 serde_json::to_writer(&mut out, &error)
97 }
98 };
99 match written.map_err(std::io::Error::from).and_then(|()| {
100 out.write_all(b"\n")?;
101 out.flush()
102 }) {
103 Ok(()) => ExitCode::SUCCESS,
104 Err(e) => {
105 eprintln!("writing output: {e}");
106 ExitCode::FAILURE
107 }
108 }
109}