use serde::{Deserialize, Serialize};
pub const MAX_STATS_POLL_INTERVAL: u8 = u8::MAX;
#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum BalloonHintingOp {
Start,
Status,
Stop,
}
impl BalloonHintingOp {
pub fn from_url_segment(s: &str) -> Result<Self, String> {
match s {
"start" => Ok(Self::Start),
"status" => Ok(Self::Status),
"stop" => Ok(Self::Stop),
other => Err(format!(
"Invalid balloon-hinting op: must be one of start | status | stop (got {other})"
)),
}
}
}
#[derive(Debug, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct RawBalloonConfig {
pub amount_mib: u64,
#[serde(default)]
pub deflate_on_oom: bool,
#[serde(default)]
pub stats_polling_interval_s: u8,
#[serde(default)]
pub free_page_hinting: bool,
#[serde(default)]
pub free_page_reporting: bool,
}
#[derive(Debug, Clone, Serialize)]
#[non_exhaustive]
pub struct BalloonConfig {
pub amount_mib: u64,
pub deflate_on_oom: bool,
pub stats_polling_interval_s: u8,
pub free_page_hinting: bool,
pub free_page_reporting: bool,
}
impl TryFrom<RawBalloonConfig> for BalloonConfig {
type Error = String;
fn try_from(raw: RawBalloonConfig) -> Result<Self, Self::Error> {
Ok(Self {
amount_mib: raw.amount_mib,
deflate_on_oom: raw.deflate_on_oom,
stats_polling_interval_s: raw.stats_polling_interval_s,
free_page_hinting: raw.free_page_hinting,
free_page_reporting: raw.free_page_reporting,
})
}
}
#[derive(Debug, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct RawBalloonUpdate {
pub amount_mib: u64,
}
#[derive(Debug, Clone, Serialize)]
#[non_exhaustive]
pub struct BalloonUpdate {
pub amount_mib: u64,
}
impl TryFrom<RawBalloonUpdate> for BalloonUpdate {
type Error = String;
fn try_from(raw: RawBalloonUpdate) -> Result<Self, Self::Error> {
Ok(Self {
amount_mib: raw.amount_mib,
})
}
}
#[derive(Debug, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct RawBalloonStatsUpdate {
pub stats_polling_interval_s: u8,
}
#[derive(Debug, Clone, Serialize)]
#[non_exhaustive]
pub struct BalloonStatsUpdate {
pub stats_polling_interval_s: u8,
}
impl TryFrom<RawBalloonStatsUpdate> for BalloonStatsUpdate {
type Error = String;
fn try_from(raw: RawBalloonStatsUpdate) -> Result<Self, Self::Error> {
Ok(Self {
stats_polling_interval_s: raw.stats_polling_interval_s,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_should_accept_minimal_balloon_config() {
let raw = RawBalloonConfig {
amount_mib: 0,
deflate_on_oom: false,
stats_polling_interval_s: 0,
free_page_hinting: false,
free_page_reporting: false,
};
let cfg = BalloonConfig::try_from(raw).unwrap();
assert_eq!(cfg.amount_mib, 0);
}
#[test]
fn test_should_reject_oversize_polling_interval() {
let raw = RawBalloonConfig {
amount_mib: 0,
deflate_on_oom: false,
stats_polling_interval_s: u8::MAX,
free_page_hinting: false,
free_page_reporting: false,
};
assert!(BalloonConfig::try_from(raw).is_ok());
}
#[test]
fn test_should_round_trip_balloon_update() {
let raw = RawBalloonUpdate { amount_mib: 128 };
let upd = BalloonUpdate::try_from(raw).unwrap();
assert_eq!(upd.amount_mib, 128);
}
}