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 [parameter!(filename: String), parameter!(resolution: Length)]
65 .into_iter()
66 .collect()
67 }
68
69 fn export(&self, model: &Model, filename: &std::path::Path) -> Result<Value, ExportError>;
71
72 fn output_type(&self) -> OutputType {
76 OutputType::NotDetermined
77 }
78}
79
80#[derive(Default)]
86pub struct ExporterRegistry {
87 io: FileIoRegistry<Rc<dyn Exporter>>,
88}
89
90impl ExporterRegistry {
91 pub fn new() -> Self {
93 Self {
94 io: FileIoRegistry::default(),
95 }
96 }
97
98 pub fn insert(mut self, exporter: impl Exporter + 'static) -> Self {
102 let rc = Rc::new(exporter);
103 self.io.insert(rc);
104 self
105 }
106
107 pub fn by_filename(
109 &self,
110 filename: impl AsRef<std::path::Path>,
111 ) -> Result<Rc<dyn Exporter>, ExportError> {
112 let importers = self.io.by_filename(filename.as_ref());
113 match importers.len() {
114 0 => Err(ExportError::NoExporterForFile(std::path::PathBuf::from(
115 filename.as_ref(),
116 ))),
117 1 => Ok(importers.first().expect("One importer").clone()),
118 _ => Err(ExportError::MultipleExportersForFileExtension(
119 importers.iter().map(|importer| importer.id()).collect(),
120 )),
121 }
122 }
123}
124
125pub trait ExporterAccess {
127 fn exporter_by_id(&self, id: &Id) -> Result<Rc<dyn Exporter>, ExportError>;
129
130 fn exporter_by_filename(
132 &self,
133 filename: &std::path::Path,
134 ) -> Result<Rc<dyn Exporter>, ExportError>;
135
136 fn find_exporter(
138 &self,
139 filename: &std::path::Path,
140 id: &Option<Id>,
141 ) -> Result<Rc<dyn Exporter>, ExportError> {
142 match id {
143 Some(id) => self.exporter_by_id(id),
144 None => self.exporter_by_filename(filename),
145 }
146 }
147}
148
149impl ExporterAccess for ExporterRegistry {
150 fn exporter_by_id(&self, id: &Id) -> Result<Rc<dyn Exporter>, ExportError> {
151 match self.io.by_id(id) {
152 Some(exporter) => Ok(exporter),
153 None => Err(ExportError::NoExporterWithId(id.clone())),
154 }
155 }
156
157 fn exporter_by_filename(
158 &self,
159 filename: &std::path::Path,
160 ) -> Result<Rc<dyn Exporter>, ExportError> {
161 self.by_filename(filename)
162 }
163}