use std::time::Duration;
use crate::{Error, Result};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RenewalHint {
pub sleep_before_renew: Option<Duration>,
pub increment: Option<String>,
pub increment_seconds: Option<u64>,
}
impl RenewalHint {
#[must_use]
pub fn from_ttl(lease_duration_secs: u64, renewable: bool) -> Self {
if !renewable || lease_duration_secs == 0 {
return Self {
sleep_before_renew: None,
increment: None,
increment_seconds: None,
};
}
let sleep_secs =
((u128::from(lease_duration_secs) * 2) / 3).min(u128::from(u64::MAX)) as u64;
Self {
sleep_before_renew: Some(Duration::from_secs(sleep_secs)),
increment: Some(format!("{lease_duration_secs}s")),
increment_seconds: Some(lease_duration_secs),
}
}
}
#[must_use]
pub fn duration_to_bao_string(duration: Duration) -> String {
let mut total_seconds = duration.as_secs();
if duration.subsec_nanos() > 0 {
total_seconds = total_seconds.saturating_add(1);
}
if total_seconds == 0 {
return "0s".to_owned();
}
let hours = total_seconds / 3600;
let minutes = (total_seconds % 3600) / 60;
let seconds = total_seconds % 60;
let mut output = String::new();
if hours > 0 {
output.push_str(&hours.to_string());
output.push('h');
}
if minutes > 0 {
output.push_str(&minutes.to_string());
output.push('m');
}
if seconds > 0 {
output.push_str(&seconds.to_string());
output.push('s');
}
output
}
pub(crate) fn nonzero_duration_to_bao_string(
duration: Duration,
field: &'static str,
) -> Result<String> {
if duration.is_zero() {
return Err(Error::InvalidParameter(format!(
"{field} duration must be non-zero"
)));
}
Ok(duration_to_bao_string(duration))
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use super::{RenewalHint, duration_to_bao_string, nonzero_duration_to_bao_string};
#[test]
fn converts_duration_to_openbao_string() {
assert_eq!(duration_to_bao_string(Duration::ZERO), "0s");
assert_eq!(duration_to_bao_string(Duration::from_secs(30)), "30s");
assert_eq!(duration_to_bao_string(Duration::from_secs(90)), "1m30s");
assert_eq!(duration_to_bao_string(Duration::from_secs(3661)), "1h1m1s");
assert_eq!(duration_to_bao_string(Duration::from_millis(1)), "1s");
}
#[test]
fn nonzero_duration_conversion_rejects_zero() {
assert!(nonzero_duration_to_bao_string(Duration::ZERO, "test ttl").is_err());
assert!(matches!(
nonzero_duration_to_bao_string(Duration::from_secs(1), "test ttl").as_deref(),
Ok("1s")
));
}
#[test]
fn builds_renewal_hint_from_ttl() {
let hint = RenewalHint::from_ttl(3600, true);
assert_eq!(hint.sleep_before_renew, Some(Duration::from_secs(2400)));
assert_eq!(hint.increment.as_deref(), Some("3600s"));
assert_eq!(hint.increment_seconds, Some(3600));
let non_renewable = RenewalHint::from_ttl(3600, false);
assert_eq!(non_renewable.sleep_before_renew, None);
assert_eq!(non_renewable.increment, None);
assert_eq!(non_renewable.increment_seconds, None);
let zero_ttl = RenewalHint::from_ttl(0, true);
assert_eq!(zero_ttl.sleep_before_renew, None);
assert_eq!(zero_ttl.increment, None);
assert_eq!(zero_ttl.increment_seconds, None);
}
}