iso14229-1 0.1.0

A ISO 14229-1 protocol.
Documentation
//! response of Service 38

use crate::{
    error::Error,
    request::{Request, SubFunction},
    utils, DataFormatIdentifier, Configuration, ModeOfOperation, RequestData, Service,
};

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum RequestFileTransfer {
    AddFile {
        filepath: String,
        dfi: DataFormatIdentifier,
        filesize_len: u8,
        uncompressed_size: u128,
        compressed_size: u128,
    },
    DeleteFile {
        // 2
        filepath: String,
    },
    ReplaceFile {
        // 3
        filepath: String,
        dfi: DataFormatIdentifier,
        filesize_len: u8,
        uncompressed_size: u128,
        compressed_size: u128,
    },
    ReadFile {
        // 4
        filepath: String,
        dfi: DataFormatIdentifier,
    },
    ReadDir {
        // 5
        filepath: String,
    },
    ResumeFile {
        // 6
        filepath: String,
        dfi: DataFormatIdentifier,
        filesize_len: u8,
        uncompressed_size: u128,
        compressed_size: u128,
    },
}

impl From<RequestFileTransfer> for Vec<u8> {
    fn from(v: RequestFileTransfer) -> Self {
        let mut result = Vec::new();
        match &v {
            RequestFileTransfer::AddFile { .. } => result.push(ModeOfOperation::AddFile.into()),
            RequestFileTransfer::DeleteFile { .. } => {
                result.push(ModeOfOperation::DeleteFile.into())
            }
            RequestFileTransfer::ReplaceFile { .. } => {
                result.push(ModeOfOperation::ReplaceFile.into())
            }
            RequestFileTransfer::ReadFile { .. } => result.push(ModeOfOperation::ReadFile.into()),
            RequestFileTransfer::ReadDir { .. } => result.push(ModeOfOperation::ReadDir.into()),
            RequestFileTransfer::ResumeFile { .. } => {
                result.push(ModeOfOperation::ResumeFile.into())
            }
        }
        match v {
            RequestFileTransfer::AddFile {
                filepath,
                dfi,
                filesize_len,
                uncompressed_size,
                compressed_size,
            }
            | RequestFileTransfer::ReplaceFile {
                filepath,
                dfi,
                filesize_len,
                uncompressed_size,
                compressed_size,
            }
            | RequestFileTransfer::ResumeFile {
                filepath,
                dfi,
                filesize_len,
                uncompressed_size,
                compressed_size,
            } => {
                let mut bytes: Vec<_> = filepath.bytes().collect();
                result.extend((bytes.len() as u16).to_be_bytes());
                result.append(&mut bytes);
                result.push(dfi.into());
                result.push(filesize_len);

                result.append(&mut utils::u128_to_vec(
                    uncompressed_size,
                    filesize_len as usize,
                ));
                result.append(&mut utils::u128_to_vec(
                    compressed_size,
                    filesize_len as usize,
                ));
            }
            RequestFileTransfer::DeleteFile { filepath }
            | RequestFileTransfer::ReadDir { filepath } => {
                let mut bytes: Vec<_> = filepath.bytes().collect();
                result.extend((bytes.len() as u16).to_be_bytes());
                result.append(&mut bytes);
            }
            RequestFileTransfer::ReadFile { filepath, dfi } => {
                let mut bytes: Vec<_> = filepath.bytes().collect();
                result.extend((bytes.len() as u16).to_be_bytes());
                result.append(&mut bytes);
                result.push(dfi.into());
            }
        }

        result
    }
}

