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