keypath/lib.rs
1//! Derivable references to arbitarily nested fields.
2//!
3//! This crate contains a basic implementation of 'keypaths', a mechanism
4//! for creating paths of references to the properties of objects that can
5//! be used to get and set their underlying values.
6//!
7//! The general idea and design is strongly influenced by keypaths in Swift.
8//!
9//! To work with keypaths, types must implement the [`Keyable`][] trait; in most
10//! cases this will be derived.
11//!
12//! [`KeyPath`][] instances can then be created with the [`keypath!`][] macro.
13//!
14//! # Examples
15//!
16//! ```
17//! use keypath::{Keyable, KeyPath, keypath};
18//!
19//! #[derive(Keyable)]
20//! struct Person {
21//! name: String,
22//! friends: Vec<String>,
23//! size: Size,
24//! }
25//!
26//! #[derive(Keyable)]
27//! struct Size {
28//! big: bool,
29//! heft: u8,
30//! }
31//!
32//! let mut person = Person {
33//! name: "coco".into(),
34//! friends: vec!["eli".into(), "nico".into(), "yaya".into()],
35//! size: Size { big: false, heft: 45 }
36//! };
37//!
38//! let first_friend: KeyPath<Person, String> = keypath!(Person.friends[0]);
39//! let heft = keypath!(Person.size.heft);
40//!
41//! assert_eq!(person[&first_friend], "eli");
42//!
43//! // mutation:
44//! person[&heft] = 101;
45//! assert_eq!(person.size.heft, 101);
46//!
47//! ```
48
49mod error;
50mod impls;
51pub mod internals;
52
53pub use error::{FieldError, FieldErrorKind};
54pub use keypath_proc_macros::{keypath, Keyable};
55
56use std::any::Any;
57use std::borrow::Cow;
58use std::marker::PhantomData;
59
60/// A non-fallible keypath.
61pub struct KeyPath<Root: ?Sized, Value: 'static> {
62 partial: PartialKeyPath<Root>,
63 _value: PhantomData<Value>,
64}
65
66// we don't really use this yet
67#[doc(hidden)]
68/// A keypath for a known route, but which doesn't know the destination type.
69#[derive(Debug)]
70pub struct PartialKeyPath<Root: ?Sized> {
71 fields: Cow<'static, [internals::PathComponent]>,
72 _root: PhantomData<Root>,
73}
74
75impl<Root, Value> KeyPath<Root, Value> {
76 /// Create a new typed `KeyPath` from the provided fields.
77 ///
78 /// This method does not ensure the path is valid; it is intended
79 /// to be called after a path has been type-checked, presumably in the
80 /// context of a proc_macro.
81 #[doc(hidden)]
82 pub const fn __conjure_from_abyss(fields: &'static [internals::PathComponent]) -> Self {
83 KeyPath {
84 partial: PartialKeyPath {
85 fields: Cow::Borrowed(fields),
86 _root: PhantomData,
87 },
88 _value: PhantomData,
89 }
90 }
91
92 /// Create a new `KeyPath` by combining two routes.
93 ///
94 /// The final type of the first route must be the first type of the second
95 /// route; assuming both paths were created with the [`keypath!`] macro,
96 /// the resulting path must be valid.
97 ///
98 /// # Examples
99 ///
100 /// ```
101 /// use keypath::{Keyable, KeyPath, keypath};
102 ///
103 /// #[derive(Keyable)]
104 /// struct Person {
105 /// name: String,
106 /// friends: Vec<String>,
107 /// size: Size,
108 /// }
109 ///
110 /// #[derive(Keyable)]
111 /// struct Size {
112 /// big: bool,
113 /// heft: u8,
114 /// }
115 ///
116 /// let mut person = Person {
117 /// name: "coco".into(),
118 /// friends: vec!["eli".into(), "nico".into(), "yaya".into()],
119 /// size: Size { big: false, heft: 45 }
120 /// };
121 ///
122 /// let size = keypath!(Person.size);
123 /// let heft = keypath!(Size.heft);
124 /// let combined = size.append(&heft);
125 ///
126 /// assert_eq!(person[&combined], 45);
127 /// ```
128 pub fn append<T>(&self, other: &KeyPath<Value, T>) -> KeyPath<Root, T> {
129 let mut partial = self.partial.clone();
130 partial
131 .fields
132 .to_mut()
133 .extend(other.partial.fields.iter().clone());
134 KeyPath {
135 partial,
136 _value: other._value,
137 }
138 }
139}
140
141/// A trait for types that can be indexed with keypaths.
142pub trait Keyable: internals::RawKeyable {
143 /// A type that describes properties on the inner type, for compile-time checking.
144 ///
145 /// This is the worst part of the code right now? We generate structs with magic
146 /// names for each Keyable type.
147 type Mirror;
148
149 /// Return an instance of this type's mirror.
150 fn mirror() -> Self::Mirror;
151
152 //TODO: this is a bit of a mess, and I don't know what methods we will want
153 //or need. Having partial keypaths or keypaths that are failable seems reasonable,
154 //but I don't know what the types are going to look like yet.
155 /// Attempt to traverse a series of `PathComponent`s, returning an `&dyn Any`
156 /// if successful.
157 fn try_any_at_path(
158 &self,
159 path: impl AsRef<[internals::PathComponent]>,
160 ) -> Result<&dyn Any, FieldError> {
161 self.get_field(path.as_ref())
162 .map(internals::RawKeyable::as_any)
163 }
164
165 /// Attempt to traverse a series of `PathComponent`s, returning an `&mut dyn Any`
166 /// if successful.
167 fn try_any_at_path_mut(
168 &mut self,
169 path: impl AsRef<[internals::PathComponent]>,
170 ) -> Result<&mut dyn Any, FieldError> {
171 self.get_field_mut(path.as_ref())
172 .map(internals::RawKeyable::as_any_mut)
173 }
174
175 //NOTE: these two methods are intended in cases where the keypath has not been
176 //validated, but currently we don't really support creating invalid keypaths.
177 #[doc(hidden)]
178 fn try_item_at_path<T>(&self, path: &KeyPath<Self, T>) -> Result<&T, FieldError> {
179 self.try_any_at_path(path)
180 ////FIXME: no unwrap here, some new more expresesive error type instead
181 .map(|t| t.downcast_ref().unwrap())
182 }
183
184 #[doc(hidden)]
185 fn try_item_at_path_mut<T>(&mut self, path: &KeyPath<Self, T>) -> Result<&mut T, FieldError> {
186 self.try_any_at_path_mut(path)
187 //FIXME: no unwrap here, some new more expresesive error type instead
188 .map(|t| t.downcast_mut().unwrap())
189 }
190
191 /// Get a reference to the value at the provided path.
192 ///
193 /// You generally won't need to call this, since you can use `[index]`
194 /// syntax instead.
195 ///
196 /// Assuming the path was constructed with the [`keypath!`] macro, this
197 /// method will not fail.
198 ///
199 ///
200 /// # Panics
201 ///
202 /// I lied, it will fail if you provide an index into a collection and
203 /// that item does not exist.
204 fn item_at_path<T>(&self, path: &KeyPath<Self, T>) -> &T {
205 self.try_item_at_path(path).unwrap()
206 }
207
208 /// Get a mutable reference to the value at the provided path.
209 ///
210 /// You generally won't need to call this, since you can use `[index]`
211 /// syntax instead.
212 ///
213 /// Assuming the path was constructed with the [`keypath!`] macro, this
214 /// method will not fail.
215 ///
216 ///
217 /// # Panics
218 ///
219 /// I lied, it will fail if you provide an index into a collection and
220 /// that item does not exist.
221 fn item_at_path_mut<T>(&mut self, path: &KeyPath<Self, T>) -> &mut T {
222 self.try_item_at_path_mut(path).unwrap()
223 }
224}
225
226impl<Root: ?Sized, Value: 'static> AsRef<[internals::PathComponent]> for KeyPath<Root, Value> {
227 fn as_ref(&self) -> &[internals::PathComponent] {
228 self.partial.fields.as_ref()
229 }
230}
231
232impl<R: ?Sized> Clone for PartialKeyPath<R> {
233 fn clone(&self) -> Self {
234 PartialKeyPath {
235 fields: self.fields.clone(),
236 _root: PhantomData,
237 }
238 }
239}