1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
//! Derivable references to arbitarily nested fields.
//!
//! This crate contains a basic implementation of 'keypaths', a mechanism
//! for creating paths of references to the properties of objects that can
//! be used to get and set their underlying values.
//!
//! The general idea and design is strongly influenced by keypaths in Swift.
//!
//! To work with keypaths, types must implement the [`Keyable`][] trait; in most
//! cases this will be derived.
//!
//! [`KeyPath`][] instances can then be created with the [`keypath!`][] macro.
//!
//! # Examples
//!
//! ```
//! use keypath::{Keyable, KeyPath, keypath};
//!
//! #[derive(Keyable)]
//! struct Person {
//!     name: String,
//!     friends: Vec<String>,
//!     size: Size,
//! }
//!
//! #[derive(Keyable)]
//! struct Size {
//!     big: bool,
//!     heft: u8,
//! }
//!
//! let mut person = Person {
//!     name: "coco".into(),
//!     friends: vec!["eli".into(), "nico".into(), "yaya".into()],
//!     size: Size { big: false, heft: 45 }
//! };
//!
//! let first_friend: KeyPath<Person, String> = keypath!(Person.friends[0]);
//! let heft = keypath!(Person.size.heft);
//!
//! assert_eq!(person[&first_friend], "eli");
//!
//! // mutation:
//! person[&heft] = 101;
//! assert_eq!(person.size.heft, 101);
//!
//! ```

mod error;
mod impls;
pub mod internals;

pub use error::{FieldError, FieldErrorKind};
pub use keypath_proc_macros::{keypath, Keyable};

use std::any::Any;
use std::borrow::Cow;
use std::marker::PhantomData;

/// A non-fallible keypath.
pub struct KeyPath<Root: ?Sized, Value: 'static> {
    partial: PartialKeyPath<Root>,
    _value: PhantomData<Value>,
}

// we don't really use this yet
#[doc(hidden)]
/// A keypath for a known route, but which doesn't know the destination type.
#[derive(Debug)]
pub struct PartialKeyPath<Root: ?Sized> {
    fields: Cow<'static, [internals::PathComponent]>,
    _root: PhantomData<Root>,
}

impl<Root, Value> KeyPath<Root, Value> {
    /// Create a new typed `KeyPath` from the provided fields.
    ///
    /// This method does not ensure the path is valid; it is intended
    /// to be called after a path has been type-checked, presumably in the
    /// context of a proc_macro.
    #[doc(hidden)]
    pub const fn __conjure_from_abyss(fields: &'static [internals::PathComponent]) -> Self {
        KeyPath {
            partial: PartialKeyPath {
                fields: Cow::Borrowed(fields),
                _root: PhantomData,
            },
            _value: PhantomData,
        }
    }

    /// Create a new `KeyPath` by combining two routes.
    ///
    /// The final type of the first route must be the first type of the second
    /// route; assuming both paths were created with the [`keypath!`] macro,
    /// the resulting path must be valid.
    ///
    /// # Examples
    ///
    /// ```
    /// use keypath::{Keyable, KeyPath, keypath};
    ///
    /// #[derive(Keyable)]
    /// struct Person {
    ///     name: String,
    ///     friends: Vec<String>,
    ///     size: Size,
    /// }
    ///
    /// #[derive(Keyable)]
    /// struct Size {
    ///     big: bool,
    ///     heft: u8,
    /// }
    ///
    /// let mut person = Person {
    ///     name: "coco".into(),
    ///     friends: vec!["eli".into(), "nico".into(), "yaya".into()],
    ///     size: Size { big: false, heft: 45 }
    /// };
    ///
    /// let size = keypath!(Person.size);
    /// let heft = keypath!(Size.heft);
    /// let combined = size.append(&heft);
    ///
    /// assert_eq!(person[&combined], 45);
    /// ```
    pub fn append<T>(&self, other: &KeyPath<Value, T>) -> KeyPath<Root, T> {
        let mut partial = self.partial.clone();
        partial
            .fields
            .to_mut()
            .extend(other.partial.fields.iter().clone());
        KeyPath {
            partial,
            _value: other._value,
        }
    }
}

