microcad_lang/builtin/
export.rs1use std::rc::Rc;
7
8use crate::{Id, builtin::file_io::*, model::*, parameter, render::RenderError, value::*};
9
10use thiserror::Error;
11
12#[derive(Error, Debug)]
14pub enum ExportError {
15 #[error("IO Error")]
17 IoError(#[from] std::io::Error),
18
19 #[error("Format Error")]
21 FormatError(#[from] std::fmt::Error),
22
23 #[error("No export attribute found in workbench (mark it with `#[export(\"filename\")`")]
25 NoExportAttribute,
26
27 #[error("No exporter found for file `{0}`")]
29 NoExporterForFile(std::path::PathBuf),
30
31 #[error("No exporter found with id `{0}`")]
33 NoExporterWithId(Id),
34
35 #[error("Multiple exporters for file extension: {0:?}")]
37 MultipleExportersForFileExtension(Vec<Id>),
38
39 #[error("Render error: {0}")]
41 RenderError(#[from] RenderError),
42}
43
44pub trait Exporter: FileIoInterface {
48 fn model_parameters(&self) -> ParameterValueList {
59 ParameterValueList::default()
60 }
61
62 fn export_parameters(&self) -> ParameterValueList {
64 [
65 parameter!(filename: String),
66 parameter!(resolution: Length = 0.1 ),
67 ]
68 .into_iter()
69 .collect()
70 }
71
72 fn export(&self, model: &Model, filename: &std::path::Path) -> Result<Value, ExportError>;
74
75 fn output_type(&self) -> OutputType {
79 OutputType::NotDetermined
80 }
81}
82
83#[derive(Default)]
89pub struct ExporterRegistry {
90 io: FileIoRegistry<Rc<dyn Exporter>>,
91}
92
93impl ExporterRegistry {
94 pub fn new() -> Self {
96 Self {
97 io: FileIoRegistry::default(),
98 }
99 }
100
101 pub fn insert(mut self, exporter: impl Exporter + 'static) -> Self {
105 let rc = Rc::new(exporter);
106 self.io.insert(rc);
107 self
108 }
109
110 pub fn by_filename(
112 &self,
113 filename: impl AsRef<std::path::Path>,
114 ) -> Result<Rc<dyn Exporter>, ExportError> {
115 let importers = self.io.by_filename(filename.as_ref());
116 match importers.len() {
117 0 => Err(ExportError::NoExporterForFile(std::path::PathBuf::from(
118 filename.as_ref(),
119 ))),
120 1 => Ok(importers.first().expect("One importer").clone()),
121 _ => Err(ExportError::MultipleExportersForFileExtension(
122 importers.iter().map(|importer| importer.id()).collect(),
123 )),
124 }
125 }
126}
127
128pub trait ExporterAccess {
130 fn exporter_by_id(&self, id: &Id) -> Result<Rc<dyn Exporter>, ExportError>;
132
133 fn exporter_by_filename(
135 &self,
136 filename: &std::path::Path,
137 ) -> Result<Rc<dyn Exporter>, ExportError>;
138
139 fn find_exporter(
141 &self,
142 filename: &std::path::Path,
143 id: &Option<Id>,
144 ) -> Result<Rc<dyn Exporter>, ExportError> {
145 match id {
146 Some(id) => self.exporter_by_id(id),
147 None => self.exporter_by_filename(filename),
148 }
149 }
150}
151
152impl ExporterAccess for ExporterRegistry {
153 fn exporter_by_id(&self, id: &Id) -> Result<Rc<dyn Exporter>, ExportError> {
154 match self.io.by_id(id) {
155 Some(exporter) => Ok(exporter),
156 None => Err(ExportError::NoExporterWithId(id.clone())),
157 }
158 }
159
160 fn exporter_by_filename(
161 &self,
162 filename: &std::path::Path,
163 ) -> Result<Rc<dyn Exporter>, ExportError> {
164 self.by_filename(filename)
165 }
166}