aede_core/
user_preferences.rs1use super::UserData;
3use crate::{json::Json, store::StoreError};
4use std::collections::BTreeSet;
5
6#[derive(Debug, Clone)]
8pub struct ClientPreference {
9 pub owner: String,
11 pub key: String,
13 pub value: String,
15}
16pub fn valid_preference(key: &str, value: &str) -> bool {
18 !key.is_empty()
19 && key.len() <= 128
20 && key
21 .bytes()
22 .all(|b| b.is_ascii_alphanumeric() || b".-_:".contains(&b))
23 && value.len() <= 8192
24 && crate::json::parse(value).is_ok()
25}
26pub(super) fn validate(rows: &[ClientPreference]) -> Result<(), StoreError> {
27 if rows.len() > 8192 {
28 return Err(StoreError::Invalid("too many client preferences"));
29 }
30 let mut known = BTreeSet::new();
31 for row in rows {
32 if row.owner.is_empty()
33 || row.owner.len() > 256
34 || !valid_preference(&row.key, &row.value)
35 || !known.insert((&row.owner, &row.key))
36 {
37 return Err(StoreError::Invalid(
38 "invalid or duplicate client preference",
39 ));
40 }
41 }
42 Ok(())
43}
44pub(super) fn write(data: &UserData, root: &mut Json) {
45 if data.client_preferences.is_empty() {
46 return;
47 }
48 root.set(
49 "client_preferences",
50 Json::Arr(
51 data.client_preferences
52 .iter()
53 .map(|entry| {
54 let mut row = Json::obj();
55 row.set("owner", entry.owner.as_str().into());
56 row.set("key", entry.key.as_str().into());
57 row.set("value", entry.value.as_str().into());
58 row
59 })
60 .collect(),
61 ),
62 );
63}
64pub(super) fn read(root: &Json) -> Result<Vec<ClientPreference>, StoreError> {
65 let Some(table) = root.get("client_preferences") else {
66 return Ok(vec![]);
67 };
68 let rows = table
69 .as_arr()
70 .ok_or(StoreError::Invalid("client preferences must be an array"))?;
71 if rows.len() > 8192 {
72 return Err(StoreError::Invalid("too many client preferences"));
73 }
74 let rows = rows
75 .iter()
76 .map(|row| {
77 let owner = row
78 .field_str("owner")
79 .ok_or(StoreError::Invalid("missing preference owner"))?;
80 let key = row
81 .field_str("key")
82 .ok_or(StoreError::Invalid("missing preference key"))?;
83 let value = row
84 .field_str("value")
85 .ok_or(StoreError::Invalid("missing preference value"))?;
86 Ok(ClientPreference { owner, key, value })
87 })
88 .collect::<Result<Vec<_>, StoreError>>()?;
89 validate(&rows)?;
90 Ok(rows)
91}
92#[cfg(test)]
93#[path = "user_preferences_tests.rs"]
94mod tests;