pub trait TreeKey {
// Required methods
fn traverse_all<W: Walk>() -> Result<W, W::Error>;
fn traverse_by_key<K, F, E>(keys: K, func: F) -> Result<usize, Error<E>>
where K: Keys,
F: FnMut(usize, Option<&'static str>, NonZero<usize>) -> Result<(), E>;
// Provided methods
fn transcode<N, K>(keys: K) -> Result<(N, Node), Traversal>
where K: IntoKeys,
N: Transcode + Default { ... }
fn nodes<N, const D: usize>() -> NodeIter<Self, N, D> ⓘ
where N: Transcode + Default { ... }
}
Expand description
Traversal, iteration of keys in a tree.
See also the sub-traits TreeSerialize
, TreeDeserialize
, TreeAny
.
§Keys
There is a one-to-one relationship between nodes and keys.
The keys used to identify nodes support Keys
/IntoKeys
. They can be
obtained from other IntoKeys
through Transcode
/TreeKey::transcode()
.
An iterator of keys for the nodes is available through TreeKey::nodes()
/NodeIter
.
usize
is modelled after ASN.1 Object Identifiers, seecrate::Indices
.&str
keys are sequences of names, like path names. When concatenated, they are separated by some path hierarchy separator, e.g.'/'
, seecrate::Path
, or by some more complex notation, seecrate::JsonPath
.crate::Packed
is a bit-packed compact compressed notation of hierarchical compound indices.- See the
scpi
example for how to implement case-insensitive, relative, and abbreviated/partial matches.
§Derive macros
Derive macros to automatically implement the correct traits on a struct or enum 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/per-variant attributes to control the derived trait implementations.
§Rename
The key for named struct fields or enum variants may be changed from the default field ident using
the rename
derive macro attribute.
use miniconf::{Leaf, Path, Tree, TreeKey};
#[derive(Tree, Default)]
struct S {
#[tree(rename = "OTHER")]
a: Leaf<f32>,
};
let (name, _node) = S::transcode::<Path<String, '/'>, _>([0usize]).unwrap();
assert_eq!(name.as_str(), "/OTHER");
§Skip
Named fields/variants may be omitted from the derived Tree
trait implementations using the
skip
attribute.
Note that for tuple structs skipping is only supported for terminal fields:
use miniconf::{Leaf, Tree};
#[derive(Tree)]
struct S(Leaf<i32>, #[tree(skip)] ());
use miniconf::{Tree, Leaf};
#[derive(Tree)]
struct S(#[tree(skip)] (), Leaf<i32>);
§Type
The type to use when accessing the field/variant through TreeKey
/TreeDeserialize::probe
can be overridden using the typ
derive macro attribute (#[tree(typ="[f32; 4]")]
).
§Deny
#[tree(deny(operation="message", ...))]
This returns Err(
Traversal::Access
)
for the respective operation
(traverse
, serialize
, deserialize
, probe
, ref_any
, mut_any
) on a
field/variant and suppresses the respective traits bounds on type paramters
of the struct/enum.
§Implementation overrides
#[tree(with(operation=expr, ...))]
This overrides the call to the child node/variant trait for the given operation
(traverse
, traverse_all
, serialize
, deserialize
, probe
, ref_any
, mut_any
).
expr
should be a method on self
(not the field!) or value
(associated function for traverse
, traverse_all
and probe
)
taking the arguments of the respective trait’s method.
#[derive(Tree, Default)]
struct S {
#[tree(with(deserialize=self.check))]
b: Leaf<f32>,
};
impl S {
fn check<'de, K: Keys, D: Deserializer<'de>>(&mut self, keys: K, de: D) -> Result<(), Error<D::Error>> {
let old = *self.b;
self.b.deserialize_by_key(keys, de)?;
if *self.b < 0.0 {
*self.b = old;
Err(Traversal::Access(0, "fail").into())
} else {
Ok(())
}
}
}
§defer
The defer
attribute is a shorthand for with()
that defers
child trait implementations to a given expression.
§Array
Blanket implementations of the Tree*
traits are provided for homogeneous arrays
[T; N]
.
§Option
Blanket implementations of the Tree*
traits are provided for Option<T>
.
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::nodes()
) 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 the same behavior as for other enums
that have the Tree*
traits derived.
§Tuples
Blanket impementations for the Tree*
traits are provided for heterogeneous tuples (T0, T1, ...)
up to length eight.
§Examples
See the crate
documentation for a longer example showing how the traits and the derive
macros work.
Required Methods§
Sourcefn traverse_all<W: Walk>() -> Result<W, W::Error>
fn traverse_all<W: Walk>() -> Result<W, W::Error>
Walk metadata about all paths.
use miniconf::{Leaf, Metadata, TreeKey};
#[derive(TreeKey)]
struct S {
foo: Leaf<u32>,
bar: [Leaf<u16>; 2],
};
let m: Metadata = S::traverse_all().unwrap();
assert_eq!((m.max_depth, m.max_length, m.count.get()), (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.
If a leaf is found early (keys
being longer than required)
Err(Traversal(TooLong(depth)))
is returned.
If keys
is exhausted before reaching a leaf node,
Err(Traversal(TooShort(depth)))
is returned.
Traversal::Access/Invalid/Absent/Finalization
are never returned.
This method should fail if and only if the key is invalid.
It should succeed at least when any of the other key based methods
in TreeAny
, TreeSerialize
, and TreeDeserialize
succeed.
use miniconf::{IntoKeys, Leaf, TreeKey};
#[derive(TreeKey)]
struct S {
foo: Leaf<u32>,
bar: [Leaf<u16>; 2],
};
let mut ret = [(1, Some("bar"), 2), (0, None, 2)].into_iter();
let func = |index, name, len: core::num::NonZero<usize>| -> Result<(), ()> {
assert_eq!(ret.next().unwrap(), (index, name, len.get()));
Ok(())
};
assert_eq!(S::traverse_by_key(["bar", "0"].into_keys(), func), Ok(2));
§Args
keys
: AnIterator
ofKey
s identifying the node.func
: AFnMut
to 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(E)
aborts the traversal. ReturningOk(())
continues the downward traversal.
§Returns
Node depth on success (number of keys consumed/number of calls to func
)
§Design note
Writing this to return an iterator instead of using a callback would have worse performance (O(n^2) instead of O(n) for matching)
Provided Methods§
Sourcefn transcode<N, K>(keys: K) -> Result<(N, Node), Traversal>
fn transcode<N, K>(keys: K) -> Result<(N, Node), Traversal>
Transcode keys to a new keys type representation
The keys can be
- too short: the internal node is returned
- matched length: the leaf node is returned
- too long: Err(TooLong(depth)) is returned
In order to not require N: Default
, use Transcode::transcode
on
an existing &mut N
.
use miniconf::{Indices, JsonPath, Leaf, Node, Packed, Path, TreeKey};
#[derive(TreeKey)]
struct S {
foo: Leaf<u32>,
bar: [Leaf<u16>; 5],
};
let idx = [1, 1];
let (path, node) = S::transcode::<Path<String, '/'>, _>(idx).unwrap();
assert_eq!(path.as_str(), "/bar/1");
let (path, node) = S::transcode::<JsonPath<String>, _>(idx).unwrap();
assert_eq!(path.as_str(), ".bar[1]");
let (indices, node) = S::transcode::<Indices<[_; 2]>, _>(&path).unwrap();
assert_eq!(&indices[..node.depth()], idx);
let (indices, node) = S::transcode::<Indices<[_; 2]>, _>(["bar", "1"]).unwrap();
assert_eq!(&indices[..node.depth()], [1, 1]);
let (packed, node) = S::transcode::<Packed, _>(["bar", "4"]).unwrap();
assert_eq!(packed.into_lsb().get(), 0b1_1_100);
let (path, node) = S::transcode::<Path<String, '/'>, _>(packed).unwrap();
assert_eq!(path.as_str(), "/bar/4");
let ((), node) = S::transcode(&path).unwrap();
assert_eq!(node, Node::leaf(2));
§Args
keys
:IntoKeys
to identify the node.
§Returns
Transcoded target and node information on success
Sourcefn nodes<N, const D: usize>() -> NodeIter<Self, N, D> ⓘ
fn nodes<N, const D: usize>() -> NodeIter<Self, N, D> ⓘ
Return an iterator over nodes of a given type
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 NodeIter::exact_size()
.
The D
const generic of NodeIter
is the maximum key depth.
use miniconf::{Indices, JsonPath, Leaf, Node, Packed, Path, TreeKey};
#[derive(TreeKey)]
struct S {
foo: Leaf<u32>,
bar: [Leaf<u16>; 2],
};
let paths: Vec<_> = S::nodes::<Path<String, '/'>, 2>()
.exact_size()
.map(|p| p.unwrap().0.into_inner())
.collect();
assert_eq!(paths, ["/foo", "/bar/0", "/bar/1"]);
let paths: Vec<_> = S::nodes::<JsonPath<String>, 2>()
.exact_size()
.map(|p| p.unwrap().0.into_inner())
.collect();
assert_eq!(paths, [".foo", ".bar[0]", ".bar[1]"]);
let indices: Vec<_> = S::nodes::<Indices<[_; 2]>, 2>()
.exact_size()
.map(|p| {
let (idx, node) = p.unwrap();
(idx.into_inner(), node.depth)
})
.collect();
assert_eq!(indices, [([0, 0], 1), ([1, 0], 2), ([1, 1], 2)]);
let packed: Vec<_> = S::nodes::<Packed, 2>()
.exact_size()
.map(|p| p.unwrap().0.into_lsb().get())
.collect();
assert_eq!(packed, [0b1_0, 0b1_1_0, 0b1_1_1]);
let nodes: Vec<_> = S::nodes::<(), 2>()
.exact_size()
.map(|p| p.unwrap().1)
.collect();
assert_eq!(nodes, [Node::leaf(1), Node::leaf(2), Node::leaf(2)]);
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.