diff --git a/Cargo.lock b/Cargo.lock index c6f945c..f324de7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 4 [[package]] name = "field_path" -version = "0.2.0" +version = "0.3.0" dependencies = [ "hashbrown", ] diff --git a/Cargo.toml b/Cargo.toml index 37ef492..57a67f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "field_path" description = "Type-safe, no-std field access and reflection utilities." -version = "0.2.1" +version = "0.3.0" edition = "2024" license = "MIT OR Apache-2.0" repository = "https://github.com/voxell-tech/field_path" diff --git a/src/accessor.rs b/src/accessor.rs index 8c67962..1439f3d 100644 --- a/src/accessor.rs +++ b/src/accessor.rs @@ -61,7 +61,7 @@ impl Accessor { (self.mut_fn)(source) } - pub fn untyped(self) -> UntypedAccessor { + pub const fn untyped(self) -> UntypedAccessor { UntypedAccessor::new(self.ref_fn, self.mut_fn) } } @@ -114,7 +114,7 @@ pub struct UntypedAccessor { impl UntypedAccessor { /// Create a new type-erased accessor from a typed accessor pair. - pub fn new( + pub const fn new( ref_fn: fn(&S) -> &T, mut_fn: fn(&mut S) -> &mut T, ) -> Self { @@ -133,7 +133,9 @@ impl UntypedAccessor { /// /// Undefined behavior if `S` and `T` do not match the types used /// when constructing this accessor. - pub unsafe fn typed_unchecked(self) -> Accessor { + pub const unsafe fn typed_unchecked( + self, + ) -> Accessor { unsafe { Accessor { ref_fn: core::mem::transmute::<*const (), fn(&S) -> &T>( diff --git a/src/field.rs b/src/field.rs index d1a529d..978c960 100644 --- a/src/field.rs +++ b/src/field.rs @@ -67,7 +67,7 @@ impl Field { } /// Returns the raw field path string. - pub fn field_path(&self) -> &'static str { + pub const fn field_path(&self) -> &'static str { self.field_path } } @@ -78,7 +78,7 @@ where T: 'static, { /// Converts into a [`UntypedField`] type. - pub fn untyped(&self) -> UntypedField { + pub const fn untyped(&self) -> UntypedField { UntypedField::new::(self.field_path) } } @@ -172,7 +172,7 @@ pub struct UntypedField { } impl UntypedField { - pub fn new( + pub const fn new( field_path: &'static str, ) -> Self { Self { @@ -182,42 +182,47 @@ impl UntypedField { } } - pub fn placeholder() -> Self { + pub const fn placeholder() -> Self { Self::placeholder_with_path("$") } - pub fn placeholder_with_path(field_path: &'static str) -> Self { + pub const fn placeholder_with_path( + field_path: &'static str, + ) -> Self { Self::new::<(), ()>(field_path) } /// Get the [`TypeId`] of the source type. - pub fn source_id(&self) -> TypeId { + pub const fn source_id(&self) -> TypeId { self.source_id } /// Get the [`TypeId`] of the target type. - pub fn target_id(&self) -> TypeId { + pub const fn target_id(&self) -> TypeId { self.target_id } /// See [`Field::field_path`]. - pub fn field_path(&self) -> &'static str { + pub const fn field_path(&self) -> &'static str { self.field_path } - /// Converts into a typed [`Field`]. - /// - /// # Panics - /// - /// Panics if the type does not match. - pub fn typed(self) -> Field { - assert_eq!(TypeId::of::(), self.source_id); - assert_eq!(TypeId::of::(), self.target_id); - self.typed_unchecked() + /// Attempt to re-interpret this accessor as a typed [`Field`], + /// returning `None` if the [`TypeId`]s do not match. + pub fn typed( + self, + ) -> Option> { + if self.source_id == TypeId::of::() + && self.target_id == TypeId::of::() + { + return Some(self.typed_unchecked()); + } + + None } /// Converts into a typed [`Field`] without type checks. - pub fn typed_unchecked(self) -> Field { + pub const fn typed_unchecked(self) -> Field { Field::new(self.field_path) } }