dir_structure/traits/resolve.rs
1//! Traits for resolving paths in a directory structure.
2//!
3//! [`HasField`] is automatically derived by the [`#[derive(HasField)]` macro](crate::HasField).
4
5#[cfg(feature = "resolve-path")]
6use crate::traits::vfs::OwnedPathType;
7
8#[doc(hidden)]
9pub const HAS_FIELD_MAX_LEN: usize = dir_structure_resolve_core::MAX_LEN;
10
11/// A trait to declare that a type has a field with a specific name,
12/// and the type of the field is [`HasField::Inner`].
13///
14/// This is used to resolve paths with [`resolve_path`].
15#[cfg(feature = "resolve-path")]
16#[cfg_attr(docsrs, doc(cfg(feature = "resolve-path")))]
17pub trait HasField<const NAME: [char; HAS_FIELD_MAX_LEN]> {
18 /// The type of the field.
19 type Inner;
20
21 /// How to resolve the path for the field, from the path of `Self`.
22 fn resolve_path<P: OwnedPathType>(p: P) -> P;
23}
24
25/// A trait for types that may or may not have a newtype wrapper around their field type for reading / writing.
26pub trait HasFieldMaybeNewtype<const NAME: [char; HAS_FIELD_MAX_LEN]>: HasField<NAME> {
27 /// The reader type, which may be a newtype wrapper around [`HasField::Inner`], or the [`HasField::Inner`] itself if no newtype is
28 /// supplied to the `#[derive(DirStructure)]` macro.
29 type ReaderType;
30 /// Parses the read type into the inner type.
31 fn parse(read: Self::ReaderType) -> Self::Inner;
32}
33
34/// A trait to declare that a type has a field with a specific name, and it's not wrapped in a newtype.
35///
36/// This allows it to be used within the `first..last-1` segments of the `load_path!` macro, where newtypes are not supported.
37pub trait HasFieldNoNewtype<const NAME: [char; HAS_FIELD_MAX_LEN]>: HasField<NAME> {}
38
39impl<const NAME: [char; HAS_FIELD_MAX_LEN], S, T> HasFieldMaybeNewtype<NAME> for S
40where
41 S: HasFieldNoNewtype<NAME, Inner = T>,
42{
43 type ReaderType = T;
44
45 fn parse(read: Self::ReaderType) -> Self::Inner {
46 read
47 }
48}
49
50/// A trait to declare that a type has fields with dynamic names,
51/// such as [`DirChildren`](crate::dir_children::DirChildren), [`DirDescendants`](crate::dir_descendants::DirDescendants), etc.
52///
53/// This is used to resolve paths with [`resolve_path`], particularly with the `"name"` and
54/// `${expr}` syntaxes.
55#[cfg(feature = "resolve-path")]
56#[cfg_attr(docsrs, doc(cfg(feature = "resolve-path")))]
57pub trait DynamicHasField {
58 /// The type of the field.
59 type Inner;
60 /// How to resolve the path for the field, from the path of `Self`, given the name
61 /// passed into [the `resolve_path!` macro](resolve_path).
62 fn resolve_path<P: OwnedPathType>(p: P, name: &str) -> P;
63}
64
65/// [`DynamicHasField`] for types that do not have a newtype wrapper around their field type.
66pub trait DynamicHasFieldNoNewtype: DynamicHasField {}
67
68/// [`DynamicHasField`] for types that may or may not have a newtype wrapper around their field type.
69pub trait DynamicHasFieldMaybeNewtype: DynamicHasField {
70 /// The reader type, which may be a newtype wrapper around [`DynamicHasField::Inner`], or the [`DynamicHasField::Inner`] itself if no newtype is
71 /// supplied to the `#[derive(DirStructure)]` macro.
72 type ReaderType;
73 /// Parses the read type into the inner type.
74 fn parse(read: Self::ReaderType) -> Self::Inner;
75}
76
77impl<S, T> DynamicHasFieldMaybeNewtype for S
78where
79 S: DynamicHasFieldNoNewtype<Inner = T>,
80{
81 type ReaderType = T;
82
83 fn parse(read: Self::ReaderType) -> Self::Inner {
84 read
85 }
86}
87
88#[cfg(all(feature = "derive", feature = "resolve-path"))]
89#[cfg_attr(docsrs, doc(cfg(all(feature = "derive", feature = "resolve-path"))))]
90pub use dir_structure_macros::load_path;
91/// A macro to resolve a path to a specific field in a directory structure.
92///
93/// # Examples
94///
95/// ```rust
96/// use std::path::PathBuf;
97/// use dir_structure::{DirStructure, HasField, traits::resolve::resolve_path};
98///
99/// #[derive(DirStructure, HasField)]
100/// struct MyStruct {
101/// #[dir_structure(path = "my_field.txt")]
102/// my_field: String,
103/// #[dir_structure(path = "my_field2.d")]
104/// my_field2: MyStruct2,
105/// }
106///
107/// #[derive(DirStructure, HasField)]
108/// struct MyStruct2 {
109/// #[dir_structure(path = "my_field3.txt")]
110/// my_field3: String,
111/// }
112///
113/// assert_eq!(
114/// resolve_path!([MyStruct @ "/path/to/dir"].my_field),
115/// PathBuf::from("/path/to/dir/my_field.txt")
116/// );
117/// assert_eq!(
118/// resolve_path!(["/path/to/dir" as MyStruct].my_field2.my_field3),
119/// PathBuf::from("/path/to/dir/my_field2.d/my_field3.txt")
120/// );
121/// ```
122#[cfg(all(feature = "derive", feature = "resolve-path"))]
123#[cfg_attr(docsrs, doc(cfg(all(feature = "derive", feature = "resolve-path"))))]
124pub use dir_structure_macros::resolve_path;