use cadmpeg_codec_catia::CatiaCodec;
use cadmpeg_codec_creo::CreoCodec;
use cadmpeg_codec_f3d::F3dCodec;
use cadmpeg_codec_freecad::FcstdCodec;
use cadmpeg_codec_iges::IgesCodec;
use cadmpeg_codec_nx::NxCodec;
use cadmpeg_codec_rhino::RhinoCodec;
use cadmpeg_codec_sldprt::SldprtCodec;
use cadmpeg_ir::codec::{CadirEncoder, Codec, CodecError, Confidence, Encoder};
use cadmpeg_ir::document::CadIr;
use cadmpeg_ir::report::ExportReport;
use cadmpeg_ir::SourceFidelity;
use cadmpeg_step::StepCodec;
pub struct Registry {
codecs: Vec<Box<dyn Codec>>,
encoders: Vec<Box<dyn Encoder>>,
}
impl Registry {
pub fn with_builtins() -> Self {
Registry {
codecs: vec![
Box::new(FcstdCodec),
Box::new(F3dCodec),
Box::new(SldprtCodec),
Box::new(CatiaCodec),
Box::new(CreoCodec),
Box::new(NxCodec),
Box::new(RhinoCodec),
Box::new(StepCodec::default()),
Box::new(IgesCodec),
],
encoders: vec![
Box::new(FcstdCodec),
Box::new(F3dCodec),
Box::new(SldprtCodec),
Box::new(RhinoCodec),
Box::new(StepCodec::default()),
Box::new(CadirEncoder),
],
}
}
pub fn detect<'a>(&'a self, prefix: &[u8]) -> Option<(&'a dyn Codec, Confidence)> {
self.codecs
.iter()
.map(|c| (c.as_ref(), c.detect(prefix)))
.filter(|(_, conf)| *conf > Confidence::No)
.max_by_key(|(_, confidence)| *confidence)
}
pub fn by_id(&self, id: &str) -> Option<&dyn Codec> {
self.codecs
.iter()
.find(|codec| codec.id() == id)
.map(Box::as_ref)
}
pub fn encoder_by_id(&self, id: &str) -> Option<&dyn Encoder> {
self.encoders
.iter()
.find(|encoder| encoder.id() == id)
.map(Box::as_ref)
}
pub fn set_step_options(&mut self, options: cadmpeg_step::StepWriteOptions) {
if let Some(encoder) = self
.encoders
.iter_mut()
.find(|encoder| encoder.id() == "step")
{
*encoder = Box::new(StepCodec { options });
}
}
pub fn encode_by_id(
&self,
id: &str,
rhino_version: Option<cadmpeg_codec_rhino::RhinoArchiveVersion>,
ir: &CadIr,
source_fidelity: Option<&SourceFidelity>,
output: &mut dyn std::io::Write,
) -> Option<Result<ExportReport, CodecError>> {
if rhino_version.is_some() && id != "rhino" {
return Some(Err(CodecError::Malformed(
"Rhino archive version requires the Rhino encoder".into(),
)));
}
if id == "rhino" {
if let Some(version) = rhino_version {
return Some(
cadmpeg_codec_rhino::RhinoEncoder::new(version).encode_with_source_fidelity(
ir,
source_fidelity,
output,
),
);
}
}
self.encoder_by_id(id)
.map(|encoder| encoder.encode_with_source_fidelity(ir, source_fidelity, output))
}
}
#[cfg(test)]
mod tests {
use super::Registry;
use crate::Format;
#[test]
fn every_exportable_format_has_an_encoder() {
let registry = Registry::with_builtins();
for format in [
Format::Cadir,
Format::Step,
Format::Fcstd,
Format::F3d,
Format::Sldprt,
Format::Rhino,
] {
assert!(
registry.encoder_by_id(format.name()).is_some(),
"{}",
format.name()
);
}
}
#[test]
fn ambiguous_zip_uses_last_registered_codec_precedence() {
let registry = Registry::with_builtins();
let (codec, confidence) = registry
.detect(b"PK\x03\x04 markerless")
.expect("required invariant");
assert_eq!(codec.id(), "f3d");
assert_eq!(confidence, cadmpeg_ir::codec::Confidence::Low);
}
#[test]
fn step_is_registered_as_a_reader() {
let registry = Registry::with_builtins();
let (codec, confidence) = registry
.detect(b"ISO-10303-21;HEADER;")
.expect("STEP codec detection");
assert_eq!(codec.id(), "step");
assert_eq!(confidence, cadmpeg_ir::codec::Confidence::High);
assert_eq!(registry.by_id("step").expect("STEP reader").id(), "step");
}
}