Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add utilities for getting a nested field using a string path from a schema ref. #249

Merged
merged 1 commit into from
Oct 23, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 134 additions & 0 deletions framework_crates/bones_schema/src/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ use std::{
alloc::handle_alloc_error,
any::{type_name, TypeId},
hash::Hash,
iter::{Filter, Map},
marker::PhantomData,
mem::MaybeUninit,
ptr::NonNull,
str::Split,
sync::OnceLock,
};

Expand Down Expand Up @@ -90,6 +92,35 @@ impl<'pointer> SchemaRef<'pointer> {
self.get_field(idx).unwrap()
}

/// Get a nested field from the box.
///
/// # Panics
///
/// Panics if the field doesn't exist in the schema.
#[track_caller]
pub fn field_path<'a, I: IntoIterator<Item = FieldIdx<'a>>>(
self,
path: I,
) -> SchemaRef<'pointer> {
self.get_field_path(path).unwrap()
}

/// Get a nested field from the box.
///
/// # Errors
///
/// Errors if the field doesn't exist in the schema.
pub fn get_field_path<'a, I: IntoIterator<Item = FieldIdx<'a>>>(
self,
path: I,
) -> Result<SchemaRef<'pointer>, SchemaFieldNotFoundError<'a>> {
let mut schemaref = self;
for item in path {
schemaref = schemaref.get_field(item)?;
}
Ok(schemaref)
}

/// Get a pointer to a field.
///
/// # Errors
Expand Down Expand Up @@ -306,6 +337,73 @@ impl<'pointer, 'parent> SchemaRefMut<'pointer, 'parent> {
self.get_field(idx).unwrap()
}

/// Get a nested field from the box.
///
/// # Panics
///
/// Panics if the field doesn't exist in the schema.
#[track_caller]
pub fn into_field_path<'a, I: IntoIterator<Item = FieldIdx<'a>>>(
self,
path: I,
) -> SchemaRefMut<'pointer, 'parent> {
self.try_into_field_path(path).unwrap()
}

/// Get a nested field from the box.
///
/// # Errors
///
/// Errors if the field doesn't exist in the schema.
pub fn try_into_field_path<'a, I: IntoIterator<Item = FieldIdx<'a>>>(
self,
path: I,
) -> Result<SchemaRefMut<'pointer, 'parent>, Self> {
let mut schemaref = self;
for item in path {
schemaref = schemaref.try_into_field(item)?;
}
Ok(schemaref)
}

/// Get a nested field from the box.
///
/// # Panics
///
/// Panics if the field doesn't exist in the schema.
#[track_caller]
pub fn get_field_path<'this, 'a, I: IntoIterator<Item = FieldIdx<'a>>>(
&'this mut self,
path: I,
) -> SchemaRefMut<'pointer, 'this> {
self.try_get_field_path(path).unwrap()
}

/// Get a nested field from the box.
///
/// # Errors
///
/// Errors if the field doesn't exist in the schema.
pub fn try_get_field_path<'this, 'a, I: IntoIterator<Item = FieldIdx<'a>>>(
&'this mut self,
path: I,
) -> Result<SchemaRefMut<'pointer, 'this>, SchemaFieldNotFoundError<'a>> {
let mut schemaref = Self {
// SOUND: we are cloning our mutable reference here, but we are returning only one
// of them, and it contains the 'this lifetime that indicates a borrow of this one,
// preventing both from being used at the same time.
ptr: unsafe { PtrMut::new(NonNull::new_unchecked(self.ptr.as_ptr())) },
schema: self.schema,
parent_lifetime: PhantomData,
};
for item in path {
schemaref = schemaref
.try_into_field(item)
.map_err(|_| SchemaFieldNotFoundError { idx: item })?;
}
Ok(schemaref)
}

/// Get a pointer to a field.
///
/// # Errors
Expand Down Expand Up @@ -949,6 +1047,42 @@ impl<'a> std::fmt::Display for FieldIdx<'a> {
}
}

/// A wrapper type that implements [`IntoIterator<Item = FieldIdx>`] for an inner string to make
/// it easier to use with [`SchemaRef::get_field_path()`] and other field path methods.
pub struct FieldPath<T>(pub T);
impl<'a> IntoIterator for FieldPath<&'a str> {
type Item = FieldIdx<'a>;
type IntoIter = Map<Filter<Split<'a, char>, fn(&&str) -> bool>, fn(&str) -> FieldIdx>;

fn into_iter(self) -> Self::IntoIter {
fn flt(x: &&str) -> bool {
!x.is_empty()
}
fn mp(x: &str) -> FieldIdx {
x.parse::<usize>()
.map(FieldIdx::Idx)
.unwrap_or(FieldIdx::Name(x))
}
self.0.split('.').filter(flt as _).map(mp as _)
}
}
impl IntoIterator for FieldPath<Ustr> {
type Item = FieldIdx<'static>;
type IntoIter = Map<Filter<Split<'static, char>, fn(&&str) -> bool>, fn(&str) -> FieldIdx>;

fn into_iter(self) -> Self::IntoIter {
fn flt(x: &&str) -> bool {
!x.is_empty()
}
fn mp(x: &str) -> FieldIdx {
x.parse::<usize>()
.map(FieldIdx::Idx)
.unwrap_or(FieldIdx::Name(x))
}
self.0.as_str().split('.').filter(flt as _).map(mp as _)
}
}

/// Error type when attempting to cast between types with mis-matched schemas.
#[derive(Debug)]
pub struct SchemaMismatchError;
Expand Down
Loading