///|
pub(all) struct Ttl {
seconds_value : UInt64
} derive(Eq, Debug)
///|
fn ttl_error(code : ZoneErrorCode, message : String, column : Int) -> ZoneError {
ZoneError::new(code, message, SourceSpan::point(0, column))
}
///|
fn ttl_unit_multiplier(code : Int) -> UInt64? {
match code {
119 | 87 => Some(604800UL)
100 | 68 => Some(86400UL)
104 | 72 => Some(3600UL)
109 | 77 => Some(60UL)
115 | 83 => Some(1UL)
_ => None
}
}
///|
fn checked_ttl_add(
total : UInt64,
value : UInt64,
multiplier : UInt64,
column : Int,
) -> Result[UInt64, ZoneError] {
let limit = 0xFFFFFFFFUL
if value > limit / multiplier || total > limit - value * multiplier {
Err(ttl_error(TtlOverflow, "TTL exceeds the unsigned 32-bit range", column))
} else {
Ok(total + value * multiplier)
}
}
///|
pub fn Ttl::from_seconds(seconds : UInt64) -> Result[Ttl, ZoneError] {
if seconds > 0xFFFFFFFFUL {
Err(ttl_error(TtlOverflow, "TTL exceeds the unsigned 32-bit range", 0))
} else {
Ok({ seconds_value: seconds })
}
}
///|
/// Parse a BIND-style TTL such as `3600`, `2h30m` or `1w2d`.
pub fn parse_ttl(text : String) -> Result[Ttl, ZoneError] {
if text.length() == 0 {
return Err(ttl_error(InvalidTtl, "TTL is empty", 0))
}
let mut total = 0UL
let mut current = 0UL
let mut digits = 0
let mut saw_unit = false
for index = 0; index < text.length(); index = index + 1 {
let code = text[index].to_int()
if code >= 48 && code <= 57 {
let digit = (code - 48).to_uint64()
if current > 0xFFFFFFFFUL / 10UL || current * 10UL > 0xFFFFFFFFUL - digit {
return Err(
ttl_error(TtlOverflow, "TTL numeric component overflows", index),
)
}
current = current * 10UL + digit
digits = digits + 1
} else {
let multiplier = match ttl_unit_multiplier(code) {
Some(value) => value
None =>
return Err(
ttl_error(InvalidTtl, "TTL contains an invalid unit", index),
)
}
if digits == 0 {
return Err(
ttl_error(InvalidTtl, "TTL unit has no numeric value", index),
)
}
total = match checked_ttl_add(total, current, multiplier, index) {
Ok(value) => value
Err(error) => return Err(error)
}
current = 0UL
digits = 0
saw_unit = true
}
}
if digits == 0 {
if saw_unit {
Ttl::from_seconds(total)
} else {
Err(ttl_error(InvalidTtl, "TTL has no numeric value", 0))
}
} else if saw_unit {
match checked_ttl_add(total, current, 1UL, text.length() - 1) {
Ok(value) => Ttl::from_seconds(value)
Err(error) => Err(error)
}
} else {
Ttl::from_seconds(current)
}
}
///|
pub fn Ttl::seconds(self : Ttl) -> UInt64 {
self.seconds_value
}
///|
fn append_ttl_unit(
result : String,
remaining : UInt64,
unit : UInt64,
suffix : String,
) -> (String, UInt64) {
if remaining >= unit {
(result + (remaining / unit).to_string() + suffix, remaining % unit)
} else {
(result, remaining)
}
}
///|
pub fn Ttl::to_text(self : Ttl) -> String {
if self.seconds_value == 0UL {
return "0"
}
let (weeks, after_weeks) = append_ttl_unit(
"",
self.seconds_value,
604800UL,
"w",
)
let (days, after_days) = append_ttl_unit(weeks, after_weeks, 86400UL, "d")
let (hours, after_hours) = append_ttl_unit(days, after_days, 3600UL, "h")
let (minutes, after_minutes) = append_ttl_unit(hours, after_hours, 60UL, "m")
if after_minutes > 0UL {
minutes + after_minutes.to_string() + "s"
} else {
minutes
}
}