1use crate::device::BlockDeviceExt;
2use std::str::{self, FromStr};
3
4pub trait SectorExt: BlockDeviceExt {
6 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#[derive(Debug, PartialEq, Clone, Copy, Hash)]
36pub enum Sector {
37 Start,
39 End,
41 Unit(u64),
44 UnitFromEnd(u64),
46 Megabyte(u64),
49 MegabyteFromEnd(u64),
51 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}