impl RequestData for RequestFileTransfer {
    fn new_request<T: AsRef<[u8]>>(
        data: T,
        sub_func: Option<u8>,
        _: &Configuration,
    ) -> Result<Request, Error> {
        let data = data.as_ref();
        match sub_func {
            Some(sub_func) => {
                let (suppress_positive, sub_func) = utils::peel_suppress_positive(sub_func);

                let data_len = data.len();
                utils::data_length_check(data_len, 3, false)?;
                let len = u16::from_be_bytes([data[0], data[1]]) as usize;
                match ModeOfOperation::try_from(sub_func)? {
                    ModeOfOperation::AddFile => {
                        utils::data_length_check(data_len, len + 2 + 2, false)?
                    }
                    ModeOfOperation::DeleteFile => {
                        utils::data_length_check(data_len, len + 2, true)?
                    }
                    ModeOfOperation::ReplaceFile => {
                        utils::data_length_check(data_len, len + 2 + 2, false)?
                    }
                    ModeOfOperation::ReadFile => {
                        utils::data_length_check(data_len, len + 2 + 1, true)?
                    }
                    ModeOfOperation::ReadDir => utils::data_length_check(data_len, len + 2, true)?,
                    ModeOfOperation::ResumeFile => {
                        utils::data_length_check(data_len, len + 2 + 2, false)?
                    }
                }

                Ok(Request {
                    service: Service::RequestFileTransfer,
                    sub_func: Some(SubFunction::new(sub_func, suppress_positive)),
                    data: data.to_vec(),
                })
            }
            None => Err(Error::SubFunctionError(Service::RequestFileTransfer)),
        }
    }
}

impl TryFrom<(&Request, &Configuration)> for RequestFileTransfer {
    type Error = Error;
    fn try_from((req, _): (&Request, &Configuration)) -> Result<Self, Self::Error> {
        let service = req.service();
        if service != Service::RequestFileTransfer || req.sub_func.is_none() {
            return Err(Error::ServiceError(service));
        }

        let sub_func: ModeOfOperation = req.sub_function().unwrap().function()?;
        let data = &req.data;
        let mut offset = 0;
        let len = u16::from_be_bytes([data[offset], data[offset + 1]]) as usize;
        offset += 2;
        let filepath = String::from_utf8(data[offset..offset + len].to_vec())
            .map_err(|_| Error::InvalidData(hex::encode(data)))?;
        offset += len;

        match sub_func {
            ModeOfOperation::AddFile => {
                let dfi = DataFormatIdentifier::from(data[offset]);
                offset += 1;
                let filesize_len = data[offset];
                offset += 1;
                let uncompressed_size =
                    utils::slice_to_u128(&data[offset..offset + filesize_len as usize]);
                offset += filesize_len as usize;
                let compressed_size =
                    utils::slice_to_u128(&data[offset..offset + filesize_len as usize]);
                Ok(Self::AddFile {
                    filepath,
                    dfi,
                    filesize_len,
                    uncompressed_size,
                    compressed_size,
                })
            }
            ModeOfOperation::DeleteFile => Ok(Self::DeleteFile { filepath }),
            ModeOfOperation::ReplaceFile => {
                let dfi = DataFormatIdentifier::from(data[offset]);
                offset += 1;
                let filesize_len = data[offset];
                offset += 1;
                let uncompressed_size =
                    utils::slice_to_u128(&data[offset..offset + filesize_len as usize]);
                offset += filesize_len as usize;
                let compressed_size =
                    utils::slice_to_u128(&data[offset..offset + filesize_len as usize]);
                Ok(Self::ReplaceFile {
                    filepath,
                    dfi,
                    filesize_len,
                    uncompressed_size,
                    compressed_size,
                })
            }
            ModeOfOperation::ReadFile => {
                let dfi = DataFormatIdentifier::from(data[offset]);

                Ok(Self::ReadFile { filepath, dfi })
            }
            ModeOfOperation::ReadDir => Ok(Self::ReadDir { filepath }),
            ModeOfOperation::ResumeFile => {
                let dfi = DataFormatIdentifier::from(data[offset]);
                offset += 1;
                let filesize_len = data[offset];
                offset += 1;
                let uncompressed_size =
                    utils::slice_to_u128(&data[offset..offset + filesize_len as usize]);
                offset += filesize_len as usize;
                let compressed_size =
                    utils::slice_to_u128(&data[offset..offset + filesize_len as usize]);
                Ok(Self::ResumeFile {
                    filepath,
                    dfi,
                    filesize_len,
                    uncompressed_size,
                    compressed_size,
                })
            }
        }
    }
}