This commit is contained in:
Benoît CORTIER 2025-04-30 23:08:48 +09:00
parent cd3973e0de
commit c8a8891f20
No known key found for this signature in database
GPG key ID: FE943BF70ABEA672
4 changed files with 69 additions and 28 deletions

3
Cargo.lock generated
View file

@ -2638,6 +2638,9 @@ version = "0.0.0"
[[package]]
name = "ironrdp-propertyset"
version = "0.1.0"
dependencies = [
"tracing",
]
[[package]]
name = "ironrdp-rdcleanpath"

View file

@ -16,5 +16,8 @@ categories.workspace = true
doctest = false
test = false
[dependencies]
tracing = { version = "0.1", features = ["log"] }
[lints]
workspace = true

View file

@ -4,6 +4,11 @@
extern crate alloc;
#[macro_use]
extern crate tracing;
use core::fmt::Display;
use alloc::borrow::Cow;
use alloc::collections::BTreeMap;
use alloc::string::String;
@ -22,15 +27,31 @@ impl PropertySet {
}
pub fn insert(&mut self, key: impl Into<Key>, value: impl Into<Value>) -> Option<Value> {
self.inner.insert(key.into(), value.into())
let (key, value) = (key.into(), value.into());
debug!("PropertySet::insert({key}, {value})");
self.inner.insert(key, value)
}
pub fn remove(&mut self, key: &str) -> Option<Value> {
self.inner.remove(key)
let value = self.inner.remove(key);
match &value {
Some(value) => debug!("PropertySet::remove({key}) = {value}"),
None => debug!("PropertySet::remove({key}) = None"),
}
value
}
pub fn get<'a, V: ExtractFrom<&'a Value>>(&'a self, key: &str) -> Option<V> {
self.inner.get(key).and_then(|val| V::extract_from(val, private::Token))
let value = self.inner.get(key);
match &value {
Some(value) => debug!("PropertySet::get({key}) = {value}"),
None => debug!("PropertySet::get({key}) = None"),
}
value.and_then(|val| V::extract_from(val, private::Token))
}
pub fn iter(&self) -> impl Iterator<Item = (&Key, &Value)> {
@ -67,7 +88,7 @@ pub trait ExtractFrom<Value>: Sized {
}
/// Represents a value of any type of the .RDP file format.
#[derive(Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Value {
/// Numerical value.
Int(i64),
@ -93,6 +114,15 @@ impl Value {
}
}
impl Display for Value {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Value::Int(value) => write!(f, "{value}"),
Value::Str(value) => write!(f, "\"{value}\""),
}
}
}
impl_from!(String => Value::Str);
impl_from!(&str => Value::Str);
impl_from!(u8 => Value::Int);

View file

@ -16,7 +16,6 @@ pub enum ErrorKind {
UnknownType { ty: String },
InvalidValue { ty: String, value: String },
MalformedLine { line: String },
DuplicatedKey { key: String },
}
#[derive(Debug, Clone)]
@ -37,28 +36,21 @@ impl fmt::Display for Error {
write!(f, "invalid value at line {line_number} for type {ty} ({value})")
}
ErrorKind::MalformedLine { line } => write!(f, "malformed line at line {line_number} ({line})"),
ErrorKind::DuplicatedKey { key } => write!(f, "duplicated key at line {line_number} ({key})"),
}
}
}
pub struct ParseResult {
pub store: PropertySet,
pub errors: Vec<Error>,
}
pub fn parse(input: &str) -> ParseResult {
let mut store = PropertySet::new();
pub fn load(properties: &mut PropertySet, input: &str) -> Result<(), Vec<Error>> {
let mut errors = Vec::new();
for (idx, line) in input.lines().enumerate() {
let mut split = line.splitn(2, ':');
if let (Some(key), Some(ty), Some(value)) = (split.next(), split.next(), split.next()) {
let is_duplicated = match ty {
match ty {
"i" => {
if let Ok(value) = value.parse::<i64>() {
store.insert(key.to_owned(), value).is_some()
properties.insert(key.to_owned(), value);
} else {
errors.push(Error {
kind: ErrorKind::InvalidValue {
@ -67,24 +59,17 @@ pub fn parse(input: &str) -> ParseResult {
},
line: idx,
});
continue;
}
}
"s" => store.insert(key.to_owned(), value).is_some(),
"s" => {
properties.insert(key.to_owned(), value);
}
_ => {
errors.push(Error {
kind: ErrorKind::UnknownType { ty: ty.to_owned() },
line: idx,
});
continue;
}
};
if is_duplicated {
errors.push(Error {
kind: ErrorKind::DuplicatedKey { key: key.to_owned() },
line: idx,
});
}
} else {
errors.push(Error {
@ -94,13 +79,33 @@ pub fn parse(input: &str) -> ParseResult {
}
}
ParseResult { store, errors }
if errors.is_empty() {
Ok(())
} else {
Err(errors)
}
}
pub fn write(store: &PropertySet) -> String {
pub struct ParseResult {
pub properties: PropertySet,
pub errors: Vec<Error>,
}
pub fn parse(input: &str) -> ParseResult {
let mut properties = PropertySet::new();
let errors = match load(&mut properties, input) {
Ok(()) => Vec::new(),
Err(errors) => errors,
};
ParseResult { properties, errors }
}
pub fn write(properties: &PropertySet) -> String {
let mut buf = String::new();
for (key, value) in store.iter() {
for (key, value) in properties.iter() {
buf.push_str(key);
match value {