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("No export attribute found in workbench (mark it with `#[export(\"filename\")`")]
21 NoExportAttribute,
22
23 #[error("No exporter found for file `{0}`")]
25 NoExporterForFile(std::path::PathBuf),
26
27 #[error("No exporter found with id `{0}`")]
29 NoExporterWithId(Id),
30
31 #[error("Multiple exporters for file extension: {0:?}")]
33 MultipleExportersForFileExtension(Vec<Id>),
34
35 #[error("Render error: {0}")]
37 RenderError(#[from] RenderError),
38}
39
40pub trait Exporter: FileIoInterface {
44 fn model_parameters(&self) -> ParameterValueList {
55 ParameterValueList::default()
56 }
57
58 fn export_parameters(&self) -> ParameterValueList {
60 [
61 parameter!(filename: String),
62 parameter!(resolution: Length = 0.1 ),
63 ]
64 .into_iter()
65 .collect()
66 }
67
68 fn export(&self, model: &Model, filename: &std::path::Path) -> Result<Value, ExportError>;
70
71 fn output_type(&self) -> OutputType {
75 OutputType::NotDetermined
76 }
77}
78
79#[derive(Default)]
85pub struct ExporterRegistry {
86 io: FileIoRegistry<Rc<dyn Exporter>>,
87}
88
89impl ExporterRegistry {
90 pub fn new() -> Self {
92 Self {
93 io: FileIoRegistry::default(),
94 }
95 }
96
97 pub fn insert(mut self, exporter: impl Exporter + 'static) -> Self {
101 let rc = Rc::new(exporter);
102 self.io.insert(rc);
103 self
104 }
105
106 pub fn by_filename(
108 &self,
109 filename: impl AsRef<std::path::Path>,
110 ) -> Result<Rc<dyn Exporter>, ExportError> {
111 let importers = self.io.by_filename(filename.as_ref());
112 match importers.len() {
113 0 => Err(ExportError::NoExporterForFile(std::path::PathBuf::from(
114 filename.as_ref(),
115 ))),
116 1 => Ok(importers.first().expect("One importer").clone()),
117 _ => Err(ExportError::MultipleExportersForFileExtension(
118 importers.iter().map(|importer| importer.id()).collect(),
119 )),
120 }
121 }
122}
123
124pub trait ExporterAccess {
126 fn exporter_by_id(&self, id: &Id) -> Result<Rc<dyn Exporter>, ExportError>;
128
129 fn exporter_by_filename(
131 &self,
132 filename: &std::path::Path,
133 ) -> Result<Rc<dyn Exporter>, ExportError>;
134
135 fn find_exporter(
137 &self,
138 filename: &std::path::Path,
139 id: &Option<Id>,
140 ) -> Result<Rc<dyn Exporter>, ExportError> {
141 match id {
142 Some(id) => self.exporter_by_id(id),
143 None => self.exporter_by_filename(filename),
144 }
145 }
146}
147
148impl ExporterAccess for ExporterRegistry {
149 fn exporter_by_id(&self, id: &Id) -> Result<Rc<dyn Exporter>, ExportError> {
150 match self.io.by_id(id) {
151 Some(exporter) => Ok(exporter),
152 None => Err(ExportError::NoExporterWithId(id.clone())),
153 }
154 }
155
156 fn exporter_by_filename(
157 &self,
158 filename: &std::path::Path,
159 ) -> Result<Rc<dyn Exporter>, ExportError> {
160 self.by_filename(filename)
161 }
162}