Skip to main content

hadris_block/
volume_sync.rs

1use crate::detect::{BlockFormat, FatVariant};
2use crate::{Error, Result};
3use hadris_io::SeekFrom;
4use hadris_io::sync::{Borrowed, Read, Seek};
5
6/// An opened block filesystem with lossless access to its concrete handle.
7#[non_exhaustive]
8pub enum OpenVolume<'a, S>
9where
10    S: Seek,
11{
12    /// An opened FAT12, FAT16, or FAT32 filesystem.
13    Fat(hadris_fat::sync::FatVolume<Borrowed<'a, S>>),
14}
15
16impl<'a, S> OpenVolume<'a, S>
17where
18    S: Read + Seek<Error = <S as Read>::Error>,
19{
20    /// Detects and opens a filesystem at the beginning of `source`.
21    ///
22    /// Partitioned disks must first be narrowed to a partition view.
23    pub fn open(source: &'a mut S, logical_block_size: u32) -> Result<Self> {
24        match crate::detect::sync::detect(source, logical_block_size)? {
25            Some(BlockFormat::Fat(format)) => Self::open_detected(source, format),
26            Some(BlockFormat::PartitionTable(kind)) => Err(Error::PartitionedDisk(kind)),
27            None => Err(Error::UnknownFormat),
28        }
29    }
30
31    /// Opens a filesystem using a previously detected FAT variant.
32    pub fn open_detected(source: &'a mut S, detected: FatVariant) -> Result<Self> {
33        if detected == FatVariant::ExFat {
34            return Err(Error::UnsupportedFormat(BlockFormat::Fat(detected)));
35        }
36        source
37            .seek(SeekFrom::Start(0))
38            .map_err(hadris_io::Error::erase)?;
39        let fat = hadris_fat::sync::FatVolume::open(Borrowed::new(source))?;
40        let opened = fat_variant(fat.fat_type());
41        if opened != detected {
42            return Err(Error::DetectedFormatMismatch { detected, opened });
43        }
44        Ok(Self::Fat(fat))
45    }
46
47    /// Returns the concrete FAT variant of the opened filesystem.
48    pub fn format(&self) -> FatVariant {
49        match self {
50            Self::Fat(fat) => fat_variant(fat.fat_type()),
51        }
52    }
53
54    /// Borrows the opened FAT filesystem.
55    pub fn as_fat(&self) -> Option<&hadris_fat::sync::FatVolume<Borrowed<'a, S>>> {
56        match self {
57            Self::Fat(fat) => Some(fat),
58        }
59    }
60
61    /// Mutably borrows the opened FAT filesystem.
62    pub fn as_fat_mut(&mut self) -> Option<&mut hadris_fat::sync::FatVolume<Borrowed<'a, S>>> {
63        match self {
64            Self::Fat(fat) => Some(fat),
65        }
66    }
67
68    #[allow(clippy::result_large_err)]
69    /// Extracts the FAT filesystem, returning `self` if its format differs.
70    pub fn into_fat(
71        self,
72    ) -> core::result::Result<hadris_fat::sync::FatVolume<Borrowed<'a, S>>, Self> {
73        match self {
74            Self::Fat(fat) => Ok(fat),
75        }
76    }
77
78    /// Closes the filesystem and returns the borrowed source.
79    pub fn into_inner(self) -> &'a mut S {
80        match self {
81            Self::Fat(fat) => fat.into_inner().0,
82        }
83    }
84}
85
86fn fat_variant(format: hadris_fat::sync::FatType) -> FatVariant {
87    match format {
88        hadris_fat::sync::FatType::Fat12 => FatVariant::Fat12,
89        hadris_fat::sync::FatType::Fat16 => FatVariant::Fat16,
90        hadris_fat::sync::FatType::Fat32 => FatVariant::Fat32,
91    }
92}