human_number: add parser for human-readable sizes

This commit is contained in:
Matt Bilker 2022-05-08 04:47:43 +00:00
parent 7490f5bfbc
commit 375386e67c
No known key found for this signature in database
GPG Key ID: 69ADF8AEB6C8B5D1
2 changed files with 152 additions and 0 deletions

148
src/human_number.rs Normal file
View File

@ -0,0 +1,148 @@
use std::convert::TryInto;
use std::fmt;
use serde::de::{Deserializer, Error, Unexpected, Visitor};
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(HumanNumberVisitor)
}
struct HumanNumberVisitor;
impl<'de> Visitor<'de> for HumanNumberVisitor {
type Value = Option<u64>;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("unsigned number or quoted human-readable unsigned number")
}
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
where
E: Error,
{
v.try_into()
.map_err(Error::custom)
.and_then(|v| self.visit_u64(v))
}
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
where
E: Error,
{
Ok(Some(v))
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: Error,
{
let v = v.trim();
if v.is_empty() {
return Err(Error::invalid_value(
Unexpected::Str(v),
&"non-empty string",
));
}
// Using `bytes` instead of `chars` here because `split_at` takes a byte offset
match v
.bytes()
.map(|byte| byte as char)
.position(|ch| !(ch.is_numeric() || ch == '.'))
{
Some(unit_index) => {
let (value, unit) = v.split_at(unit_index);
let value: f64 = value.parse().map_err(Error::custom)?;
let multiple: u64 = match unit.trim_start() {
"KB" | "kB" => 1000,
"MB" => 1000 * 1000,
"GB" => 1000 * 1000 * 1000,
"TB" => 1000 * 1000 * 1000 * 1000,
"KiB" => 1024,
"MiB" => 1024 * 1024,
"GiB" => 1024 * 1024 * 1024,
"TiB" => 1024 * 1024 * 1024 * 1024,
unit => {
return Err(Error::invalid_value(
Unexpected::Str(unit),
&"known unit of measurement",
))
}
};
let value = value * (multiple as f64);
Ok(Some(value.round() as u64))
}
None => {
// No unit found, interpret as raw number
v.parse().map(Some).map_err(Error::custom)
}
}
}
fn visit_none<E>(self) -> Result<Self::Value, E>
where
E: Error,
{
Ok(None)
}
fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(self)
}
}
#[cfg(test)]
mod test {
use serde::de::value::Error;
use serde::de::IntoDeserializer;
use super::deserialize;
#[test]
fn test_deserialize() {
fn check_result(input: &str, value: u64) {
assert_eq!(
deserialize(input.into_deserializer()),
Ok::<_, Error>(Some(value))
);
}
check_result("1234", 1234);
check_result("1234 ", 1234);
check_result(" 1234", 1234);
check_result(" 1234 ", 1234);
check_result("1234kB", 1234 * 1000);
check_result("1234KB", 1234 * 1000);
check_result("1234MB", 1234 * 1000 * 1000);
check_result("1234GB", 1234 * 1000 * 1000 * 1000);
check_result("1234TB", 1234 * 1000 * 1000 * 1000 * 1000);
check_result("1234KiB", 1234 * 1024);
check_result("1234MiB", 1234 * 1024 * 1024);
check_result("1234GiB", 1234 * 1024 * 1024 * 1024);
check_result("1234TiB", 1234 * 1024 * 1024 * 1024 * 1024);
check_result("1234 kB", 1234 * 1000);
check_result("1234 KB", 1234 * 1000);
check_result("1234 MB", 1234 * 1000 * 1000);
check_result("1234 GB", 1234 * 1000 * 1000 * 1000);
check_result("1234 TB", 1234 * 1000 * 1000 * 1000 * 1000);
check_result("1234 KiB", 1234 * 1024);
check_result("1234 MiB", 1234 * 1024 * 1024);
check_result("1234 GiB", 1234 * 1024 * 1024 * 1024);
check_result("1234 TiB", 1234 * 1024 * 1024 * 1024 * 1024);
}
}

View File

@ -26,6 +26,7 @@ use serde::Deserialize;
mod config;
mod dump;
mod format;
mod human_number;
mod log;
use crate::config::Config;
@ -195,8 +196,11 @@ struct VgpuProfileOverride<'a> {
multi_vgpu_supported: Option<u32>,
pci_id: Option<u64>,
pci_device_id: Option<u64>,
#[serde(with = "human_number")]
framebuffer: Option<u64>,
#[serde(with = "human_number")]
mappable_video_size: Option<u64>,
#[serde(with = "human_number")]
framebuffer_reservation: Option<u64>,
encoder_capacity: Option<u64>,
bar1_length: Option<u64>,