fcmlib 0.1.0

Library to read and write Brother's FCM plotter files
Documentation
use std::io::Write;

use nom::bytes::complete::take;
use nom::combinator::{cond, flat_map, map, map_res};
use nom::multi::{length_count, length_data, length_value};
use nom::number::complete::{le_f32, le_u32};
use nom::sequence::tuple;
use nom::IResult;

use crate::encode::Encode;
use crate::path::Path;
use crate::piece_restrictions::PieceRestrictions;
use crate::util::bool32;
use crate::{path, piece_restrictions};

#[derive(Debug)]
pub struct Piece {
    pub width: u32,
    pub height: u32,
    pub transform: Option<(f32, f32, f32, f32, f32, f32)>,
    pub expansion_limit_value: u32,
    pub reduction_limit_value: u32,
    pub restriction_flags: PieceRestrictions,
    pub label: String,
    pub paths: Vec<Path>,
}

pub(crate) fn read_piece(input: &[u8]) -> IResult<&[u8], Piece> {
    map(
        tuple((
            take(8usize),
            le_u32,
            le_u32,
            flat_map(bool32, |has_transform| {
                cond(
                    has_transform,
                    tuple((le_f32, le_f32, le_f32, le_f32, le_f32, le_f32)),
                )
            }),
            le_u32,
            le_u32,
            piece_restrictions::read_piece_restrictions,
            read_piece_label,
            length_count(le_u32, length_value(le_u32, path::read_path)),
        )),
        |(
            _,
            width,
            height,
            transform,
            expansion_limit_value,
            reduction_limit_value,
            restriction_flags,
            label,
            paths,
        )| Piece {
            width,
            height,
            transform,
            expansion_limit_value,
            reduction_limit_value,
            restriction_flags,
            label,
            paths,
        },
    )(input)
}

fn read_piece_label(input: &[u8]) -> IResult<&[u8], String> {
    map_res(length_data(le_u32), |label_data: &[u8]| {
        if label_data[0] == 1 {
            std::str::from_utf8(&label_data[1..4]).map(String::from)
        } else {
            Ok(String::new())
        }
    })(input)
}

impl Encode for Piece {
    fn encode(&self, buffer: &mut Vec<u8>) -> std::io::Result<()> {
        0u32.encode(buffer)?;
        0u32.encode(buffer)?;
        self.width.encode(buffer)?;
        self.height.encode(buffer)?;
        if let Some(transform) = self.transform {
            1u32.encode(buffer)?;
            transform.0.encode(buffer)?;
            transform.1.encode(buffer)?;
            transform.2.encode(buffer)?;
            transform.3.encode(buffer)?;
            transform.4.encode(buffer)?;
            transform.5.encode(buffer)?;
        } else {
            0u32.encode(buffer)?;
        }
        self.expansion_limit_value.encode(buffer)?;
        self.reduction_limit_value.encode(buffer)?;
        self.restriction_flags.encode(buffer)?;

        4u32.encode(buffer)?;
        if self.label.is_empty() {
            0u32.encode(buffer)?;
        } else {
            1u8.encode(buffer)?;
            buffer.write_all(&self.label.as_bytes()[0..3])?;
        }

        let mut path_data: Vec<Vec<u8>> = vec![];
        for path in &self.paths {
            path_data.push(path.encode_to_vec()?);
        }

        (path_data.len() as u32).encode(buffer)?;
        for path in path_data {
            (path.len() as u32).encode(buffer)?;
            buffer.write_all(&path)?;
        }

        Ok(())
    }
}