disk_types/
sector.rs

1use crate::device::BlockDeviceExt;
2use std::str::{self, FromStr};
3
4/// Trait for getting and sectors from a device.
5pub trait SectorExt: BlockDeviceExt {
6    /// Calculates the requested sector from a given `Sector` variant.
7    fn get_sector(&self, sector: Sector) -> u64 {
8        const MIB2: u64 = 2 * 1024 * 1024;
9
10        let end = || self.get_sectors() - (MIB2 / self.get_logical_block_size());
11        let megabyte = |size| (size * 1_000_000) / self.get_logical_block_size();
12
13        match sector {
14            Sector::Start => MIB2 / self.get_logical_block_size(),
15            Sector::End => end(),
16            Sector::Megabyte(size) => megabyte(size),
17            Sector::MegabyteFromEnd(size) => end() - megabyte(size),
18            Sector::Unit(size) => size,
19            Sector::UnitFromEnd(size) => end() - size,
20            Sector::Percent(value) => {
21                if value == ::std::u16::MAX {
22                    self.get_sectors()
23                } else {
24                    ((self.get_sectors() * self.get_logical_block_size()) / ::std::u16::MAX as u64)
25                        * value as u64
26                        / self.get_logical_block_size()
27                }
28            }
29        }
30    }
31}
32
33/// Used with the `Disk::get_sector` method for converting a more human-readable unit
34/// into the corresponding sector for the given disk.
35#[derive(Debug, PartialEq, Clone, Copy, Hash)]
36pub enum Sector {
37    /// The first sector in the disk where partitions should be created.
38    Start,
39    /// The last sector in the disk where partitions should be created.
40    End,
41    /// A raw value that directly corrects to the exact number of sectors that
42    /// will be used.
43    Unit(u64),
44    /// Similar to the above, but subtracting from the end.
45    UnitFromEnd(u64),
46    /// Rather than specifying the sector count, the user can specify the actual size in megabytes.
47    /// This value will later be used to get the exact sector count based on the sector size.
48    Megabyte(u64),
49    /// Similar to the above, but subtracting from the end.
50    MegabyteFromEnd(u64),
51    /// The percent can be represented by specifying a value between 0 and
52    /// u16::MAX, where u16::MAX is 100%.
53    Percent(u16),
54}
55
56impl From<u64> for Sector {
57    fn from(sectors: u64) -> Sector { Sector::Unit(sectors) }
58}
59
60impl FromStr for Sector {
61    type Err = &'static str;
62
63    fn from_str(input: &str) -> Result<Self, Self::Err> {
64        if input.ends_with('M') {
65            if input.starts_with('-') {
66                if let Ok(value) = input[1..input.len() - 1].parse::<u64>() {
67                    return Ok(Sector::MegabyteFromEnd(value));
68                }
69            } else if let Ok(value) = input[..input.len() - 1].parse::<u64>() {
70                return Ok(Sector::Megabyte(value));
71            }
72        } else if input.ends_with('%') {
73            if let Ok(value) = input[..input.len() - 1].parse::<u16>() {
74                if value <= 100 {
75                    return Ok(Sector::Percent(value));
76                }
77            }
78        } else if input == "start" {
79            return Ok(Sector::Start);
80        } else if input == "end" {
81            return Ok(Sector::End);
82        } else if input.starts_with('-') {
83            if let Ok(value) = input[1..input.len()].parse::<u64>() {
84                return Ok(Sector::UnitFromEnd(value));
85            }
86        } else if let Ok(value) = input[..input.len()].parse::<u64>() {
87            return Ok(Sector::Unit(value));
88        }
89
90        Err("invalid sector value")
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97    use std::u16;
98    use std::path::Path;
99
100    struct FictionalBlock(u64);
101
102    impl SectorExt for FictionalBlock {}
103
104    impl BlockDeviceExt for FictionalBlock {
105        fn get_device_name(&self) -> &str { "fictional" }
106        fn get_device_path(&self) -> &Path { Path::new("/dev/fictional")  }
107        fn get_sectors(&self) -> u64 { self.0 }
108        fn get_logical_block_size(&self) -> u64 { 512 }
109    }
110
111    #[test]
112    fn sector_get() {
113        let block = FictionalBlock(100_000_000);
114        assert_eq!(4096, block.get_sector(Sector::Start));
115        assert_eq!(99_995_904, block.get_sector(Sector::End));
116        assert_eq!(1000, block.get_sector(Sector::Unit(1000)));
117        assert_eq!(99_994_904, block.get_sector(Sector::UnitFromEnd(1000)));
118    }
119
120    #[test]
121    fn sector_get_megabyte() {
122        let block = FictionalBlock(100_000_000);
123        assert_eq!(2_000_000, block.get_sector(Sector::Megabyte(1024)));
124        assert_eq!(97_995_904, block.get_sector(Sector::MegabyteFromEnd(1024)));
125    }
126
127    #[test]
128    fn sector_get_percent() {
129        let block = FictionalBlock(100_000_000);
130        assert_eq!(0, block.get_sector(Sector::Percent(0)));
131        assert_eq!(24_998_826, block.get_sector(Sector::Percent(u16::MAX / 4)));
132        assert_eq!(49_999_178, block.get_sector(Sector::Percent(u16::MAX / 2)));
133        assert_eq!(100_000_000, block.get_sector(Sector::Percent(u16::MAX)));
134    }
135
136    #[test]
137    fn sector_percentages() {
138        assert_eq!("0%".parse::<Sector>(), Ok(Sector::Percent(0)));
139        assert_eq!("50%".parse::<Sector>(), Ok(Sector::Percent(50)));
140        assert_eq!("100%".parse::<Sector>(), Ok(Sector::Percent(100)));
141    }
142
143    #[test]
144    fn sector_ends() {
145        assert_eq!("start".parse::<Sector>(), Ok(Sector::Start));
146        assert_eq!("end".parse::<Sector>(), Ok(Sector::End));
147    }
148
149    #[test]
150    fn sector_units() {
151        assert_eq!("0".parse::<Sector>(), Ok(Sector::Unit(0)));
152        assert_eq!("1024".parse::<Sector>(), Ok(Sector::Unit(1024)));
153        assert_eq!("-1024".parse::<Sector>(), Ok(Sector::UnitFromEnd(1024)));
154    }
155
156    #[test]
157    fn sector_megabytes() {
158        assert_eq!("0M".parse::<Sector>(), Ok(Sector::Megabyte(0)));
159        assert_eq!("500M".parse::<Sector>(), Ok(Sector::Megabyte(500)));
160        assert_eq!("20480M".parse::<Sector>(), Ok(Sector::Megabyte(20480)));
161        assert_eq!("-0M".parse::<Sector>(), Ok(Sector::MegabyteFromEnd(0)));
162        assert_eq!("-500M".parse::<Sector>(), Ok(Sector::MegabyteFromEnd(500)));
163        assert_eq!("-20480M".parse::<Sector>(), Ok(Sector::MegabyteFromEnd(20480)));
164    }
165}