use binarygcode::{BlockKind, DeserialisedResult, Deserialiser};
use super::slice_summary::{SliceSummary, SourceFormat};
use super::{ParseError, SliceParser, gcode_metadata};
pub struct PrusaBgcode;
impl SliceParser for PrusaBgcode {
fn detect(bytes: &[u8], filename: &str) -> bool {
if filename.to_ascii_lowercase().ends_with(".bgcode") {
return true;
}
bytes.starts_with(b"GCDE")
}
fn parse(bytes: &[u8]) -> Result<SliceSummary, ParseError> {
let text = metadata_text(bytes)?;
let metadata = gcode_metadata::parse_lines(text.lines());
gcode_metadata::build_slice_summary(&metadata, SourceFormat::PrusaBgcode)
}
}
fn metadata_text(bytes: &[u8]) -> Result<String, ParseError> {
if !bytes.starts_with(b"GCDE") {
return Err(ParseError::NotThisFormat);
}
let mut deserialiser = Deserialiser::default();
deserialiser.digest(bytes);
let mut buf = Vec::new();
loop {
match deserialiser
.deserialise()
.map_err(|e| ParseError::InvalidField("bgcode", e.to_string()))?
{
DeserialisedResult::FileHeader(_) => {}
DeserialisedResult::Block(mut block) => {
if !matches!(block.kind, BlockKind::GCode | BlockKind::Thumbnail) {
block
.to_ascii(&mut buf, false)
.map_err(|e| ParseError::InvalidField("bgcode", e.to_string()))?;
}
}
DeserialisedResult::MoreBytesRequired(_) => break,
}
}
String::from_utf8(buf).map_err(|e| ParseError::InvalidField("bgcode", e.to_string()))
}
#[cfg(test)]
mod tests {
use super::*;
fn read_test_file(name: &str) -> Vec<u8> {
let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../Testfiles")
.join(name);
std::fs::read(path).expect("test fixture missing")
}
#[test]
fn detects_by_extension_and_magic() {
assert!(PrusaBgcode::detect(b"", "widget.bgcode"));
assert!(PrusaBgcode::detect(b"GCDEanything", "widget"));
assert!(!PrusaBgcode::detect(b"not gcde", "widget.gcode"));
}
#[test]
fn parses_real_fixture() {
let bytes = read_test_file("Shape-Box_0.4n_0.2mm_PLA_MINIIS_24m.bgcode");
assert!(PrusaBgcode::detect(
&bytes,
"Shape-Box_0.4n_0.2mm_PLA_MINIIS_24m.bgcode"
));
let summary = PrusaBgcode::parse(&bytes).unwrap();
assert_eq!(summary.source_format, SourceFormat::PrusaBgcode);
assert_eq!(summary.plates.len(), 1);
let plate = &summary.plates[0];
assert!((plate.print_time_seconds - 1437.0).abs() < 1e-9);
assert_eq!(plate.printer_model.as_deref(), Some("MINIIS"));
assert_eq!(plate.layer_height, Some(0.2));
assert_eq!(plate.filament_usage.len(), 1);
let usage = &plate.filament_usage[0];
assert_eq!(usage.slot_id, "1");
assert!((usage.grams.unwrap() - 2.90).abs() < 1e-9);
assert_eq!(usage.material_type.as_deref(), Some("PLA"));
}
}