Skip to content

Commit

Permalink
Add more methods on Object to access underlying values (#210)
Browse files Browse the repository at this point in the history
  • Loading branch information
noib3 authored Dec 30, 2024
1 parent a72ad38 commit b8393d9
Show file tree
Hide file tree
Showing 5 changed files with 290 additions and 23 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@
a crate containing integration tests annotated with `#[nvim_oxi::test]`
([#201](https://github.com/noib3/nvim-oxi/pull/201));

- a series of `Object::as_{kind}_unchecked_mut()` methods to get a mutable
reference to an `Object`'s underlying value without performing any runtime
checks;

- three `Object::as_{string,array,dictionary}_unchecked()` methods to get a
shared reference to an `Object`'s underlying string/array/dictionary without
performing any runtime checks;

### Changed

- `nvim_oxi::api::echo` is now generic over the highlight group type instead of
Expand All @@ -33,6 +41,9 @@
at its third parameter, and returns `Result<Object>` instead of `Result<()>`
([#208](https://github.com/noib3/nvim-oxi/pull/208));

- renamed `Object::into_dict_unchecked()` to
`Object::into_dictionary_unchecked()`;

### Removed

- the `SetHighlightOptsBuilder::global_link()` method. Use
Expand Down
2 changes: 1 addition & 1 deletion crates/api/src/types/command_modifiers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,6 @@ impl ToObject for CommandModifiers {
impl From<CommandModifiers> for Dictionary {
fn from(mods: CommandModifiers) -> Self {
let obj = mods.to_object().unwrap();
unsafe { obj.into_dict_unchecked() }
unsafe { obj.into_dictionary_unchecked() }
}
}
4 changes: 3 additions & 1 deletion crates/types/src/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,9 @@ impl FromObject for Array {
impl FromObject for Dictionary {
fn from_object(obj: Object) -> Result<Self, Error> {
match obj.kind() {
ObjectKind::Dictionary => Ok(unsafe { obj.into_dict_unchecked() }),
ObjectKind::Dictionary => {
Ok(unsafe { obj.into_dictionary_unchecked() })
},

other => Err(Error::FromWrongType {
expected: "string",
Expand Down
287 changes: 270 additions & 17 deletions crates/types/src/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,71 +148,322 @@ impl Object {
unsafe { NonOwning::new(std::ptr::read(self)) }
}

/// Returns the boolean stored in this [`Object`].
///
/// This is a zero-cost method that directly accesses the underlying value
/// without performing any runtime checks.
///
/// # Safety
///
/// TODO
/// This `Object`'s [`ObjectKind`] must be a
/// [`Boolean`][ObjectKind::Boolean]. Calling this method on an `Object`
/// with any other kind may result in undefined behavior.
#[cfg_attr(debug_assertions, track_caller)]
#[inline(always)]
pub unsafe fn as_boolean_unchecked(&self) -> bool {
debug_assert!(self.ty == ObjectKind::Boolean, "{:?}", self.ty);
self.data.boolean
}

/// Returns a mutable reference to the boolean stored in this
/// [`Object`].
///
/// This is a zero-cost method that directly accesses the underlying
/// value without performing any runtime checks.
///
/// # Safety
///
/// TODO
/// This `Object`'s [`ObjectKind`] must be a
/// [`Boolean`][ObjectKind::Boolean]. Calling this method on an `Object`
/// with any other kind may result in undefined behavior.
#[cfg_attr(debug_assertions, track_caller)]
#[inline(always)]
pub unsafe fn as_boolean_unchecked_mut(&mut self) -> &mut bool {
debug_assert!(self.ty == ObjectKind::Boolean, "{:?}", self.ty);
&mut self.data.boolean
}

/// Returns the integer stored in this [`Object`].
///
/// This is a zero-cost method that directly accesses the underlying
/// value without performing any runtime checks.
///
/// # Safety
///
/// This `Object`'s [`ObjectKind`] must be one of
/// [`Integer`][ObjectKind::Integer], [`Buffer`][ObjectKind::Buffer],
/// [`Window`][ObjectKind::Window], or [`TabPage`][ObjectKind::TabPage].
/// Calling this method on an `Object` with any other kind may result in
/// undefined
/// behavior.
#[cfg_attr(debug_assertions, track_caller)]
#[inline(always)]
pub unsafe fn as_integer_unchecked(&self) -> Integer {
debug_assert!(
matches!(
self.ty,
ObjectKind::Integer
| ObjectKind::Buffer
| ObjectKind::Window
| ObjectKind::TabPage
),
"{:?}",
self.ty
);
self.data.integer
}

/// Returns a mutable reference to the integer stored in this
/// [`Object`].
///
/// This is a zero-cost method that directly accesses the underlying
/// value without performing any runtime checks.
///
/// # Safety
///
/// TODO
/// This `Object`'s [`ObjectKind`] must be one of
/// [`Integer`][ObjectKind::Integer], [`Buffer`][ObjectKind::Buffer],
/// [`Window`][ObjectKind::Window], or [`TabPage`][ObjectKind::TabPage].
/// Calling this method on an `Object` with any other kind may result in
/// undefined
/// behavior.
#[cfg_attr(debug_assertions, track_caller)]
#[inline(always)]
pub unsafe fn as_integer_unchecked_mut(&mut self) -> &mut Integer {
debug_assert!(
matches!(
self.ty,
ObjectKind::Integer
| ObjectKind::Buffer
| ObjectKind::Window
| ObjectKind::TabPage
),
"{:?}",
self.ty
);
&mut self.data.integer
}

/// Returns the float stored in this [`Object`].
///
/// This is a zero-cost method that directly accesses the underlying
/// value without performing any runtime checks.
///
/// # Safety
///
/// This `Object`'s [`ObjectKind`] must be a [`Float`][ObjectKind::Float].
/// Calling this method on an `Object` with any other kind may result in
/// undefined behavior.
#[cfg_attr(debug_assertions, track_caller)]
#[inline(always)]
pub unsafe fn as_float_unchecked(&self) -> Float {
debug_assert!(self.ty == ObjectKind::Float, "{:?}", self.ty);
self.data.float
}

/// Returns a mutable reference to the float stored in this
/// [`Object`].
///
/// This is a zero-cost method that directly accesses the underlying
/// value without performing any runtime checks.
///
/// # Safety
///
/// This `Object`'s [`ObjectKind`] must be a [`Float`][ObjectKind::Float].
/// Calling this method on an `Object` with any other kind may result in
/// undefined behavior.
#[cfg_attr(debug_assertions, track_caller)]
#[inline(always)]
pub unsafe fn as_float_unchecked_mut(&mut self) -> &mut Float {
debug_assert!(self.ty == ObjectKind::Float, "{:?}", self.ty);
&mut self.data.float
}

/// Returns the Lua reference stored in this [`Object`].
///
/// This is a zero-cost method that directly accesses the underlying
/// value without performing any runtime checks.
///
/// # Safety
///
/// TODO
/// This `Object`'s [`ObjectKind`] must be a
/// [`LuaRef`][ObjectKind::LuaRef]. Calling this method on an `Object` with
/// any other kind may result in undefined behavior.
#[cfg_attr(debug_assertions, track_caller)]
#[inline(always)]
pub unsafe fn as_luaref_unchecked(&self) -> LuaRef {
debug_assert!(self.ty == ObjectKind::LuaRef, "{:?}", self.ty);
self.data.luaref
}

/// Returns a mutable reference to the Lua reference stored in this
/// [`Object`].
///
/// This is a zero-cost method that directly accesses the underlying
/// value without performing any runtime checks.
///
/// # Safety
///
/// TODO
/// This `Object`'s [`ObjectKind`] must be a
/// [`LuaRef`][ObjectKind::LuaRef]. Calling this method on an `Object` with
/// any other kind may result in undefined behavior.
#[cfg_attr(debug_assertions, track_caller)]
#[inline(always)]
pub unsafe fn as_luaref_unchecked_mut(&mut self) -> &mut LuaRef {
debug_assert!(self.ty == ObjectKind::LuaRef, "{:?}", self.ty);
&mut self.data.luaref
}

/// Returns a reference to the string stored in this [`Object`].
///
/// This is a zero-cost method that directly accesses the underlying
/// value without performing any runtime checks.
///
/// # Safety
///
/// This `Object`'s [`ObjectKind`] must be a
/// [`String`][ObjectKind::String]. Calling this method on an `Object` with
/// any other kind may result in undefined behavior.
#[cfg_attr(debug_assertions, track_caller)]
pub unsafe fn as_string_unchecked(&self) -> &crate::String {
debug_assert!(self.ty == ObjectKind::String, "{:?}", self.ty);
&self.data.string
}

/// Returns a mutable reference to the string stored in this
/// [`Object`].
///
/// Extracts the contained [`String`](crate::String) value without checking
/// that the object actually contains a [`String`](crate::String).
/// This is a zero-cost method that directly accesses the underlying
/// value without performing any runtime checks.
///
/// # Safety
///
/// This `Object`'s [`ObjectKind`] must be a
/// [`String`][ObjectKind::String]. Calling this method on an `Object` with
/// any other kind may result in undefined behavior.
#[cfg_attr(debug_assertions, track_caller)]
pub unsafe fn as_string_unchecked_mut(&mut self) -> &mut crate::String {
debug_assert!(self.ty == ObjectKind::String, "{:?}", self.ty);
&mut self.data.string
}

/// Returns the string stored in this [`Object`].
///
/// This is a zero-cost method that directly accesses the underlying value
/// without performing any runtime checks.
///
/// # Safety
///
/// This `Object`'s [`ObjectKind`] must be a
/// [`String`][ObjectKind::String]. Calling this method on an `Object` with
/// any other kind may result in undefined behavior.
#[cfg_attr(debug_assertions, track_caller)]
pub unsafe fn into_string_unchecked(self) -> crate::String {
debug_assert!(self.ty == ObjectKind::String, "{:?}", self.ty);
#[allow(clippy::unnecessary_struct_initialization)]
let s = crate::String { ..*self.data.string };
let string = crate::String { ..*self.data.string };
core::mem::forget(self);
s
string
}

/// Returns a reference to the array stored in this [`Object`].
///
/// This is a zero-cost method that directly accesses the underlying value
/// without performing any runtime checks.
///
/// # Safety
///
/// TODO
/// This `Object`'s [`ObjectKind`] must be an [`Array`][ObjectKind::Array].
/// Calling this method on an `Object` with any other kind may result in
/// undefined behavior.
#[cfg_attr(debug_assertions, track_caller)]
pub unsafe fn as_array_unchecked(&self) -> &Array {
debug_assert!(self.ty == ObjectKind::Array, "{:?}", self.ty);
&self.data.array
}

/// Returns a mutable reference to the array stored in this
/// [`Object`].
///
/// This is a zero-cost method that directly accesses the underlying value
/// without performing any runtime checks.
///
/// # Safety
///
/// This `Object`'s [`ObjectKind`] must be an [`Array`][ObjectKind::Array].
/// Calling this method on an `Object` with any other kind may result in
/// undefined behavior.
#[cfg_attr(debug_assertions, track_caller)]
pub unsafe fn as_array_unchecked_mut(&mut self) -> &mut Array {
debug_assert!(self.ty == ObjectKind::Array, "{:?}", self.ty);
&mut self.data.array
}

/// Returns the array stored in this [`Object`].
///
/// Extracts the contained [`Array`] value without checking that the object
/// actually contains an [`Array`].
/// This is a zero-cost method that directly accesses the underlying value
/// without performing any runtime checks.
///
/// # Safety
///
/// This `Object`'s [`ObjectKind`] must be an [`Array`][ObjectKind::Array].
/// Calling this method on an `Object` with any other kind may result in
/// undefined behavior.
#[cfg_attr(debug_assertions, track_caller)]
pub unsafe fn into_array_unchecked(self) -> Array {
debug_assert!(self.ty == ObjectKind::Array, "{:?}", self.ty);
#[allow(clippy::unnecessary_struct_initialization)]
let array = Array(crate::kvec::KVec { ..self.data.array.0 });
core::mem::forget(self);
array
}

/// Returns a reference to the dictionary stored in this [`Object`].
///
/// This is a zero-cost method that directly accesses the underlying value
/// without performing any runtime checks.
///
/// # Safety
///
/// This `Object`'s [`ObjectKind`] must be a
/// [`Dictionary`][ObjectKind::Dictionary]. Calling this method on an
/// `Object` with any other kind may result in undefined behavior.
#[cfg_attr(debug_assertions, track_caller)]
pub unsafe fn as_dictionary_unchecked(&self) -> &Dictionary {
debug_assert!(self.ty == ObjectKind::Dictionary, "{:?}", self.ty);
&self.data.dictionary
}

/// Returns a mutable reference to the dictionary stored in this
/// [`Object`].
///
/// This is a zero-cost method that directly accesses the underlying value
/// without performing any runtime checks.
///
/// # Safety
///
/// TODO
/// This `Object`'s [`ObjectKind`] must be a
/// [`Dictionary`][ObjectKind::Dictionary]. Calling this method on an
/// `Object` with any other kind may result in undefined behavior.
#[cfg_attr(debug_assertions, track_caller)]
pub unsafe fn as_dictionary_unchecked_mut(&mut self) -> &mut Dictionary {
debug_assert!(self.ty == ObjectKind::Dictionary, "{:?}", self.ty);
&mut self.data.dictionary
}

/// Returns the dictionary stored in this [`Object`].
///
/// This is a zero-cost method that directly accesses the underlying value
/// without performing any runtime checks.
///
/// Extracts the contained [`Dictionary`] value without checking that the
/// object actually contains a [`Dictionary`].
pub unsafe fn into_dict_unchecked(self) -> Dictionary {
/// # Safety
///
/// This `Object`'s [`ObjectKind`] must be a
/// [`Dictionary`][ObjectKind::Dictionary]. Calling this method on an
/// `Object` with any other kind may result in undefined behavior.
#[cfg_attr(debug_assertions, track_caller)]
pub unsafe fn into_dictionary_unchecked(self) -> Dictionary {
debug_assert!(self.ty == ObjectKind::Dictionary, "{:?}", self.ty);
#[allow(clippy::unnecessary_struct_initialization)]
let dict = Dictionary(crate::kvec::KVec { ..self.data.dictionary.0 });
core::mem::forget(self);
Expand Down Expand Up @@ -473,7 +724,9 @@ impl Pushable for Object {
ObjectKind::Float => self.as_float_unchecked().push(lstate),
ObjectKind::String => self.into_string_unchecked().push(lstate),
ObjectKind::Array => self.into_array_unchecked().push(lstate),
ObjectKind::Dictionary => self.into_dict_unchecked().push(lstate),
ObjectKind::Dictionary => {
self.into_dictionary_unchecked().push(lstate)
},
ObjectKind::LuaRef => {
Function::<(), ()>::from_ref(self.as_luaref_unchecked())
.push(lstate)
Expand Down
Loading

0 comments on commit b8393d9

Please sign in to comment.