section-config: add lookup and convert_to_typed_array helpers
These helpers exist on the untyped SectionConfigData struct, but not on the typed one. To facilitate the migration to the typed SectionConfigData<T> add these as macros. These can be used to simply lookup an item with a specific type by passing the type, e.g.: lookup!(config, "root", UserSectionConfig::User) or retrieve a vector with the sections that correspond to the passed type, e.g.: convert_to_typed_array!(config, UserSectionConfig::ApiToken) Also add some simple unit-tests to test them. Signed-off-by: Gabriel Goller <g.goller@proxmox.com>
This commit is contained in:
committed by
Wolfgang Bumiller
parent
f3a738370f
commit
a77ef4191a
@ -19,3 +19,6 @@ serde_json.workspace = true
|
||||
proxmox-schema.workspace = true
|
||||
# FIXME: remove!
|
||||
proxmox-lang.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
|
@ -211,6 +211,35 @@ impl<T> SectionConfigData<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! lookup {
|
||||
($map:expr, $key:expr, $variant:path) => {
|
||||
match $map.get($key) {
|
||||
Some($variant(inner)) => Some(inner),
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! convert_to_typed_array {
|
||||
($map:expr, $variant:path) => {
|
||||
$map.values()
|
||||
.filter_map(|value| match value {
|
||||
$variant(inner) => Some(inner),
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(inline)]
|
||||
pub use convert_to_typed_array;
|
||||
#[doc(inline)]
|
||||
pub use lookup;
|
||||
|
||||
impl<T: ApiSectionDataEntry + DeserializeOwned> TryFrom<RawSectionConfigData>
|
||||
for SectionConfigData<T>
|
||||
{
|
||||
@ -371,6 +400,7 @@ mod test {
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use proxmox_schema::{ApiStringFormat, EnumEntry, ObjectSchema, Schema, StringSchema};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{SectionConfig, SectionConfigPlugin};
|
||||
|
||||
@ -512,4 +542,109 @@ mod test {
|
||||
.expect("failed to write out section config");
|
||||
assert_eq!(written, raw);
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
struct User {
|
||||
user_id: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
struct Token {
|
||||
token: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
enum UserSectionConfig {
|
||||
#[serde(rename = "user")]
|
||||
User(User),
|
||||
#[serde(rename = "token")]
|
||||
Token(Token),
|
||||
}
|
||||
|
||||
const USER_PROPERTIES: ObjectSchema = ObjectSchema::new(
|
||||
"User",
|
||||
&[("user_id", false, &StringSchema::new("Some id.").schema())],
|
||||
);
|
||||
|
||||
const TOKEN_PROPERTIES: ObjectSchema = ObjectSchema::new(
|
||||
"Token",
|
||||
&[("token", false, &StringSchema::new("Some token.").schema())],
|
||||
);
|
||||
|
||||
impl ApiSectionDataEntry for UserSectionConfig {
|
||||
fn section_config() -> &'static SectionConfig {
|
||||
static SC: OnceLock<SectionConfig> = OnceLock::new();
|
||||
|
||||
SC.get_or_init(|| {
|
||||
let mut config = SectionConfig::new(&ID_SCHEMA);
|
||||
config.register_plugin(SectionConfigPlugin::new(
|
||||
"user".to_string(),
|
||||
None,
|
||||
&USER_PROPERTIES,
|
||||
));
|
||||
config.register_plugin(SectionConfigPlugin::new(
|
||||
"token".to_string(),
|
||||
None,
|
||||
&TOKEN_PROPERTIES,
|
||||
));
|
||||
config
|
||||
})
|
||||
}
|
||||
|
||||
fn section_type(&self) -> &'static str {
|
||||
match self {
|
||||
UserSectionConfig::User(_) => "user",
|
||||
UserSectionConfig::Token(_) => "token",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_macros() {
|
||||
let filename = "sync.cfg";
|
||||
let raw = "\
|
||||
token: first\n\
|
||||
\ttoken 1\n\
|
||||
\n\
|
||||
user: second\n\
|
||||
\tuser_id 2\n\
|
||||
\n\
|
||||
user: third\n\
|
||||
\tuser_id 4\n\
|
||||
";
|
||||
|
||||
let parsed = UserSectionConfig::section_config()
|
||||
.parse(filename, raw)
|
||||
.expect("failed to parse");
|
||||
let config: SectionConfigData<UserSectionConfig> =
|
||||
parsed.try_into().expect("failed to convert");
|
||||
|
||||
let token = lookup!(config, "first", UserSectionConfig::Token);
|
||||
assert_eq!(token.unwrap().token, "1");
|
||||
let user = lookup!(config, "second", UserSectionConfig::User);
|
||||
assert_eq!(user.unwrap().user_id, "2");
|
||||
|
||||
let mut tokens = convert_to_typed_array!(config, UserSectionConfig::Token);
|
||||
tokens.sort();
|
||||
assert_eq!(
|
||||
tokens,
|
||||
vec![&Token {
|
||||
token: "1".to_owned()
|
||||
}]
|
||||
);
|
||||
|
||||
let mut users = convert_to_typed_array!(config, UserSectionConfig::User);
|
||||
users.sort();
|
||||
assert_eq!(
|
||||
users,
|
||||
vec![
|
||||
&User {
|
||||
user_id: "2".to_owned()
|
||||
},
|
||||
&User {
|
||||
user_id: "4".to_owned()
|
||||
}
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user