1use std::collections::BTreeMap;
9use std::fmt;
10use std::io::{Read, Seek, Write};
11
12use crate::document::CadIr;
13use crate::report::DecodeReport;
14use crate::report::ExportReport;
15use schemars::JsonSchema;
16use serde::{Deserialize, Serialize};
17
18pub trait ReadSeek: Read + Seek {}
20impl<T: Read + Seek> ReadSeek for T {}
21
22#[derive(
24 Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema,
25)]
26#[serde(rename_all = "snake_case")]
27pub enum Confidence {
28 No,
30 Low,
32 Medium,
34 High,
36}
37
38impl fmt::Display for Confidence {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 f.write_str(match self {
41 Self::No => "no",
42 Self::Low => "low",
43 Self::Medium => "medium",
44 Self::High => "high",
45 })
46 }
47}
48
49#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
54pub struct ContainerEntry {
55 pub name: String,
57 pub role: String,
59 pub compression: String,
61 pub compressed_size: u64,
63 pub uncompressed_size: u64,
65 #[serde(default)]
67 pub attributes: BTreeMap<String, String>,
68}
69
70#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
72pub struct ContainerSummary {
73 pub format: String,
75 pub container_kind: String,
77 pub entries: Vec<ContainerEntry>,
79 pub notes: Vec<String>,
81}
82
83#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
85pub struct DecodeOptions {
86 pub container_only: bool,
88}
89
90#[derive(Debug, Clone, PartialEq)]
92pub struct DecodeResult {
93 pub ir: CadIr,
95 pub report: DecodeReport,
97}
98
99impl DecodeResult {
100 pub fn new(mut ir: CadIr, report: DecodeReport) -> Self {
102 ir.finalize();
103 Self { ir, report }
104 }
105}
106
107#[derive(Debug, thiserror::Error)]
109pub enum CodecError {
110 #[error("not the expected format: {0}")]
112 WrongFormat(String),
113 #[error("malformed container: {0}")]
115 Malformed(String),
116 #[error("not implemented yet: {0}")]
118 NotImplemented(String),
119 #[error("io error: {0}")]
121 Io(#[from] std::io::Error),
122}
123
124impl From<crate::native::NativeConvertError> for CodecError {
125 fn from(error: crate::native::NativeConvertError) -> Self {
126 Self::Malformed(error.to_string())
127 }
128}
129
130pub trait Codec {
132 fn id(&self) -> &'static str;
134
135 fn detect(&self, prefix: &[u8]) -> Confidence;
137
138 fn inspect(&self, reader: &mut dyn ReadSeek) -> Result<ContainerSummary, CodecError>;
140
141 fn decode(
143 &self,
144 reader: &mut dyn ReadSeek,
145 options: &DecodeOptions,
146 ) -> Result<DecodeResult, CodecError>;
147}
148
149pub trait Encoder {
151 fn id(&self) -> &'static str;
153
154 fn encode(&self, ir: &CadIr, writer: &mut dyn Write) -> Result<ExportReport, CodecError>;
156}
157
158#[derive(Debug, Clone, Copy, Default)]
160pub struct CadirEncoder;
161
162impl Encoder for CadirEncoder {
163 fn id(&self) -> &'static str {
164 "cadir"
165 }
166
167 fn encode(&self, ir: &CadIr, writer: &mut dyn Write) -> Result<ExportReport, CodecError> {
168 let mut json = ir
169 .to_canonical_json()
170 .map_err(|error| CodecError::Malformed(error.to_string()))?;
171 json.push('\n');
172 writer.write_all(json.as_bytes())?;
173 let validation = crate::validate(ir, Vec::new());
174 let total_entities = validation.entity_counts.values().sum();
175 Ok(ExportReport {
176 format: "cadir".into(),
177 entity_counts: validation.entity_counts,
178 total_entities,
179 losses: Vec::new(),
180 notes: Vec::new(),
181 })
182 }
183}