/// A trait for types that can be indexed with keypaths.
pub trait Keyable: internals::RawKeyable {
    /// A type that describes properties on the inner type, for compile-time checking.
    ///
    /// This is the worst part of the code right now? We generate structs with magic
    /// names for each Keyable type.
    type Mirror;

    /// Return an instance of this type's mirror.
    fn mirror() -> Self::Mirror;

    //TODO: this is a bit of a mess, and I don't know what methods we will want
    //or need. Having partial keypaths or keypaths that are failable seems reasonable,
    //but I don't know what the types are going to look like yet.
    /// Attempt to traverse a series of `PathComponent`s, returning an `&dyn Any`
    /// if successful.
    fn try_any_at_path(
        &self,
        path: impl AsRef<[internals::PathComponent]>,
    ) -> Result<&dyn Any, FieldError> {
        self.get_field(path.as_ref())
            .map(internals::RawKeyable::as_any)
    }

    /// Attempt to traverse a series of `PathComponent`s, returning an `&mut dyn Any`
    /// if successful.
    fn try_any_at_path_mut(
        &mut self,
        path: impl AsRef<[internals::PathComponent]>,
    ) -> Result<&mut dyn Any, FieldError> {
        self.get_field_mut(path.as_ref())
            .map(internals::RawKeyable::as_any_mut)
    }

    //NOTE: these two methods are intended in cases where the keypath has not been
    //validated, but currently we don't really support creating invalid keypaths.
    #[doc(hidden)]
    fn try_item_at_path<T>(&self, path: &KeyPath<Self, T>) -> Result<&T, FieldError> {
        self.try_any_at_path(path)
            ////FIXME: no unwrap here, some new more expresesive error type instead
            .map(|t| t.downcast_ref().unwrap())
    }

    #[doc(hidden)]
    fn try_item_at_path_mut<T>(&mut self, path: &KeyPath<Self, T>) -> Result<&mut T, FieldError> {
        self.try_any_at_path_mut(path)
            //FIXME: no unwrap here, some new more expresesive error type instead
            .map(|t| t.downcast_mut().unwrap())
    }

    /// Get a reference to the value at the provided path.
    ///
    /// You generally won't need to call this, since you can use `[index]`
    /// syntax instead.
    ///
    /// Assuming the path was constructed with the [`keypath!`] macro, this
    /// method will not fail.
    ///
    ///
    /// # Panics
    ///
    /// I lied, it will fail if you provide an index into a collection and
    /// that item does not exist.
    fn item_at_path<T>(&self, path: &KeyPath<Self, T>) -> &T {
        self.try_item_at_path(path).unwrap()
    }

    /// Get a mutable reference to the value at the provided path.
    ///
    /// You generally won't need to call this, since you can use `[index]`
    /// syntax instead.
    ///
    /// Assuming the path was constructed with the [`keypath!`] macro, this
    /// method will not fail.
    ///
    ///
    /// # Panics
    ///
    /// I lied, it will fail if you provide an index into a collection and
    /// that item does not exist.
    fn item_at_path_mut<T>(&mut self, path: &KeyPath<Self, T>) -> &mut T {
        self.try_item_at_path_mut(path).unwrap()
    }
}

impl<Root: ?Sized, Value: 'static> AsRef<[internals::PathComponent]> for KeyPath<Root, Value> {
    fn as_ref(&self) -> &[internals::PathComponent] {
        self.partial.fields.as_ref()
    }
}

impl<R: ?Sized> Clone for PartialKeyPath<R> {
    fn clone(&self) -> Self {
        PartialKeyPath {
            fields: self.fields.clone(),
            _root: PhantomData,
        }
    }
}