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}