byte_unit/byte/parse.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
use rust_decimal::prelude::*;
use super::Byte;
use crate::{common::get_char_from_bytes, unit::parse::read_xib, ParseError, ValueParseError};
/// Associated functions for parsing strings.
impl Byte {
/// Create a new `Byte` instance from a string.
/// The string may be `"10"`, `"10B"`, `"10M"`, `"10MB"`, `"10MiB"`, `"80b"`, `"80Mb"`, `"80Mbit"`.
///
/// You can ignore the case of **"B"** (byte), which means **b** will still be treated as bytes instead of bits.
///
/// # Examples
///
/// ```
/// # use byte_unit::Byte;
/// let byte = Byte::parse_str("123Kib", true).unwrap(); // 123 * 1024 bytes
/// ```
///
/// ```
/// # use byte_unit::Byte;
/// let byte = Byte::parse_str("123Kib", false).unwrap(); // 123 * 1024 bits = 123 * 1024 / 8 bytes
/// ```
pub fn parse_str<S: AsRef<str>>(s: S, ignore_case: bool) -> Result<Self, ParseError> {
let s = s.as_ref().trim();
let mut bytes = s.bytes();
let mut value = match bytes.next() {
Some(e) => match e {
b'0'..=b'9' => Decimal::from(e - b'0'),
_ => {
return Err(ValueParseError::NotNumber(unsafe {
get_char_from_bytes(e, bytes)
})
.into());
},
},
None => return Err(ValueParseError::NoValue.into()),
};
let e = 'outer: loop {
match bytes.next() {
Some(e) => match e {
b'0'..=b'9' => {
value = value
.checked_mul(Decimal::TEN)
.ok_or(ValueParseError::NumberTooLong)?
.checked_add(Decimal::from(e - b'0'))
.ok_or(ValueParseError::NumberTooLong)?;
},
b'.' => {
let mut i = 1u32;
loop {
match bytes.next() {
Some(e) => match e {
b'0'..=b'9' => {
value += {
let mut d = Decimal::from(e - b'0');
d.set_scale(i)
.map_err(|_| ValueParseError::NumberTooLong)?;
d
};
i += 1;
},
_ => {
if i == 1 {
return Err(ValueParseError::NotNumber(unsafe {
get_char_from_bytes(e, bytes)
})
.into());
}
match e {
b' ' => loop {
match bytes.next() {
Some(e) => match e {
b' ' => (),
_ => break 'outer Some(e),
},
None => break 'outer None,
}
},
_ => break 'outer Some(e),
}
},
},
None => {
if i == 1 {
return Err(ValueParseError::NotNumber(unsafe {
get_char_from_bytes(e, bytes)
})
.into());
}
break 'outer None;
},
}
}
},
b' ' => loop {
match bytes.next() {
Some(e) => match e {
b' ' => (),
_ => break 'outer Some(e),
},
None => break 'outer None,
}
},
_ => break 'outer Some(e),
},
None => break None,
}
};
let unit = read_xib(e, bytes, ignore_case, true)?;
Self::from_decimal_with_unit(value, unit)
.ok_or_else(|| ValueParseError::ExceededBounds(value).into())
}
}