pub trait TreeKey<const Y: usize = 1> {
// Required methods
fn metadata() -> Metadata;
fn traverse_by_key<K, F, E>(keys: K, func: F) -> Result<usize, Error<E>>
where K: Keys,
F: FnMut(usize, Option<&'static str>, usize) -> Result<(), E>;
// Provided methods
fn path<K, P>(
keys: K,
path: P,
separator: &str
) -> Result<usize, Error<Error>>
where K: IntoKeys,
P: Write { ... }
fn json_path<K, P>(keys: K, path: P) -> Result<usize, Error<Error>>
where K: IntoKeys,
P: Write { ... }
fn indices<K>(keys: K) -> Result<([usize; Y], usize), Traversal>
where K: IntoKeys { ... }
fn packed<K>(keys: K) -> Result<(Packed, usize), Error<()>>
where K: IntoKeys { ... }
fn iter_paths<P: Write + Default>(
separator: &str
) -> PathIter<'_, Self, Y, P, Y> ⓘ { ... }
fn iter_indices() -> IndexIter<Self, Y, Y> ⓘ { ... }
fn iter_packed() -> PackedIter<Self, Y, Y> ⓘ { ... }
}Expand description
Traversal, iteration of keys in a tree.
See also the sub-traits TreeSerialize<Y>, TreeDeserialize<Y>, TreeAny<Y>.
§Recursion depth
The Tree* traits are meant to be implemented
recursively on nested data structures. Recursion here means that a container
that implements Tree*, may call on the Tree* implementations of
inner types or Serialize/Deserialize/Any of leaf types.
The const parameter Y in the traits is the recursion depth and determines the
maximum nesting of Tree* layers. It’s at least 1 and defaults to 1.
The recursion depth Y doubles as an upper bound to the maximum key length
(the depth/height of the tree):
An implementor of TreeKey<Y> may consume at most Y items from the
keys argument. This includes
both the items consumed directly before recursing/terminating and those consumed
indirectly by recursing into inner types. In the same way it may call func in
TreeKey::traverse_by_key() at most Y times, again including those calls due
to recursion into inner TreeKey types.
This implies that if an implementor T of TreeKey<Y> (with Y >= 1) contains and recurses into
an inner type using that type’s TreeKey<Z> implementation, then 1 <= Z <= Y must
hold and T may consume at most Y - Z items from the keys iterators and call
func at most Y - Z times. It is recommended (but not necessary) to keep Z = Y - 1
(even if no keys are consumed directly) to satisfy the bound
heuristics in the derive macro.
The exact maximum key depth can be obtained through TreeKey::metadata().
§Keys
The keys used to identify nodes can be iterators over usize indices or &str names or can
be Packed compound indices.
usizeis modelled after ASN.1 Object Identifiers.&strkeys are sequences of names, like path names. When concatenated, they are separated by some path hierarchy separator, e.g.'/'.Packedis a variable bit-width compact compressed notation of hierarchical indices.
There is a one-to-one relationship between nodes and keys.
§Derive macros
Derive macros to automatically implement the correct traits on a struct are available through
crate::TreeKey, crate::TreeSerialize, crate::TreeDeserialize,
and crate::TreeAny.
A shorthand derive macro that derives all four trait implementations is also available at crate::Tree.
The derive macros support per-field attribute to control the derived trait implementations.
§Depth
For each field, the recursion depth is configured through the #[tree(depth=Y)]
attribute, with Y = 0 being the implied default.
If Y = 0, the field is a leaf and accessed only through its
Serialize/Deserialize/Any implementation.
With Y > 0 the field is accessed through its TreeKey<Y> implementation with the given
remaining recursion depth.
§Rename
The key for named struct fields may be changed from the default field ident using the rename
derive macro attribute (#[tree(rename="otherName")]).
§Skip
Named fields may be omitted from the derived Tree trait implementations using the skip attribute
(#[tree(skip)]).
Note that for tuple structs skipping is only supported for terminal fields:
use miniconf::Tree;
#[derive(Tree)]
struct S(#[tree(skip)] (), i32);use miniconf::Tree;
#[derive(Tree)]
struct S(i32, #[tree(skip)] ());§Type
The type to use when accessing the field through TreeKey can be overridden using the typ
derive macro attribute (#[tree(typ="[f32; 4]")]).
§Accessors
The get, get_mut, validate callbacks can be used to implement accessors,
validation or support remote types (e.g. #[tree(get_mut=func)])
§get
The getter is called during serialize_by_key() before leaf serialization and
during ref_any_by_key(). Its signature is fn(&self) -> Result<&T, &'static str>.
The default getter is Ok(&self.field).
Getters can be used for both leaf fields as well as internal (non-leaf) fields.
If a getter returns an error message Err(&str) the serialization/traversal
is not performed, further getters at greater depth are not invoked
and Traversal::Access is returned.
§get_mut
For internal (non-leaf) fields get_mut is invoked during mut_any_by_key() and
during deserialize_by_key() before deserialization while traversing down to
the leaf node.
For leaf fields it is invoked after deserialization and validation but before
updating the leaf value.
The signature is fn(&mut self) -> Result<&mut T, &str>.
The default get_mut is Ok(&mut self.field).
If get_mut returns an Err Traversal::Access will be returned.
If a leaf get_mut returns an Err the leaf node is not updated in
deserialize_by_key().
Note: In both cases get_mut receives &mut self as an argument and may
mutate the struct.
use miniconf::{Error, Tree, JsonCoreSlash};
#[derive(Tree, Default)]
struct S {
#[tree(validate=leaf)]
a: f32,
#[tree(depth=1, validate=non_leaf)]
b: [f32; 2],
};
fn leaf(s: &mut S, new: f32) -> Result<f32, &'static str> {
Err("fail")
}
fn non_leaf(s: &mut S, depth: usize) -> Result<usize, &'static str> {
Err("fail")
}§validate
For leaf fields the validate callback is called during deserialize_by_key()
after successful deserialization of the leaf value but before get_mut() and
before storing the value.
The leaf validate signature is fn(&mut self, value: T) -> Result<T, &'static str>. It may mutate the value before it is being stored.
If a leaf validate callback returns Err(&str), the leaf value is not updated
and Traversal::Invalid is returned from deserialize_by_key().
For internal fields validate is called after the successful update of the leaf field
during upward traversal.
The internal validate signature is fn(&mut self, depth: usize) -> Result<usize, &'static str>
If an internal validate callback returns Err(), the leaf value has been
updated and Traversal::Invalid is returned from deserialize_by_key().
Note: In both cases validate receives &mut self as an argument and may
mutate the struct.
§Bounds
To derive TreeSerialize/TreeDeserialize/TreeAny, each field (that is not skipped)
in the struct must either implement Serialize/Deserialize/Any
or implement the respective TreeSerialize/TreeDeserialize/TreeAny trait
for the required remaining recursion depth.
§Generics
The macros add bounds to generic types of the struct they are acting on.
If a generic type parameter T of the struct S<T>is used as a type parameter to a
field type a: F1<F2<T>> the type T will be considered to reside at type depth X = 2 (as it is
within F2 which is within F1) and the following bounds will be applied:
- With the
#[tree()]attribute not present ona,Twill receive boundsSerialize/DeserializewhenTreeSerialize/TreeDeserializeis derived. - With
#[tree(depth=Y)], andY - X < 1it will receive the boundsSerialize/Deserialize. - For
Y - X >= 1it will receive the boundT: TreeKey<Y - X>.
E.g. In the following T resides at depth 2 and T: TreeKey<1> will be inferred:
use miniconf::TreeKey;
#[derive(TreeKey)]
struct S<T> {
#[tree(depth=3)]
a: [Option<T>; 2],
};
// This works as [u32; N] implements TreeKey<1>:
S::<[u32; 5]>::metadata();
// This does not compile as u32 does not implement TreeKey<1>:
// S::<u32>::metadata();This behavior is upheld by and compatible with all implementations in this crate. It is only violated
when deriving TreeKey for a struct that (a) forwards its own type parameters as type
parameters to its field types, (b) uses TreeKey on those fields, and (c) those field
types use their type parameters at other depths than TreeKey<Y - 1>. See also the
test_derive_macro_bound_failure test in tests/generics.rs.
§Array
Blanket implementations of the TreeKey traits are provided for homogeneous arrays [T; N]
up to recursion depth Y = 16.
When a [T; N] is used as TreeKey<Y> (i.e. marked as #[tree(depth=Y)] in a struct)
and Y > 1 each item of the array is accessed as a TreeKey tree.
For Y = 1 each index of the array is is instead accessed as
an atomic value.
For a depth Y = 0 (attribute absent), the entire array is accessed as one atomic
value.
The depth to use on the array depends on the desired semantics of the data contained
in the array. If the array contains TreeKey items, you likely want use Y >= 2.
However, if each element in the array should be individually configurable as a single value (e.g. a list
of u32), then Y = 1 can be used. With Y = 0 all items are to be accessed simultaneously and atomically.
For e.g. [[T; 2]; 3] where T: TreeKey<3> the recursion depth is Y = 3 + 1 + 1 = 5.
It automatically implements TreeKey<5>.
For [[T; 2]; 3] with T: Serialize/T: Deserialize/T: Any any Y <= 2 trait is implemented.
§Option
Blanket implementations of the TreeKey traits are provided for Option<T>
up to recursion depth Y = 16.
These implementations do not alter the path hierarchy and do not consume any items from the keys
iterators. The TreeKey behavior of an Option is such that the None variant makes the corresponding part
of the tree inaccessible at run-time. It will still be iterated over (e.g. by TreeKey::iter_paths()) but attempts
to access it (e.g. TreeSerialize::serialize_by_key(), TreeDeserialize::deserialize_by_key(),
TreeAny::ref_any_by_key(), or TreeAny::mut_any_by_key())
return the special Traversal::Absent.
This is intended as a mechanism to provide run-time construction of the namespace. In some
cases, run-time detection may indicate that some component is not present. In this case,
the nodes will not be exposed for serialization/deserialization.
If the depth specified by the #[tree(depth=Y)] attribute exceeds 1,
the Option can be used to access within the inner type using its TreeKey trait.
If there is no tree attribute on an Option field in a struct or in an array, JSON nullcorresponds toNoneas usual and theTreeKey` trait is not used.
§Examples
See the crate documentation for a longer example showing how the traits and the derive macros work.
Required Methods§
sourcefn metadata() -> Metadata
fn metadata() -> Metadata
Compute metadata about all paths.
use miniconf::TreeKey;
#[derive(TreeKey)]
struct S {
foo: u32,
#[tree(depth=1)]
bar: [u16; 2],
};
let m = S::metadata();
assert_eq!((m.max_depth, m.max_length, m.count), (2, 4, 3));sourcefn traverse_by_key<K, F, E>(keys: K, func: F) -> Result<usize, Error<E>>
fn traverse_by_key<K, F, E>(keys: K, func: F) -> Result<usize, Error<E>>
Traverse from the root to a leaf and call a function for each node.
Traversal is aborted once func returns an Err(E).
This may not exhaust keys if a leaf is found early. i.e. keys
may be longer than required: Error::TooLong is not returned.
If Self is a leaf, nothing will be consumed from keys
and Ok(0) will be returned.
If Self is non-leaf (internal node) and Keys is exhausted (empty),
Err(Error::TooShort(0)) will be returned.
use miniconf::TreeKey;
#[derive(TreeKey)]
struct S {
foo: u32,
#[tree(depth=1)]
bar: [u16; 2],
};
let mut ret = [(1, Some("bar"), 2), (0, None, 2)].into_iter();
let func = |index, name, len| -> Result<(), ()> {
assert_eq!(ret.next().unwrap(), (index, name, len));
Ok(())
};
assert_eq!(S::traverse_by_key(["bar", "0"].into_iter(), func), Ok(2));§Args
keys: AnIteratorofKeys identifying the node.func: AFnMutto be called for each (internal and leaf) node on the path. Its arguments are the index and the optional name of the node and the number of top-level nodes at the given depth. ReturningErr()aborts the traversal.
§Returns
Final node depth on success (the number of keys consumed, number of calls to func)
Provided Methods§
sourcefn path<K, P>(keys: K, path: P, separator: &str) -> Result<usize, Error<Error>>
fn path<K, P>(keys: K, path: P, separator: &str) -> Result<usize, Error<Error>>
Convert keys to path.
keys may be longer than required. Extra items are ignored. Error::TooLong
is not returned.
use miniconf::TreeKey;
#[derive(TreeKey)]
struct S {
foo: u32,
#[tree(depth=1)]
bar: [u16; 2],
};
let mut s = String::new();
S::path([1, 1], &mut s, "/").unwrap();
assert_eq!(s, "/bar/1");§Args
keys:IntoKeysto identify the node.path: AWriteto write the separators and node names into. See also TreeKey::metadata() andMetadata::max_length()for upper bounds on path length.separator: The path hierarchy separator to be inserted before each name.
§Returns
Final node depth on success
sourcefn json_path<K, P>(keys: K, path: P) -> Result<usize, Error<Error>>
fn json_path<K, P>(keys: K, path: P) -> Result<usize, Error<Error>>
Return the keys formatted as a normalized JSON path.
- Named fields (struct) are encoded in dot notation.
- Indices (tuple struct, array) are encoded in index notation
See also TreeKey::path().
use miniconf::{TreeKey, JsonPath};
#[derive(TreeKey)]
struct S {
foo: u32,
#[tree(depth=1)]
bar: [u16; 2],
};
let mut s = String::new();
let idx = [1, 1];
S::json_path(idx, &mut s).unwrap();
assert_eq!(s, ".bar[1]");
let (indices, depth) = S::indices(JsonPath::from(&s)).unwrap();
assert_eq!(&indices[..depth], idx);sourcefn indices<K>(keys: K) -> Result<([usize; Y], usize), Traversal>where
K: IntoKeys,
fn indices<K>(keys: K) -> Result<([usize; Y], usize), Traversal>where
K: IntoKeys,
Convert keys to indices.
See also TreeKey::path().
use miniconf::TreeKey;
#[derive(TreeKey)]
struct S {
foo: u32,
#[tree(depth=1)]
bar: [u16; 2],
};
let (indices, depth) = S::indices(["bar", "1"]).unwrap();
assert_eq!(&indices[..depth], [1, 1]);§Returns
Indices and depth on success
sourcefn packed<K>(keys: K) -> Result<(Packed, usize), Error<()>>where
K: IntoKeys,
fn packed<K>(keys: K) -> Result<(Packed, usize), Error<()>>where
K: IntoKeys,
Convert keys to packed usize bitfield representation.
See also Packed and TreeKey::path().
use miniconf::TreeKey;
#[derive(TreeKey)]
struct S {
foo: u32,
#[tree(depth=1)]
bar: [u16; 5],
};
let (p, _) = S::packed(["bar", "4"]).unwrap();
assert_eq!(p.into_lsb().get(), 0b1_1_100);
let mut s = String::new();
S::path(p, &mut s, "/").unwrap();
assert_eq!(s, "/bar/4");§Returns
The packed indices representation and leaf depth on success.
sourcefn iter_paths<P: Write + Default>(
separator: &str
) -> PathIter<'_, Self, Y, P, Y> ⓘ
fn iter_paths<P: Write + Default>( separator: &str ) -> PathIter<'_, Self, Y, P, Y> ⓘ
Create an iterator of all possible leaf paths.
This is a walk of all leaf nodes.
The iterator will walk all paths, including those that may be absent at
runtime (see TreeKey).
An iterator with an exact and trusted size_hint() can be obtained from
this through PathIter::count().
use miniconf::TreeKey;
#[derive(TreeKey)]
struct S {
foo: u32,
#[tree(depth=1)]
bar: [u16; 2],
};
let paths: Vec<String> = S::iter_paths("/").count().map(|p| p.unwrap()).collect();
assert_eq!(paths, ["/foo", "/bar/0", "/bar/1"]);§Generics
P- The type to hold the path.
§Args
separator- The path hierarchy separator
sourcefn iter_indices() -> IndexIter<Self, Y, Y> ⓘ
fn iter_indices() -> IndexIter<Self, Y, Y> ⓘ
Create an iterator of all possible leaf indices.
See also TreeKey::iter_paths().
use miniconf::TreeKey;
#[derive(TreeKey)]
struct S {
foo: u32,
#[tree(depth=1)]
bar: [u16; 2],
};
let indices: Vec<_> = S::iter_indices().count().collect();
assert_eq!(indices, [([0, 0], 1), ([1, 0], 2), ([1, 1], 2)]);sourcefn iter_packed() -> PackedIter<Self, Y, Y> ⓘ
fn iter_packed() -> PackedIter<Self, Y, Y> ⓘ
Create an iterator of all packed leaf indices.
See also TreeKey::iter_paths().
use miniconf::TreeKey;
#[derive(TreeKey)]
struct S {
foo: u32,
#[tree(depth=1)]
bar: [u16; 2],
};
let packed: Vec<_> = S::iter_packed()
.count()
.map(|p| p.unwrap().into_lsb().get())
.collect();
assert_eq!(packed, [0b1_0, 0b1_1_0, 0b1_1_1]);