Skip to main content

hadris_block/
volume_async.rs

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