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
|
proxmox-schema.workspace = true
|
||||||
# FIXME: remove!
|
# FIXME: remove!
|
||||||
proxmox-lang.workspace = true
|
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>
|
impl<T: ApiSectionDataEntry + DeserializeOwned> TryFrom<RawSectionConfigData>
|
||||||
for SectionConfigData<T>
|
for SectionConfigData<T>
|
||||||
{
|
{
|
||||||
@ -371,6 +400,7 @@ mod test {
|
|||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
use proxmox_schema::{ApiStringFormat, EnumEntry, ObjectSchema, Schema, StringSchema};
|
use proxmox_schema::{ApiStringFormat, EnumEntry, ObjectSchema, Schema, StringSchema};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{SectionConfig, SectionConfigPlugin};
|
use crate::{SectionConfig, SectionConfigPlugin};
|
||||||
|
|
||||||
@ -512,4 +542,109 @@ mod test {
|
|||||||
.expect("failed to write out section config");
|
.expect("failed to write out section config");
|
||||||
assert_eq!(written, raw);
|
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