mod act;
pub mod asm_header;
pub mod brep;
pub mod container;
pub mod decode;
pub mod design;
pub mod history;
pub mod materials;
pub mod nurbs;
pub mod sab;
mod writer;
use cadmpeg_ir::codec::{
Codec, CodecError, Confidence, ContainerSummary, DecodeOptions, DecodeResult, Encoder, ReadSeek,
};
use cadmpeg_ir::document::CadIr;
use sha2::{Digest, Sha256};
use std::io::Write;
const ZIP_MAGIC: &[u8] = b"PK\x03\x04";
#[derive(Debug, Default, Clone, Copy)]
pub struct F3dCodec;
impl F3dCodec {
pub fn write_preserved(&self, ir: &CadIr, writer: &mut dyn Write) -> Result<(), CodecError> {
let expected = ir
.source
.as_ref()
.and_then(|source| source.attributes.get("semantic_sha256"))
.ok_or_else(|| CodecError::NotImplemented("IR has no F3D semantic baseline".into()))?;
let record = ir
.unknowns
.iter()
.find(|record| record.id.0 == "f3d:file:source-image#0")
.ok_or_else(|| {
CodecError::NotImplemented("IR has no retained F3D source image".into())
})?;
let data = record.data.as_ref().ok_or_else(|| {
CodecError::Malformed("retained F3D source image has no bytes".into())
})?;
let hash = Sha256::digest(data)
.iter()
.fold(String::new(), |mut output, byte| {
use std::fmt::Write as _;
let _ = write!(output, "{byte:02x}");
output
});
if data.len() as u64 != record.byte_len || hash != record.sha256 {
return Err(CodecError::Malformed(
"retained F3D source image failed integrity validation".into(),
));
}
if decode::semantic_hash(ir) != *expected {
return writer::write_semantic(ir, data, writer);
}
writer.write_all(data)?;
Ok(())
}
}
impl Codec for F3dCodec {
fn id(&self) -> &'static str {
"f3d"
}
fn detect(&self, prefix: &[u8]) -> Confidence {
if !prefix.starts_with(ZIP_MAGIC) {
return Confidence::No;
}
if container::DETECT_MARKERS
.iter()
.any(|m| contains_subslice(prefix, m))
{
Confidence::High
} else {
Confidence::Low
}
}
fn inspect(&self, reader: &mut dyn ReadSeek) -> Result<ContainerSummary, CodecError> {
let scan = container::scan(reader)?;
Ok(container::summarize(&scan))
}
fn decode(
&self,
reader: &mut dyn ReadSeek,
options: &DecodeOptions,
) -> Result<DecodeResult, CodecError> {
decode::decode(reader, options)
}
}
impl Encoder for F3dCodec {
fn id(&self) -> &'static str {
"f3d"
}
fn encode(&self, ir: &CadIr, writer: &mut dyn Write) -> Result<(), CodecError> {
if ir
.unknowns
.iter()
.any(|record| record.id.0 == "f3d:file:source-image#0")
{
self.write_preserved(ir, writer)
} else {
writer::write_new(ir, writer)
}
}
}
fn contains_subslice(haystack: &[u8], needle: &[u8]) -> bool {
if needle.is_empty() || haystack.len() < needle.len() {
return false;
}
haystack.windows(needle.len()).any(|w| w == needle)
}
#[cfg(test)]
mod tests;