Skip to content

Commit b8393d9

Browse files
authored
Add more methods on Object to access underlying values (#210)
1 parent a72ad38 commit b8393d9

File tree

5 files changed

+290
-23
lines changed

5 files changed

+290
-23
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@
1919
a crate containing integration tests annotated with `#[nvim_oxi::test]`
2020
([#201](https://github.com/noib3/nvim-oxi/pull/201));
2121

22+
- a series of `Object::as_{kind}_unchecked_mut()` methods to get a mutable
23+
reference to an `Object`'s underlying value without performing any runtime
24+
checks;
25+
26+
- three `Object::as_{string,array,dictionary}_unchecked()` methods to get a
27+
shared reference to an `Object`'s underlying string/array/dictionary without
28+
performing any runtime checks;
29+
2230
### Changed
2331

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

44+
- renamed `Object::into_dict_unchecked()` to
45+
`Object::into_dictionary_unchecked()`;
46+
3647
### Removed
3748

3849
- the `SetHighlightOptsBuilder::global_link()` method. Use

crates/api/src/types/command_modifiers.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,6 @@ impl ToObject for CommandModifiers {
4444
impl From<CommandModifiers> for Dictionary {
4545
fn from(mods: CommandModifiers) -> Self {
4646
let obj = mods.to_object().unwrap();
47-
unsafe { obj.into_dict_unchecked() }
47+
unsafe { obj.into_dictionary_unchecked() }
4848
}
4949
}

crates/types/src/conversion.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,9 @@ impl FromObject for Array {
135135
impl FromObject for Dictionary {
136136
fn from_object(obj: Object) -> Result<Self, Error> {
137137
match obj.kind() {
138-
ObjectKind::Dictionary => Ok(unsafe { obj.into_dict_unchecked() }),
138+
ObjectKind::Dictionary => {
139+
Ok(unsafe { obj.into_dictionary_unchecked() })
140+
},
139141

140142
other => Err(Error::FromWrongType {
141143
expected: "string",

crates/types/src/object.rs

Lines changed: 270 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -148,71 +148,322 @@ impl Object {
148148
unsafe { NonOwning::new(std::ptr::read(self)) }
149149
}
150150

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

168+
/// Returns a mutable reference to the boolean stored in this
169+
/// [`Object`].
170+
///
171+
/// This is a zero-cost method that directly accesses the underlying
172+
/// value without performing any runtime checks.
173+
///
159174
/// # Safety
160175
///
161-
/// TODO
176+
/// This `Object`'s [`ObjectKind`] must be a
177+
/// [`Boolean`][ObjectKind::Boolean]. Calling this method on an `Object`
178+
/// with any other kind may result in undefined behavior.
179+
#[cfg_attr(debug_assertions, track_caller)]
180+
#[inline(always)]
181+
pub unsafe fn as_boolean_unchecked_mut(&mut self) -> &mut bool {
182+
debug_assert!(self.ty == ObjectKind::Boolean, "{:?}", self.ty);
183+
&mut self.data.boolean
184+
}
185+
186+
/// Returns the integer stored in this [`Object`].
187+
///
188+
/// This is a zero-cost method that directly accesses the underlying
189+
/// value without performing any runtime checks.
190+
///
191+
/// # Safety
192+
///
193+
/// This `Object`'s [`ObjectKind`] must be one of
194+
/// [`Integer`][ObjectKind::Integer], [`Buffer`][ObjectKind::Buffer],
195+
/// [`Window`][ObjectKind::Window], or [`TabPage`][ObjectKind::TabPage].
196+
/// Calling this method on an `Object` with any other kind may result in
197+
/// undefined
198+
/// behavior.
199+
#[cfg_attr(debug_assertions, track_caller)]
162200
#[inline(always)]
163201
pub unsafe fn as_integer_unchecked(&self) -> Integer {
202+
debug_assert!(
203+
matches!(
204+
self.ty,
205+
ObjectKind::Integer
206+
| ObjectKind::Buffer
207+
| ObjectKind::Window
208+
| ObjectKind::TabPage
209+
),
210+
"{:?}",
211+
self.ty
212+
);
164213
self.data.integer
165214
}
166215

216+
/// Returns a mutable reference to the integer stored in this
217+
/// [`Object`].
218+
///
219+
/// This is a zero-cost method that directly accesses the underlying
220+
/// value without performing any runtime checks.
221+
///
167222
/// # Safety
168223
///
169-
/// TODO
224+
/// This `Object`'s [`ObjectKind`] must be one of
225+
/// [`Integer`][ObjectKind::Integer], [`Buffer`][ObjectKind::Buffer],
226+
/// [`Window`][ObjectKind::Window], or [`TabPage`][ObjectKind::TabPage].
227+
/// Calling this method on an `Object` with any other kind may result in
228+
/// undefined
229+
/// behavior.
230+
#[cfg_attr(debug_assertions, track_caller)]
231+
#[inline(always)]
232+
pub unsafe fn as_integer_unchecked_mut(&mut self) -> &mut Integer {
233+
debug_assert!(
234+
matches!(
235+
self.ty,
236+
ObjectKind::Integer
237+
| ObjectKind::Buffer
238+
| ObjectKind::Window
239+
| ObjectKind::TabPage
240+
),
241+
"{:?}",
242+
self.ty
243+
);
244+
&mut self.data.integer
245+
}
246+
247+
/// Returns the float stored in this [`Object`].
248+
///
249+
/// This is a zero-cost method that directly accesses the underlying
250+
/// value without performing any runtime checks.
251+
///
252+
/// # Safety
253+
///
254+
/// This `Object`'s [`ObjectKind`] must be a [`Float`][ObjectKind::Float].
255+
/// Calling this method on an `Object` with any other kind may result in
256+
/// undefined behavior.
257+
#[cfg_attr(debug_assertions, track_caller)]
170258
#[inline(always)]
171259
pub unsafe fn as_float_unchecked(&self) -> Float {
260+
debug_assert!(self.ty == ObjectKind::Float, "{:?}", self.ty);
172261
self.data.float
173262
}
174263

264+
/// Returns a mutable reference to the float stored in this
265+
/// [`Object`].
266+
///
267+
/// This is a zero-cost method that directly accesses the underlying
268+
/// value without performing any runtime checks.
269+
///
270+
/// # Safety
271+
///
272+
/// This `Object`'s [`ObjectKind`] must be a [`Float`][ObjectKind::Float].
273+
/// Calling this method on an `Object` with any other kind may result in
274+
/// undefined behavior.
275+
#[cfg_attr(debug_assertions, track_caller)]
276+
#[inline(always)]
277+
pub unsafe fn as_float_unchecked_mut(&mut self) -> &mut Float {
278+
debug_assert!(self.ty == ObjectKind::Float, "{:?}", self.ty);
279+
&mut self.data.float
280+
}
281+
282+
/// Returns the Lua reference stored in this [`Object`].
283+
///
284+
/// This is a zero-cost method that directly accesses the underlying
285+
/// value without performing any runtime checks.
286+
///
175287
/// # Safety
176288
///
177-
/// TODO
289+
/// This `Object`'s [`ObjectKind`] must be a
290+
/// [`LuaRef`][ObjectKind::LuaRef]. Calling this method on an `Object` with
291+
/// any other kind may result in undefined behavior.
292+
#[cfg_attr(debug_assertions, track_caller)]
178293
#[inline(always)]
179294
pub unsafe fn as_luaref_unchecked(&self) -> LuaRef {
295+
debug_assert!(self.ty == ObjectKind::LuaRef, "{:?}", self.ty);
180296
self.data.luaref
181297
}
182298

299+
/// Returns a mutable reference to the Lua reference stored in this
300+
/// [`Object`].
301+
///
302+
/// This is a zero-cost method that directly accesses the underlying
303+
/// value without performing any runtime checks.
304+
///
183305
/// # Safety
184306
///
185-
/// TODO
307+
/// This `Object`'s [`ObjectKind`] must be a
308+
/// [`LuaRef`][ObjectKind::LuaRef]. Calling this method on an `Object` with
309+
/// any other kind may result in undefined behavior.
310+
#[cfg_attr(debug_assertions, track_caller)]
311+
#[inline(always)]
312+
pub unsafe fn as_luaref_unchecked_mut(&mut self) -> &mut LuaRef {
313+
debug_assert!(self.ty == ObjectKind::LuaRef, "{:?}", self.ty);
314+
&mut self.data.luaref
315+
}
316+
317+
/// Returns a reference to the string stored in this [`Object`].
318+
///
319+
/// This is a zero-cost method that directly accesses the underlying
320+
/// value without performing any runtime checks.
321+
///
322+
/// # Safety
323+
///
324+
/// This `Object`'s [`ObjectKind`] must be a
325+
/// [`String`][ObjectKind::String]. Calling this method on an `Object` with
326+
/// any other kind may result in undefined behavior.
327+
#[cfg_attr(debug_assertions, track_caller)]
328+
pub unsafe fn as_string_unchecked(&self) -> &crate::String {
329+
debug_assert!(self.ty == ObjectKind::String, "{:?}", self.ty);
330+
&self.data.string
331+
}
332+
333+
/// Returns a mutable reference to the string stored in this
334+
/// [`Object`].
186335
///
187-
/// Extracts the contained [`String`](crate::String) value without checking
188-
/// that the object actually contains a [`String`](crate::String).
336+
/// This is a zero-cost method that directly accesses the underlying
337+
/// value without performing any runtime checks.
338+
///
339+
/// # Safety
340+
///
341+
/// This `Object`'s [`ObjectKind`] must be a
342+
/// [`String`][ObjectKind::String]. Calling this method on an `Object` with
343+
/// any other kind may result in undefined behavior.
344+
#[cfg_attr(debug_assertions, track_caller)]
345+
pub unsafe fn as_string_unchecked_mut(&mut self) -> &mut crate::String {
346+
debug_assert!(self.ty == ObjectKind::String, "{:?}", self.ty);
347+
&mut self.data.string
348+
}
349+
350+
/// Returns the string stored in this [`Object`].
351+
///
352+
/// This is a zero-cost method that directly accesses the underlying value
353+
/// without performing any runtime checks.
354+
///
355+
/// # Safety
356+
///
357+
/// This `Object`'s [`ObjectKind`] must be a
358+
/// [`String`][ObjectKind::String]. Calling this method on an `Object` with
359+
/// any other kind may result in undefined behavior.
360+
#[cfg_attr(debug_assertions, track_caller)]
189361
pub unsafe fn into_string_unchecked(self) -> crate::String {
362+
debug_assert!(self.ty == ObjectKind::String, "{:?}", self.ty);
190363
#[allow(clippy::unnecessary_struct_initialization)]
191-
let s = crate::String { ..*self.data.string };
364+
let string = crate::String { ..*self.data.string };
192365
core::mem::forget(self);
193-
s
366+
string
194367
}
195368

369+
/// Returns a reference to the array stored in this [`Object`].
370+
///
371+
/// This is a zero-cost method that directly accesses the underlying value
372+
/// without performing any runtime checks.
373+
///
196374
/// # Safety
197375
///
198-
/// TODO
376+
/// This `Object`'s [`ObjectKind`] must be an [`Array`][ObjectKind::Array].
377+
/// Calling this method on an `Object` with any other kind may result in
378+
/// undefined behavior.
379+
#[cfg_attr(debug_assertions, track_caller)]
380+
pub unsafe fn as_array_unchecked(&self) -> &Array {
381+
debug_assert!(self.ty == ObjectKind::Array, "{:?}", self.ty);
382+
&self.data.array
383+
}
384+
385+
/// Returns a mutable reference to the array stored in this
386+
/// [`Object`].
387+
///
388+
/// This is a zero-cost method that directly accesses the underlying value
389+
/// without performing any runtime checks.
390+
///
391+
/// # Safety
392+
///
393+
/// This `Object`'s [`ObjectKind`] must be an [`Array`][ObjectKind::Array].
394+
/// Calling this method on an `Object` with any other kind may result in
395+
/// undefined behavior.
396+
#[cfg_attr(debug_assertions, track_caller)]
397+
pub unsafe fn as_array_unchecked_mut(&mut self) -> &mut Array {
398+
debug_assert!(self.ty == ObjectKind::Array, "{:?}", self.ty);
399+
&mut self.data.array
400+
}
401+
402+
/// Returns the array stored in this [`Object`].
199403
///
200-
/// Extracts the contained [`Array`] value without checking that the object
201-
/// actually contains an [`Array`].
404+
/// This is a zero-cost method that directly accesses the underlying value
405+
/// without performing any runtime checks.
406+
///
407+
/// # Safety
408+
///
409+
/// This `Object`'s [`ObjectKind`] must be an [`Array`][ObjectKind::Array].
410+
/// Calling this method on an `Object` with any other kind may result in
411+
/// undefined behavior.
412+
#[cfg_attr(debug_assertions, track_caller)]
202413
pub unsafe fn into_array_unchecked(self) -> Array {
414+
debug_assert!(self.ty == ObjectKind::Array, "{:?}", self.ty);
203415
#[allow(clippy::unnecessary_struct_initialization)]
204416
let array = Array(crate::kvec::KVec { ..self.data.array.0 });
205417
core::mem::forget(self);
206418
array
207419
}
208420

421+
/// Returns a reference to the dictionary stored in this [`Object`].
422+
///
423+
/// This is a zero-cost method that directly accesses the underlying value
424+
/// without performing any runtime checks.
425+
///
426+
/// # Safety
427+
///
428+
/// This `Object`'s [`ObjectKind`] must be a
429+
/// [`Dictionary`][ObjectKind::Dictionary]. Calling this method on an
430+
/// `Object` with any other kind may result in undefined behavior.
431+
#[cfg_attr(debug_assertions, track_caller)]
432+
pub unsafe fn as_dictionary_unchecked(&self) -> &Dictionary {
433+
debug_assert!(self.ty == ObjectKind::Dictionary, "{:?}", self.ty);
434+
&self.data.dictionary
435+
}
436+
437+
/// Returns a mutable reference to the dictionary stored in this
438+
/// [`Object`].
439+
///
440+
/// This is a zero-cost method that directly accesses the underlying value
441+
/// without performing any runtime checks.
442+
///
209443
/// # Safety
210444
///
211-
/// TODO
445+
/// This `Object`'s [`ObjectKind`] must be a
446+
/// [`Dictionary`][ObjectKind::Dictionary]. Calling this method on an
447+
/// `Object` with any other kind may result in undefined behavior.
448+
#[cfg_attr(debug_assertions, track_caller)]
449+
pub unsafe fn as_dictionary_unchecked_mut(&mut self) -> &mut Dictionary {
450+
debug_assert!(self.ty == ObjectKind::Dictionary, "{:?}", self.ty);
451+
&mut self.data.dictionary
452+
}
453+
454+
/// Returns the dictionary stored in this [`Object`].
455+
///
456+
/// This is a zero-cost method that directly accesses the underlying value
457+
/// without performing any runtime checks.
212458
///
213-
/// Extracts the contained [`Dictionary`] value without checking that the
214-
/// object actually contains a [`Dictionary`].
215-
pub unsafe fn into_dict_unchecked(self) -> Dictionary {
459+
/// # Safety
460+
///
461+
/// This `Object`'s [`ObjectKind`] must be a
462+
/// [`Dictionary`][ObjectKind::Dictionary]. Calling this method on an
463+
/// `Object` with any other kind may result in undefined behavior.
464+
#[cfg_attr(debug_assertions, track_caller)]
465+
pub unsafe fn into_dictionary_unchecked(self) -> Dictionary {
466+
debug_assert!(self.ty == ObjectKind::Dictionary, "{:?}", self.ty);
216467
#[allow(clippy::unnecessary_struct_initialization)]
217468
let dict = Dictionary(crate::kvec::KVec { ..self.data.dictionary.0 });
218469
core::mem::forget(self);
@@ -473,7 +724,9 @@ impl Pushable for Object {
473724
ObjectKind::Float => self.as_float_unchecked().push(lstate),
474725
ObjectKind::String => self.into_string_unchecked().push(lstate),
475726
ObjectKind::Array => self.into_array_unchecked().push(lstate),
476-
ObjectKind::Dictionary => self.into_dict_unchecked().push(lstate),
727+
ObjectKind::Dictionary => {
728+
self.into_dictionary_unchecked().push(lstate)
729+
},
477730
ObjectKind::LuaRef => {
478731
Function::<(), ()>::from_ref(self.as_luaref_unchecked())
479732
.push(lstate)

0 commit comments

Comments
 (0)