use alloc::string::{String, ToString};
use core::fmt::Display;
use spacepackets::cfdp::ChecksumType;
use spacepackets::ByteConversionError;
#[cfg(feature = "std")]
use std::error::Error;
use std::path::Path;
#[cfg(feature = "std")]
pub use std_mod::*;
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum FilestoreError {
    FileDoesNotExist,
    FileAlreadyExists,
    DirDoesNotExist,
    Permission,
    IsNotFile,
    IsNotDirectory,
    ByteConversion(ByteConversionError),
    Io {
        raw_errno: Option<i32>,
        string: String,
    },
    ChecksumTypeNotImplemented(ChecksumType),
    Utf8Error,
    Other,
}
impl From<ByteConversionError> for FilestoreError {
    fn from(value: ByteConversionError) -> Self {
        Self::ByteConversion(value)
    }
}
impl Display for FilestoreError {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            FilestoreError::FileDoesNotExist => {
                write!(f, "file does not exist")
            }
            FilestoreError::FileAlreadyExists => {
                write!(f, "file already exists")
            }
            FilestoreError::DirDoesNotExist => {
                write!(f, "directory does not exist")
            }
            FilestoreError::Permission => {
                write!(f, "permission error")
            }
            FilestoreError::IsNotFile => {
                write!(f, "is not a file")
            }
            FilestoreError::IsNotDirectory => {
                write!(f, "is not a directory")
            }
            FilestoreError::ByteConversion(e) => {
                write!(f, "filestore error: {e}")
            }
            FilestoreError::Io { raw_errno, string } => {
                write!(
                    f,
                    "filestore generic IO error with raw errno {:?}: {}",
                    raw_errno, string
                )
            }
            FilestoreError::ChecksumTypeNotImplemented(checksum_type) => {
                write!(f, "checksum {:?} not implemented", checksum_type)
            }
            FilestoreError::Utf8Error => {
                write!(f, "utf8 error")
            }
            FilestoreError::Other => {
                write!(f, "some filestore error occured")
            }
        }
    }
}
impl Error for FilestoreError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match self {
            FilestoreError::ByteConversion(e) => Some(e),
            _ => None,
        }
    }
}
#[cfg(feature = "std")]
impl From<std::io::Error> for FilestoreError {
    fn from(value: std::io::Error) -> Self {
        Self::Io {
            raw_errno: value.raw_os_error(),
            string: value.to_string(),
        }
    }
}
pub trait VirtualFilestore {
    fn create_file(&self, file_path: &str) -> Result<(), FilestoreError>;
    fn remove_file(&self, file_path: &str) -> Result<(), FilestoreError>;
    fn truncate_file(&self, file_path: &str) -> Result<(), FilestoreError>;
    fn remove_dir(&self, dir_path: &str, all: bool) -> Result<(), FilestoreError>;
    fn create_dir(&self, dir_path: &str) -> Result<(), FilestoreError>;
    fn read_data(
        &self,
        file_path: &str,
        offset: u64,
        read_len: u64,
        buf: &mut [u8],
    ) -> Result<(), FilestoreError>;
    fn write_data(&self, file: &str, offset: u64, buf: &[u8]) -> Result<(), FilestoreError>;
    fn filename_from_full_path(path: &str) -> Option<&str>
    where
        Self: Sized,
    {
        let path = Path::new(path);
        path.file_name().and_then(|name| name.to_str())
    }
    fn is_file(&self, path: &str) -> Result<bool, FilestoreError>;
    fn is_dir(&self, path: &str) -> Result<bool, FilestoreError> {
        Ok(!self.is_file(path)?)
    }
    fn exists(&self, path: &str) -> Result<bool, FilestoreError>;
    fn file_name<'a>(&self, full_path: &'a str) -> Result<Option<&'a str>, FilestoreError>;
    fn file_size(&self, path: &str) -> Result<u64, FilestoreError>;
    fn calculate_checksum(
        &self,
        file_path: &str,
        checksum_type: ChecksumType,
        size_to_verify: u64,
        verification_buf: &mut [u8],
    ) -> Result<u32, FilestoreError>;
    fn checksum_verify(
        &self,
        expected_checksum: u32,
        file_path: &str,
        checksum_type: ChecksumType,
        size_to_verify: u64,
        verification_buf: &mut [u8],
    ) -> Result<bool, FilestoreError> {
        Ok(
            self.calculate_checksum(file_path, checksum_type, size_to_verify, verification_buf)?
                == expected_checksum,
        )
    }
}
#[cfg(feature = "std")]
pub mod std_mod {
    use crc::Crc;
    use crate::{CRC_32, CRC_32C};
    use super::*;
    use std::{
        fs::{self, File, OpenOptions},
        io::{BufReader, Read, Seek, SeekFrom, Write},
    };
    #[derive(Default)]
    pub struct NativeFilestore {}
    impl VirtualFilestore for NativeFilestore {
        fn create_file(&self, file_path: &str) -> Result<(), FilestoreError> {
            if self.exists(file_path)? {
                return Err(FilestoreError::FileAlreadyExists);
            }
            File::create(file_path)?;
            Ok(())
        }
        fn remove_file(&self, file_path: &str) -> Result<(), FilestoreError> {
            if !self.exists(file_path)? {
                return Err(FilestoreError::FileDoesNotExist);
            }
            if !self.is_file(file_path)? {
                return Err(FilestoreError::IsNotFile);
            }
            fs::remove_file(file_path)?;
            Ok(())
        }
        fn file_name<'a>(&self, full_path: &'a str) -> Result<Option<&'a str>, FilestoreError> {
            let path = Path::new(full_path);
            path.file_name()
                .map(|s| s.to_str())
                .ok_or(FilestoreError::Utf8Error)
        }
        fn truncate_file(&self, file_path: &str) -> Result<(), FilestoreError> {
            if !self.exists(file_path)? {
                return Err(FilestoreError::FileDoesNotExist);
            }
            if !self.is_file(file_path)? {
                return Err(FilestoreError::IsNotFile);
            }
            OpenOptions::new()
                .write(true)
                .truncate(true)
                .open(file_path)?;
            Ok(())
        }
        fn create_dir(&self, dir_path: &str) -> Result<(), FilestoreError> {
            fs::create_dir(dir_path).map_err(|e| FilestoreError::Io {
                raw_errno: e.raw_os_error(),
                string: e.to_string(),
            })?;
            Ok(())
        }
        fn remove_dir(&self, dir_path: &str, all: bool) -> Result<(), FilestoreError> {
            if !self.exists(dir_path)? {
                return Err(FilestoreError::DirDoesNotExist);
            }
            if !self.is_dir(dir_path)? {
                return Err(FilestoreError::IsNotDirectory);
            }
            if !all {
                fs::remove_dir(dir_path)?;
                return Ok(());
            }
            fs::remove_dir_all(dir_path)?;
            Ok(())
        }
        fn read_data(
            &self,
            file_name: &str,
            offset: u64,
            read_len: u64,
            buf: &mut [u8],
        ) -> Result<(), FilestoreError> {
            if buf.len() < read_len as usize {
                return Err(ByteConversionError::ToSliceTooSmall {
                    found: buf.len(),
                    expected: read_len as usize,
                }
                .into());
            }
            if !self.exists(file_name)? {
                return Err(FilestoreError::FileDoesNotExist);
            }
            if !self.is_file(file_name)? {
                return Err(FilestoreError::IsNotFile);
            }
            let mut file = File::open(file_name)?;
            file.seek(SeekFrom::Start(offset))?;
            file.read_exact(&mut buf[0..read_len as usize])?;
            Ok(())
        }
        fn write_data(&self, file: &str, offset: u64, buf: &[u8]) -> Result<(), FilestoreError> {
            if !self.exists(file)? {
                return Err(FilestoreError::FileDoesNotExist);
            }
            if !self.is_file(file)? {
                return Err(FilestoreError::IsNotFile);
            }
            let mut file = OpenOptions::new().write(true).open(file)?;
            file.seek(SeekFrom::Start(offset))?;
            file.write_all(buf)?;
            Ok(())
        }
        fn is_file(&self, str_path: &str) -> Result<bool, FilestoreError> {
            let path = Path::new(str_path);
            if !self.exists(str_path)? {
                return Err(FilestoreError::FileDoesNotExist);
            }
            Ok(path.is_file())
        }
        fn exists(&self, path: &str) -> Result<bool, FilestoreError> {
            let path = Path::new(path);
            Ok(self.exists_internal(path))
        }
        fn file_size(&self, str_path: &str) -> Result<u64, FilestoreError> {
            let path = Path::new(str_path);
            if !self.exists_internal(path) {
                return Err(FilestoreError::FileDoesNotExist);
            }
            if !path.is_file() {
                return Err(FilestoreError::IsNotFile);
            }
            Ok(path.metadata()?.len())
        }
        fn calculate_checksum(
            &self,
            file_path: &str,
            checksum_type: ChecksumType,
            size_to_verify: u64,
            verification_buf: &mut [u8],
        ) -> Result<u32, FilestoreError> {
            let mut calc_with_crc_lib = |crc: Crc<u32>| -> Result<u32, FilestoreError> {
                let mut digest = crc.digest();
                let mut buf_reader = BufReader::new(File::open(file_path)?);
                let mut remaining_bytes = size_to_verify;
                while remaining_bytes > 0 {
                    let bytes_to_read = remaining_bytes.min(verification_buf.len() as u64) as usize;
                    let bytes_read = buf_reader.read(&mut verification_buf[0..bytes_to_read])?;
                    if bytes_read == 0 {
                        break; }
                    digest.update(&verification_buf[0..bytes_read]);
                    remaining_bytes -= bytes_read as u64;
                }
                Ok(digest.finalize())
            };
            match checksum_type {
                ChecksumType::Modular => self.calc_modular_checksum(file_path),
                ChecksumType::Crc32 => calc_with_crc_lib(CRC_32),
                ChecksumType::Crc32C => calc_with_crc_lib(CRC_32C),
                ChecksumType::NullChecksum => Ok(0),
                _ => Err(FilestoreError::ChecksumTypeNotImplemented(checksum_type)),
            }
        }
    }
    impl NativeFilestore {
        pub fn calc_modular_checksum(&self, file_path: &str) -> Result<u32, FilestoreError> {
            let mut checksum: u32 = 0;
            let file = File::open(file_path)?;
            let mut buf_reader = BufReader::new(file);
            let mut buffer = [0; 4];
            loop {
                let bytes_read = buf_reader.read(&mut buffer)?;
                if bytes_read == 0 {
                    break;
                }
                (bytes_read..4).for_each(|i| {
                    buffer[i] = 0;
                });
                checksum = checksum.wrapping_add(u32::from_be_bytes(buffer));
            }
            Ok(checksum)
        }
        fn exists_internal(&self, path: &Path) -> bool {
            if !path.exists() {
                return false;
            }
            true
        }
    }
}
#[cfg(test)]
mod tests {
    use std::{fs, path::Path, println};
    use super::*;
    use alloc::format;
    use tempfile::tempdir;
    const EXAMPLE_DATA_CFDP: [u8; 15] = [
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
    ];
    const NATIVE_FS: NativeFilestore = NativeFilestore {};
    #[test]
    fn test_basic_native_filestore_create() {
        let tmpdir = tempdir().expect("creating tmpdir failed");
        let file_path = tmpdir.path().join("test.txt");
        let result =
            NATIVE_FS.create_file(file_path.to_str().expect("getting str for file failed"));
        assert!(result.is_ok());
        let path = Path::new(&file_path);
        assert!(path.exists());
        assert!(NATIVE_FS.exists(file_path.to_str().unwrap()).unwrap());
        assert!(NATIVE_FS.is_file(file_path.to_str().unwrap()).unwrap());
    }
    #[test]
    fn test_basic_native_fs_file_exists() {
        let tmpdir = tempdir().expect("creating tmpdir failed");
        let file_path = tmpdir.path().join("test.txt");
        assert!(!NATIVE_FS.exists(file_path.to_str().unwrap()).unwrap());
        NATIVE_FS
            .create_file(file_path.to_str().expect("getting str for file failed"))
            .unwrap();
        assert!(NATIVE_FS.exists(file_path.to_str().unwrap()).unwrap());
        assert!(NATIVE_FS.is_file(file_path.to_str().unwrap()).unwrap());
    }
    #[test]
    fn test_basic_native_fs_dir_exists() {
        let tmpdir = tempdir().expect("creating tmpdir failed");
        let dir_path = tmpdir.path().join("testdir");
        assert!(!NATIVE_FS.exists(dir_path.to_str().unwrap()).unwrap());
        NATIVE_FS
            .create_dir(dir_path.to_str().expect("getting str for file failed"))
            .unwrap();
        assert!(NATIVE_FS.exists(dir_path.to_str().unwrap()).unwrap());
        assert!(NATIVE_FS
            .is_dir(dir_path.as_path().to_str().unwrap())
            .unwrap());
    }
    #[test]
    fn test_basic_native_fs_remove_file() {
        let tmpdir = tempdir().expect("creating tmpdir failed");
        let file_path = tmpdir.path().join("test.txt");
        NATIVE_FS
            .create_file(file_path.to_str().expect("getting str for file failed"))
            .expect("creating file failed");
        assert!(NATIVE_FS.exists(file_path.to_str().unwrap()).unwrap());
        NATIVE_FS
            .remove_file(file_path.to_str().unwrap())
            .expect("removing file failed");
        assert!(!NATIVE_FS.exists(file_path.to_str().unwrap()).unwrap());
    }
    #[test]
    fn test_basic_native_fs_write() {
        let tmpdir = tempdir().expect("creating tmpdir failed");
        let file_path = tmpdir.path().join("test.txt");
        assert!(!NATIVE_FS.exists(file_path.to_str().unwrap()).unwrap());
        NATIVE_FS
            .create_file(file_path.to_str().expect("getting str for file failed"))
            .unwrap();
        assert!(NATIVE_FS.exists(file_path.to_str().unwrap()).unwrap());
        assert!(NATIVE_FS.is_file(file_path.to_str().unwrap()).unwrap());
        println!("{}", file_path.to_str().unwrap());
        let write_data = "hello world\n";
        NATIVE_FS
            .write_data(file_path.to_str().unwrap(), 0, write_data.as_bytes())
            .expect("writing to file failed");
        let read_back = fs::read_to_string(file_path).expect("reading back data failed");
        assert_eq!(read_back, write_data);
    }
    #[test]
    fn test_basic_native_fs_read() {
        let tmpdir = tempdir().expect("creating tmpdir failed");
        let file_path = tmpdir.path().join("test.txt");
        assert!(!NATIVE_FS.exists(file_path.to_str().unwrap()).unwrap());
        NATIVE_FS
            .create_file(file_path.to_str().expect("getting str for file failed"))
            .unwrap();
        assert!(NATIVE_FS.exists(file_path.to_str().unwrap()).unwrap());
        assert!(NATIVE_FS.is_file(file_path.to_str().unwrap()).unwrap());
        println!("{}", file_path.to_str().unwrap());
        let write_data = "hello world\n";
        NATIVE_FS
            .write_data(file_path.to_str().unwrap(), 0, write_data.as_bytes())
            .expect("writing to file failed");
        let read_back = fs::read_to_string(file_path).expect("reading back data failed");
        assert_eq!(read_back, write_data);
    }
    #[test]
    fn test_truncate_file() {
        let tmpdir = tempdir().expect("creating tmpdir failed");
        let file_path = tmpdir.path().join("test.txt");
        NATIVE_FS
            .create_file(file_path.to_str().expect("getting str for file failed"))
            .expect("creating file failed");
        fs::write(file_path.clone(), [1, 2, 3, 4]).unwrap();
        assert_eq!(fs::read(file_path.clone()).unwrap(), [1, 2, 3, 4]);
        NATIVE_FS
            .truncate_file(file_path.to_str().unwrap())
            .unwrap();
        assert_eq!(fs::read(file_path.clone()).unwrap(), []);
    }
    #[test]
    fn test_remove_dir() {
        let tmpdir = tempdir().expect("creating tmpdir failed");
        let dir_path = tmpdir.path().join("testdir");
        assert!(!NATIVE_FS.exists(dir_path.to_str().unwrap()).unwrap());
        NATIVE_FS
            .create_dir(dir_path.to_str().expect("getting str for file failed"))
            .unwrap();
        assert!(NATIVE_FS.exists(dir_path.to_str().unwrap()).unwrap());
        NATIVE_FS
            .remove_dir(dir_path.to_str().unwrap(), false)
            .unwrap();
        assert!(!NATIVE_FS.exists(dir_path.to_str().unwrap()).unwrap());
    }
    #[test]
    fn test_read_file() {
        let tmpdir = tempdir().expect("creating tmpdir failed");
        let file_path = tmpdir.path().join("test.txt");
        NATIVE_FS
            .create_file(file_path.to_str().expect("getting str for file failed"))
            .expect("creating file failed");
        fs::write(file_path.clone(), [1, 2, 3, 4]).unwrap();
        let read_buf: &mut [u8] = &mut [0; 4];
        NATIVE_FS
            .read_data(file_path.to_str().unwrap(), 0, 4, read_buf)
            .unwrap();
        assert_eq!([1, 2, 3, 4], read_buf);
        NATIVE_FS
            .write_data(file_path.to_str().unwrap(), 4, &[5, 6, 7, 8])
            .expect("writing to file failed");
        NATIVE_FS
            .read_data(file_path.to_str().unwrap(), 2, 4, read_buf)
            .unwrap();
        assert_eq!([3, 4, 5, 6], read_buf);
    }
    #[test]
    fn test_remove_which_does_not_exist() {
        let tmpdir = tempdir().expect("creating tmpdir failed");
        let file_path = tmpdir.path().join("test.txt");
        let result = NATIVE_FS.read_data(file_path.to_str().unwrap(), 0, 4, &mut [0; 4]);
        assert!(result.is_err());
        let error = result.unwrap_err();
        if let FilestoreError::FileDoesNotExist = error {
            assert_eq!(error.to_string(), "file does not exist");
        } else {
            panic!("unexpected error");
        }
    }
    #[test]
    fn test_file_already_exists() {
        let tmpdir = tempdir().expect("creating tmpdir failed");
        let file_path = tmpdir.path().join("test.txt");
        let result =
            NATIVE_FS.create_file(file_path.to_str().expect("getting str for file failed"));
        assert!(result.is_ok());
        let result =
            NATIVE_FS.create_file(file_path.to_str().expect("getting str for file failed"));
        assert!(result.is_err());
        let error = result.unwrap_err();
        if let FilestoreError::FileAlreadyExists = error {
            assert_eq!(error.to_string(), "file already exists");
        } else {
            panic!("unexpected error");
        }
    }
    #[test]
    fn test_remove_file_with_dir_api() {
        let tmpdir = tempdir().expect("creating tmpdir failed");
        let file_path = tmpdir.path().join("test.txt");
        NATIVE_FS
            .create_file(file_path.to_str().expect("getting str for file failed"))
            .unwrap();
        let result = NATIVE_FS.remove_dir(file_path.to_str().unwrap(), true);
        assert!(result.is_err());
        let error = result.unwrap_err();
        if let FilestoreError::IsNotDirectory = error {
            assert_eq!(error.to_string(), "is not a directory");
        } else {
            panic!("unexpected error");
        }
    }
    #[test]
    fn test_remove_dir_remove_all() {
        let tmpdir = tempdir().expect("creating tmpdir failed");
        let dir_path = tmpdir.path().join("test");
        NATIVE_FS
            .create_dir(dir_path.to_str().expect("getting str for file failed"))
            .unwrap();
        let file_path = dir_path.as_path().join("test.txt");
        NATIVE_FS
            .create_file(file_path.to_str().expect("getting str for file failed"))
            .unwrap();
        let result = NATIVE_FS.remove_dir(dir_path.to_str().unwrap(), true);
        assert!(result.is_ok());
        assert!(!NATIVE_FS.exists(dir_path.to_str().unwrap()).unwrap());
    }
    #[test]
    fn test_remove_dir_with_file_api() {
        let tmpdir = tempdir().expect("creating tmpdir failed");
        let file_path = tmpdir.path().join("test");
        NATIVE_FS
            .create_dir(file_path.to_str().expect("getting str for file failed"))
            .unwrap();
        let result = NATIVE_FS.remove_file(file_path.to_str().unwrap());
        assert!(result.is_err());
        let error = result.unwrap_err();
        if let FilestoreError::IsNotFile = error {
            assert_eq!(error.to_string(), "is not a file");
        } else {
            panic!("unexpected error");
        }
    }
    #[test]
    fn test_remove_dir_which_does_not_exist() {
        let tmpdir = tempdir().expect("creating tmpdir failed");
        let file_path = tmpdir.path().join("test");
        let result = NATIVE_FS.remove_dir(file_path.to_str().unwrap(), true);
        assert!(result.is_err());
        let error = result.unwrap_err();
        if let FilestoreError::DirDoesNotExist = error {
            assert_eq!(error.to_string(), "directory does not exist");
        } else {
            panic!("unexpected error");
        }
    }
    #[test]
    fn test_remove_file_which_does_not_exist() {
        let tmpdir = tempdir().expect("creating tmpdir failed");
        let file_path = tmpdir.path().join("test.txt");
        let result = NATIVE_FS.remove_file(file_path.to_str().unwrap());
        assert!(result.is_err());
        let error = result.unwrap_err();
        if let FilestoreError::FileDoesNotExist = error {
            assert_eq!(error.to_string(), "file does not exist");
        } else {
            panic!("unexpected error");
        }
    }
    #[test]
    fn test_truncate_file_which_does_not_exist() {
        let tmpdir = tempdir().expect("creating tmpdir failed");
        let file_path = tmpdir.path().join("test.txt");
        let result = NATIVE_FS.truncate_file(file_path.to_str().unwrap());
        assert!(result.is_err());
        let error = result.unwrap_err();
        if let FilestoreError::FileDoesNotExist = error {
            assert_eq!(error.to_string(), "file does not exist");
        } else {
            panic!("unexpected error");
        }
    }
    #[test]
    fn test_truncate_file_on_directory() {
        let tmpdir = tempdir().expect("creating tmpdir failed");
        let file_path = tmpdir.path().join("test");
        NATIVE_FS.create_dir(file_path.to_str().unwrap()).unwrap();
        let result = NATIVE_FS.truncate_file(file_path.to_str().unwrap());
        assert!(result.is_err());
        let error = result.unwrap_err();
        if let FilestoreError::IsNotFile = error {
            assert_eq!(error.to_string(), "is not a file");
        } else {
            panic!("unexpected error");
        }
    }
    #[test]
    fn test_byte_conversion_error_when_reading() {
        let tmpdir = tempdir().expect("creating tmpdir failed");
        let file_path = tmpdir.path().join("test.txt");
        NATIVE_FS
            .create_file(file_path.to_str().expect("getting str for file failed"))
            .unwrap();
        let result = NATIVE_FS.read_data(file_path.to_str().unwrap(), 0, 2, &mut []);
        assert!(result.is_err());
        let error = result.unwrap_err();
        if let FilestoreError::ByteConversion(byte_conv_error) = error {
            if let ByteConversionError::ToSliceTooSmall { found, expected } = byte_conv_error {
                assert_eq!(found, 0);
                assert_eq!(expected, 2);
            } else {
                panic!("unexpected error");
            }
            assert_eq!(
                error.to_string(),
                format!("filestore error: {}", byte_conv_error)
            );
        } else {
            panic!("unexpected error");
        }
    }
    #[test]
    fn test_read_file_on_dir() {
        let tmpdir = tempdir().expect("creating tmpdir failed");
        let dir_path = tmpdir.path().join("test");
        NATIVE_FS
            .create_dir(dir_path.to_str().expect("getting str for file failed"))
            .unwrap();
        let result = NATIVE_FS.read_data(dir_path.to_str().unwrap(), 0, 4, &mut [0; 4]);
        assert!(result.is_err());
        let error = result.unwrap_err();
        if let FilestoreError::IsNotFile = error {
            assert_eq!(error.to_string(), "is not a file");
        } else {
            panic!("unexpected error");
        }
    }
    #[test]
    fn test_write_file_non_existing() {
        let tmpdir = tempdir().expect("creating tmpdir failed");
        let file_path = tmpdir.path().join("test.txt");
        let result = NATIVE_FS.write_data(file_path.to_str().unwrap(), 0, &[]);
        assert!(result.is_err());
        let error = result.unwrap_err();
        if let FilestoreError::FileDoesNotExist = error {
        } else {
            panic!("unexpected error");
        }
    }
    #[test]
    fn test_write_file_on_dir() {
        let tmpdir = tempdir().expect("creating tmpdir failed");
        let file_path = tmpdir.path().join("test");
        NATIVE_FS.create_dir(file_path.to_str().unwrap()).unwrap();
        let result = NATIVE_FS.write_data(file_path.to_str().unwrap(), 0, &[]);
        assert!(result.is_err());
        let error = result.unwrap_err();
        if let FilestoreError::IsNotFile = error {
        } else {
            panic!("unexpected error");
        }
    }
    #[test]
    fn test_filename_extraction() {
        let tmpdir = tempdir().expect("creating tmpdir failed");
        let file_path = tmpdir.path().join("test.txt");
        NATIVE_FS
            .create_file(file_path.to_str().expect("getting str for file failed"))
            .unwrap();
        NativeFilestore::filename_from_full_path(file_path.to_str().unwrap());
    }
    #[test]
    fn test_modular_checksum() {
        let tmpdir = tempdir().expect("creating tmpdir failed");
        let file_path = tmpdir.path().join("mod-crc.bin");
        fs::write(file_path.as_path(), EXAMPLE_DATA_CFDP).expect("writing test file failed");
        let mut checksum: u32 = 0;
        let mut buffer: [u8; 4] = [0; 4];
        for i in 0..3 {
            buffer = EXAMPLE_DATA_CFDP[i * 4..(i + 1) * 4].try_into().unwrap();
            checksum = checksum.wrapping_add(u32::from_be_bytes(buffer));
        }
        buffer[0..3].copy_from_slice(&EXAMPLE_DATA_CFDP[12..15]);
        buffer[3] = 0;
        checksum = checksum.wrapping_add(u32::from_be_bytes(buffer));
        let mut verif_buf: [u8; 32] = [0; 32];
        let result = NATIVE_FS.checksum_verify(
            checksum,
            file_path.to_str().unwrap(),
            ChecksumType::Modular,
            EXAMPLE_DATA_CFDP.len() as u64,
            &mut verif_buf,
        );
        assert!(result.is_ok());
    }
    #[test]
    fn test_null_checksum_impl() {
        let tmpdir = tempdir().expect("creating tmpdir failed");
        let file_path = tmpdir.path().join("mod-crc.bin");
        let result = NATIVE_FS.checksum_verify(
            0,
            file_path.to_str().unwrap(),
            ChecksumType::NullChecksum,
            0,
            &mut [],
        );
        assert!(result.is_ok());
        assert!(result.unwrap());
    }
    #[test]
    fn test_checksum_not_implemented() {
        let tmpdir = tempdir().expect("creating tmpdir failed");
        let file_path = tmpdir.path().join("mod-crc.bin");
        let result = NATIVE_FS.checksum_verify(
            0,
            file_path.to_str().unwrap(),
            ChecksumType::Crc32Proximity1,
            0,
            &mut [],
        );
        assert!(result.is_err());
        let error = result.unwrap_err();
        if let FilestoreError::ChecksumTypeNotImplemented(cksum_type) = error {
            assert_eq!(
                error.to_string(),
                format!("checksum {:?} not implemented", cksum_type)
            );
        } else {
            panic!("unexpected error");
        }
    }
}