fcmlib 0.1.0

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

use nom::combinator::{flat_map, map};
use nom::multi::{count, length_value};
use nom::number::complete::le_u32;
use nom::sequence::tuple;
use nom::IResult;

use crate::encode::Encode;
use crate::path_shape::PathShape;
use crate::path_tool::PathTool;
use crate::point::{read_point, Point};
use crate::{path_shape, path_tool};

#[derive(Debug)]
pub struct Path {
    pub tool: PathTool,
    pub shape: Option<PathShape>,
    pub rhinestone_diameter: Option<u32>,
    pub rhinestones: Vec<Point>,
}

fn read_rhinestone_diameter(input: &[u8]) -> IResult<&[u8], Option<u32>> {
    map(le_u32, |diameter| {
        if diameter == 0x3f000000 {
            None
        } else {
            Some(diameter)
        }
    })(input)
}

pub(crate) fn read_path(input: &[u8]) -> IResult<&[u8], Path> {
    flat_map(
        tuple((
            length_value(le_u32, path_tool::read_path_tool),
            le_u32,
            le_u32,
            read_rhinestone_diameter,
        )),
        |(tool, outline_count, rhinestone_count, rhinestone_diameter)| {
            map(
                tuple((
                    path_shape::read_path_shape(outline_count as usize),
                    count(read_point, rhinestone_count as usize),
                )),
                move |(shape, rhinestones)| Path {
                    tool,
                    shape,
                    rhinestone_diameter,
                    rhinestones,
                },
            )
        },
    )(input)
}

impl Encode for Path {
    fn encode(&self, buffer: &mut Vec<u8>) -> io::Result<()> {
        self.tool.encode(buffer)?;
        buffer.write_all(
            &self
                .shape
                .as_ref()
                .map(|it| it.outlines.len() as u32)
                .unwrap_or_default()
                .to_le_bytes(),
        )?;
        (self.rhinestones.len() as u32).encode(buffer)?;
        if let Some(rhinestone_diameter) = &self.rhinestone_diameter {
            rhinestone_diameter.encode(buffer)?;
        } else {
            0x3f000000u32.encode(buffer)?;
        }
        if let Some(shape) = &self.shape {
            shape.start.encode(buffer)?;
            for outline in &shape.outlines {
                outline.encode(buffer)?;
            }
        }
        for rhinestone in &self.rhinestones {
            rhinestone.encode(buffer)?;
        }
        Ok(())
    }
}