Implement bounded Notation LLSD codec
This commit is contained in:
@@ -273,11 +273,11 @@ impl OSD {
|
||||
Ok(match self {
|
||||
Self::Boolean(value) => if *value { "1" } else { "0" }.to_owned(),
|
||||
Self::Integer(value) => value.to_string(),
|
||||
Self::Real(value) => format_real(*value),
|
||||
Self::Real(value) => format_real_for_codec(*value),
|
||||
Self::String(value) | Self::LlsdXml(value) => value.clone(),
|
||||
Self::UUID(value) => value.to_string(),
|
||||
Self::Date(value) => format_system_time(*value),
|
||||
Self::Uri(value) => value.0.clone(),
|
||||
Self::Date(value) => format_system_time_for_codec(*value),
|
||||
Self::Uri(value) => format_uri_for_codec(&value.0),
|
||||
Self::Binary(value) => base64::engine::general_purpose::STANDARD.encode(value),
|
||||
_ => String::new(),
|
||||
})
|
||||
@@ -296,7 +296,7 @@ impl OSD {
|
||||
pub fn as_date(&self) -> Result<SystemTime, Error> {
|
||||
Ok(match self {
|
||||
Self::Date(value) => *value,
|
||||
Self::String(value) => parse_system_time(value).unwrap_or(UNIX_EPOCH),
|
||||
Self::String(value) => parse_system_time_for_codec(value).unwrap_or(UNIX_EPOCH),
|
||||
_ => UNIX_EPOCH,
|
||||
})
|
||||
}
|
||||
@@ -776,7 +776,7 @@ impl OSDReal {
|
||||
Ok(self.value)
|
||||
}
|
||||
pub fn as_string(&self) -> Result<String, Error> {
|
||||
Ok(format_real(self.value))
|
||||
Ok(format_real_for_codec(self.value))
|
||||
}
|
||||
pub fn as_u_integer(&self) -> Result<u32, Error> {
|
||||
Ok(real_to_u32(self.value))
|
||||
@@ -795,7 +795,7 @@ impl OSDString {
|
||||
Ok(string_as_boolean(&self.value))
|
||||
}
|
||||
pub fn as_date(&self) -> Result<SystemTime, Error> {
|
||||
Ok(parse_system_time(&self.value).unwrap_or(UNIX_EPOCH))
|
||||
Ok(parse_system_time_for_codec(&self.value).unwrap_or(UNIX_EPOCH))
|
||||
}
|
||||
pub fn as_integer(&self) -> Result<i32, Error> {
|
||||
Ok(string_to_f64(&self.value).map_or(0, floor_to_i32))
|
||||
@@ -854,7 +854,7 @@ impl OSDDate {
|
||||
Ok(i64::from(unix_seconds_u32(self.value)))
|
||||
}
|
||||
pub fn as_string(&self) -> Result<String, Error> {
|
||||
Ok(format_system_time(self.value))
|
||||
Ok(format_system_time_for_codec(self.value))
|
||||
}
|
||||
pub fn as_u_integer(&self) -> Result<u32, Error> {
|
||||
Ok(unix_seconds_u32(self.value))
|
||||
@@ -1414,7 +1414,7 @@ fn real_to_u64(value: f64) -> u64 {
|
||||
value.round_ties_even() as u64
|
||||
}
|
||||
}
|
||||
fn format_real(value: f64) -> String {
|
||||
pub(crate) fn format_real_for_codec(value: f64) -> String {
|
||||
if value.is_nan() {
|
||||
"NaN".to_owned()
|
||||
} else if value == f64::INFINITY {
|
||||
@@ -1438,7 +1438,7 @@ fn unix_seconds_u32(value: SystemTime) -> u32 {
|
||||
}
|
||||
}
|
||||
|
||||
fn format_system_time(value: SystemTime) -> String {
|
||||
pub(crate) fn format_system_time_for_codec(value: SystemTime) -> String {
|
||||
let (whole, nanos) = match value.duration_since(UNIX_EPOCH) {
|
||||
Ok(duration) => (duration.as_secs() as i64, duration.subsec_nanos()),
|
||||
Err(error) => {
|
||||
@@ -1485,7 +1485,7 @@ fn civil_from_unix(seconds: i64) -> (i32, u32, u32, u32, u32, u32) {
|
||||
)
|
||||
}
|
||||
|
||||
fn parse_system_time(value: &str) -> Option<SystemTime> {
|
||||
pub(crate) fn parse_system_time_for_codec(value: &str) -> Option<SystemTime> {
|
||||
let value = value.trim();
|
||||
let (date, time) = value.split_once('T').or_else(|| value.split_once(' '))?;
|
||||
let mut date_parts = date.split('-');
|
||||
@@ -1556,6 +1556,29 @@ fn valid_uri_reference(value: &str) -> bool {
|
||||
!value.chars().any(char::is_whitespace) && !value.chars().any(char::is_control)
|
||||
}
|
||||
|
||||
fn format_uri_for_codec(value: &str) -> String {
|
||||
if !value.contains("://") {
|
||||
return value.to_owned();
|
||||
}
|
||||
let mut output = String::with_capacity(value.len());
|
||||
for character in value.chars() {
|
||||
match character {
|
||||
' ' => output.push_str("%20"),
|
||||
'"' => output.push_str("%22"),
|
||||
'<' => output.push_str("%3C"),
|
||||
'>' => output.push_str("%3E"),
|
||||
character if character.is_control() => {
|
||||
let mut bytes = [0_u8; 4];
|
||||
for byte in character.encode_utf8(&mut bytes).as_bytes() {
|
||||
write!(output, "%{byte:02X}").expect("writing to String is infallible");
|
||||
}
|
||||
}
|
||||
character => output.push(character),
|
||||
}
|
||||
}
|
||||
output
|
||||
}
|
||||
|
||||
fn escape_text(value: &str) -> String {
|
||||
let mut output = String::with_capacity(value.len() + 2);
|
||||
output.push('"');
|
||||
@@ -1597,7 +1620,7 @@ fn json_text_with_defaults(value: &OSD) -> String {
|
||||
OSD::Undefined => "null".to_owned(),
|
||||
OSD::Boolean(value) => value.to_string(),
|
||||
OSD::Integer(value) => value.to_string(),
|
||||
OSD::Real(value) => format_real(*value),
|
||||
OSD::Real(value) => format_real_for_codec(*value),
|
||||
OSD::String(value) => escape_text(value),
|
||||
OSD::LlsdXml(_) => "null".to_owned(),
|
||||
OSD::UUID(_) | OSD::Date(_) | OSD::Uri(_) => {
|
||||
|
||||
Reference in New Issue
Block a user