minimum for miniconf v0.13

This commit is contained in:
Max Känner 2024-07-25 12:54:33 +02:00
parent d142655a25
commit 14ce7ba845
3 changed files with 87 additions and 58 deletions

View File

@ -1,6 +1,6 @@
[package] [package]
name = "macroconf" name = "macroconf"
version = "0.1.0" version = "0.2.0"
edition = "2021" edition = "2021"
description = "macro for creating configurations using miniconf" description = "macro for creating configurations using miniconf"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
@ -30,5 +30,5 @@ quote = "1.0"
convert_case = "0.6.0" convert_case = "0.6.0"
[dev-dependencies] [dev-dependencies]
miniconf = "0.9" miniconf = { version = "0.13", features = ["json-core"] }
serde = "1.0" serde = "1.0"

View File

@ -142,7 +142,7 @@ fn generate_helper_struct(
.any(|key| attr.path().is_ident(key)) .any(|key| attr.path().is_ident(key))
}); });
field.attrs.push(parse_quote!(#[tree(depth(1))])); field.attrs.push(parse_quote!(#[tree(depth=1)]));
let vis = if matches!(field.vis, Visibility::Public(_)) let vis = if matches!(field.vis, Visibility::Public(_))
|| matches!(field.vis, Visibility::Inherited) || matches!(field.vis, Visibility::Inherited)
{ {
@ -159,6 +159,7 @@ fn generate_helper_struct(
let tree_key = generate_tree_key(&new_type_ident, new_type_miniconf_names.iter()); let tree_key = generate_tree_key(&new_type_ident, new_type_miniconf_names.iter());
let tree_serialize = generate_tree_serialize(&new_type_ident, &new_type_miniconf_consts[..]); let tree_serialize = generate_tree_serialize(&new_type_ident, &new_type_miniconf_consts[..]);
let tree_deserialize = generate_tree_deserialize(&new_type_ident, miniconf_fields); let tree_deserialize = generate_tree_deserialize(&new_type_ident, miniconf_fields);
let tree_any = generate_tree_any(&new_type_ident);
Some(quote! { Some(quote! {
#[allow(clippy::derive_partial_eq_without_eq)] #[allow(clippy::derive_partial_eq_without_eq)]
@ -173,6 +174,7 @@ fn generate_helper_struct(
#tree_key #tree_key
#tree_serialize #tree_serialize
#tree_deserialize #tree_deserialize
#tree_any
}) })
} }
@ -424,32 +426,16 @@ fn generate_tree_key<'a>(
.expect("safe because both iterators (once and original keys) are exact"); .expect("safe because both iterators (once and original keys) are exact");
let max_length = keys.clone().map(|v| v.len()).max(); let max_length = keys.clone().map(|v| v.len()).max();
quote! { quote! {
impl #ident { impl ::miniconf::KeyLookup for #ident {
const __MINICONF_NAMES: [&'static str; #num_keys] = [#(#keys,)*]; const LEN: usize = #num_keys;
const NAMES: &'static [&'static str] = &[#(#keys,)*];
fn name_to_index(value: &str) -> Option<usize> {
Self::NAMES.iter().position(|name| *name == value)
}
} }
impl ::miniconf::TreeKey<1> for #ident { impl ::miniconf::TreeKey<1> for #ident {
fn name_to_index(name: &str) -> ::core::option::Option<usize> {
Self::__MINICONF_NAMES.iter().position(|&n| n == name)
}
fn traverse_by_key<K, F, E>(mut keys: K, mut func: F) -> ::core::result::Result<usize, ::miniconf::Error<E>>
where
K: ::core::iter::Iterator,
K::Item: ::miniconf::Key,
// Writing this to return an iterator instead of using a callback
// would have worse performance (O(n^2) instead of O(n) for matching)
F: FnMut(usize, &str) -> ::core::result::Result<(), E>,
{
let ::core::option::Option::Some(key) = keys.next() else { return ::core::result::Result::Ok(0) };
let index = ::miniconf::Key::find::<1, Self>(&key).ok_or(::miniconf::Error::NotFound(1))?;
let name = Self::__MINICONF_NAMES
.get(index)
.ok_or(::miniconf::Error::NotFound(1))?;
func(index, name)?;
::miniconf::Increment::increment(::core::result::Result::Ok(0))
}
fn metadata() -> miniconf::Metadata { fn metadata() -> miniconf::Metadata {
let mut metadata = miniconf::Metadata::default(); let mut metadata = miniconf::Metadata::default();
metadata.max_depth = 1; metadata.max_depth = 1;
@ -457,6 +443,22 @@ fn generate_tree_key<'a>(
metadata.max_length = #max_length; metadata.max_length = #max_length;
metadata metadata
} }
fn traverse_by_key<K, F, E>(mut keys: K, mut func: F) -> ::core::result::Result<usize, ::miniconf::Error<E>>
where
K: ::miniconf::Keys,
// Writing this to return an iterator instead of using a callback
// would have worse performance (O(n^2) instead of O(n) for matching)
F: FnMut(usize, Option<&'static str>, usize) -> ::core::result::Result<(), E>,
{
let ::core::result::Result::Ok(key) = keys.next::<Self>() else { return ::core::result::Result::Ok(0) };
let index = ::miniconf::Key::find::<Self>(&key).ok_or(::miniconf::Traversal::NotFound(1))?;
let name = <Self as ::miniconf::KeyLookup>::NAMES
.get(index)
.ok_or(::miniconf::Traversal::NotFound(1))?;
func(index, Some(name), #num_keys).map_err(|err| ::miniconf::Error::Inner(1, err))?;
::miniconf::Error::increment_result(::core::result::Result::Ok(0))
}
} }
} }
} }
@ -465,7 +467,7 @@ fn generate_tree_serialize(ident: &Ident, consts: &[Ident]) -> TokenStream2 {
let matches = consts.iter().enumerate().map(|(i, ident)| { let matches = consts.iter().enumerate().map(|(i, ident)| {
let index = i + 1; let index = i + 1;
quote! { quote! {
#index => ::miniconf::Serialize::serialize(&Self::#ident, ser)?, #index => ::miniconf::Serialize::serialize(&Self::#ident, ser).map_err(|err| ::miniconf::Error::Inner(0, err))?,
} }
}); });
quote! { quote! {
@ -476,23 +478,22 @@ fn generate_tree_serialize(ident: &Ident, consts: &[Ident]) -> TokenStream2 {
ser: S, ser: S,
) -> ::core::result::Result<usize, ::miniconf::Error<S::Error>> ) -> ::core::result::Result<usize, ::miniconf::Error<S::Error>>
where where
K: ::core::iter::Iterator, K: ::miniconf::Keys,
K::Item: ::miniconf::Key,
S: ::serde::Serializer, S: ::serde::Serializer,
{ {
let ::core::option::Option::Some(key) = keys.next() else { let ::core::result::Result::Ok(key) = keys.next::<Self>() else {
return ::miniconf::Increment::increment({ return ::miniconf::Error::increment_result({
::miniconf::Serialize::serialize(&self.0, ser)?; ::miniconf::Serialize::serialize(&self.0, ser).map_err(|err| ::miniconf::Error::Inner(0, err))?;
::core::result::Result::Ok(0) ::core::result::Result::Ok(0)
}); });
}; };
let index = ::miniconf::Key::find::<1, Self>(&key).ok_or(miniconf::Error::NotFound(1))?; let index = ::miniconf::Key::find::<Self>(&key).ok_or(miniconf::Traversal::NotFound(1))?;
if keys.next().is_some() { if !keys.finalize() {
return ::core::result::Result::Err(::miniconf::Error::TooLong(1)); ::core::result::Result::Err(::miniconf::Traversal::TooLong(1))?;
} }
::miniconf::Increment::increment({ ::miniconf::Error::increment_result({
match index { match index {
0 => ::miniconf::Serialize::serialize(&self.0, ser)?, 0 => ::miniconf::Serialize::serialize(&self.0, ser).map_err(|err| ::miniconf::Error::Inner(0, err))?,
#(#matches)* #(#matches)*
_ => unreachable!(), _ => unreachable!(),
}; };
@ -512,27 +513,46 @@ fn generate_tree_deserialize(ident: &Ident, num_keys: usize) -> TokenStream2 {
de: D, de: D,
) -> ::core::result::Result<usize, ::miniconf::Error<D::Error>> ) -> ::core::result::Result<usize, ::miniconf::Error<D::Error>>
where where
K: ::core::iter::Iterator, K: ::miniconf::Keys,
K::Item: ::miniconf::Key,
D: ::serde::Deserializer<'de>, D: ::serde::Deserializer<'de>,
{ {
let ::core::option::Option::Some(key) = keys.next() else { let ::core::result::Result::Ok(key) = keys.next::<Self>() else {
self.0 = ::miniconf::Deserialize::deserialize(de)?; self.0 = ::miniconf::Deserialize::deserialize(de).map_err(|err| ::miniconf::Error::Inner(0, err))?;
return ::core::result::Result::Ok(0); return ::core::result::Result::Ok(0);
}; };
let index = ::miniconf::Key::find::<1, Self>(&key).ok_or(::miniconf::Error::NotFound(1))?; let index = ::miniconf::Key::find::<Self>(&key).ok_or(::miniconf::Traversal::NotFound(1))?;
if keys.next().is_some() { if !keys.finalize() {
return ::core::result::Result::Err(miniconf::Error::TooLong(1)); ::core::result::Result::Err(miniconf::Traversal::TooLong(1))?;
} }
match index { match index {
0 => ::miniconf::Increment::increment({ 0 => ::miniconf::Error::increment_result((||{
self.0 = ::miniconf::Deserialize::deserialize(de)?; self.0 = ::miniconf::Deserialize::deserialize(de).map_err(|err| ::miniconf::Error::Inner(0, err))?;
Ok(0) Ok(0)
}), })()),
1..=#num_keys => ::core::result::Result::Err(::miniconf::Error::Absent(0)), 1..=#num_keys => ::core::result::Result::Err(::miniconf::Traversal::Absent(0))?,
_ => unreachable!(), _ => unreachable!(),
} }
} }
} }
} }
} }
fn generate_tree_any(ident: &Ident) -> TokenStream2 {
quote! {
impl ::miniconf::TreeAny<1> for #ident {
fn ref_any_by_key<K>(&self, keys: K) -> Result<&dyn ::core::any::Any, ::miniconf::Traversal>
where
K: ::miniconf::Keys,
{
unimplemented!();
}
fn mut_any_by_key<K>(&mut self, keys: K) -> Result<&mut dyn ::core::any::Any, ::miniconf::Traversal>
where
K: ::miniconf::Keys,
{
unimplemented!();
}
}
}
}

View File

@ -1,7 +1,12 @@
use std::str::from_utf8; use std::str::from_utf8;
use macroconf::config; use macroconf::config;
use miniconf::{Error::Absent, JsonCoreSlash, Tree, TreeKey}; use miniconf::{
Error::Traversal,
IntoKeys, JsonCoreSlash, Path,
Traversal::{Absent, TooLong},
Tree, TreeKey,
};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[config] #[config]
@ -21,7 +26,7 @@ struct SubConfig {
#[config] #[config]
#[derive(Debug, Clone, Copy, Tree)] #[derive(Debug, Clone, Copy, Tree)]
struct Config { struct Config {
#[tree(depth(2))] #[tree(depth = 2)]
sub_config: SubConfig, sub_config: SubConfig,
} }
@ -32,8 +37,8 @@ fn keys() {
.enumerate() .enumerate()
{ {
assert_eq!( assert_eq!(
SubConfig::traverse_by_key(std::iter::once(field), |index, name| { SubConfig::traverse_by_key([field].into_keys(), |index, name, _len| {
assert_eq!((id, field), (index, name)); assert_eq!((id, Some(field)), (index, name));
Ok::<_, ()>(()) Ok::<_, ()>(())
}), }),
Ok(1) Ok(1)
@ -44,16 +49,20 @@ fn keys() {
#[test] #[test]
fn sub_keys() { fn sub_keys() {
assert_eq!( assert_eq!(
SubConfig::traverse_by_key(["skipped", "value"].into_iter(), |_, _| Ok::<_, ()>(())), SubConfig::traverse_by_key(["skipped", "value"].into_keys(), |_, _, _| Ok::<_, ()>(())),
Err(Traversal(TooLong(1)))
);
assert_eq!(
SubConfig::traverse_by_key(["skipped"].into_keys(), |_, _, _| Ok::<_, ()>(())),
Ok(1) Ok(1)
); );
for field in ["min", "max", "default", "description"] { for field in ["min", "max", "default", "description"] {
assert_eq!( assert_eq!(
SubConfig::traverse_by_key([field, "value"].into_iter(), |_, _| Ok::<_, ()>(())), SubConfig::traverse_by_key([field, "value"].into_keys(), |_, _, _| Ok::<_, ()>(())),
Ok(2) Ok(2)
); );
assert_eq!( assert_eq!(
SubConfig::traverse_by_key([field, field].into_iter(), |_, _| Ok::<_, ()>(())), SubConfig::traverse_by_key([field, field].into_keys(), |_, _, _| Ok::<_, ()>(())),
Ok(2) Ok(2)
); );
} }
@ -123,7 +132,7 @@ fn deserialize() {
"/description/description", "/description/description",
] { ] {
let res = config.set_json(input, b"10"); let res = config.set_json(input, b"10");
assert_eq!(res, Err(Absent(1))); assert_eq!(res, Err(Traversal(Absent(1))));
} }
} }
@ -140,8 +149,8 @@ fn subconfig() {
"/sub_config/description/value".to_owned(), "/sub_config/description/value".to_owned(),
"/sub_config/description/description".to_owned(), "/sub_config/description/description".to_owned(),
]; ];
let paths: Vec<String> = Config::iter_paths::<String>("/") let paths: Vec<String> = Config::nodes::<Path<String, '/'>>()
.filter_map(|path| path.ok()) .filter_map(|path| path.ok().map(|(n, _)| n.0))
.collect(); .collect();
assert_eq!(paths, control); assert_eq!(paths, control);
} }