]> git.proxmox.com Git - perlmod.git/blob - perlmod/src/value.rs
experimental direct substr support
[perlmod.git] / perlmod / src / value.rs
1 //! The [`Value`] type is a generic perl value reference distinguishing between its types
2 //! automatically.
3
4 use std::fmt;
5
6 use serde::{Deserialize, Serialize};
7
8 use crate::ffi::{self, SV};
9 use crate::scalar::ScalarRef;
10 use crate::Error;
11 use crate::{perl_fn, raw_value};
12 use crate::{Array, Hash, Scalar};
13
14 /// A higher level value. This is basically an [`SV`] already cast to [`AV`](crate::ffi::AV) or
15 /// [`HV`](crate::ffi::HV) for arrays and hashes.
16 #[derive(Clone)]
17 pub enum Value {
18 Scalar(Scalar),
19 Reference(Scalar),
20 Array(Array),
21 Hash(Hash),
22 }
23
24 impl Value {
25 /// Create a new undef value:
26 pub fn new_undef() -> Self {
27 Value::Scalar(Scalar::new_undef())
28 }
29
30 /// Create a new integer value:
31 pub fn new_int(v: isize) -> Self {
32 Value::Scalar(Scalar::new_int(v))
33 }
34
35 /// Create a new unsigned integer value:
36 pub fn new_uint(v: usize) -> Self {
37 Value::Scalar(Scalar::new_uint(v))
38 }
39
40 /// Create a new floating point value.
41 pub fn new_float(v: f64) -> Self {
42 Value::Scalar(Scalar::new_float(v))
43 }
44
45 /// Create a new string value.
46 pub fn new_string(s: &str) -> Self {
47 Value::Scalar(Scalar::new_string(s))
48 }
49
50 /// Create a new byte string.
51 pub fn new_bytes(s: &[u8]) -> Self {
52 Value::Scalar(Scalar::new_bytes(s))
53 }
54
55 /// Create a new reference code reference.
56 ///
57 /// # Safety
58 ///
59 /// It is up to the user to ensure that only a valid perl XSUB is used. You should know what
60 /// you're doing, as perl code WILL execute the function and expect it to behave in a
61 /// particular way!
62 pub unsafe fn new_xsub(xsub: perl_fn!(extern "C" fn(*mut crate::ffi::CV))) -> Self {
63 unsafe {
64 Self::from_raw_move(crate::ffi::RSPL_newXS_flags(
65 std::ptr::null(),
66 xsub as _,
67 std::ptr::null(),
68 std::ptr::null(),
69 0,
70 ) as _)
71 }
72 }
73
74 /// If the value is an array, returns the associated [`Array`].
75 pub fn as_array(&self) -> Option<&Array> {
76 match self {
77 Value::Array(v) => Some(v),
78 _ => None,
79 }
80 }
81
82 /// If the value is an array, returns the associated mutable [`Array`].
83 pub fn as_array_mut(&mut self) -> Option<&mut Array> {
84 match self {
85 Value::Array(v) => Some(v),
86 _ => None,
87 }
88 }
89
90 /// If the value is a hash, returns the associated [`struct@Hash`].
91 pub fn as_hash(&self) -> Option<&Hash> {
92 match self {
93 Value::Hash(v) => Some(v),
94 _ => None,
95 }
96 }
97
98 /// If the value is a hash, returns the associated mutable [`struct@Hash`].
99 pub fn as_hash_mut(&mut self) -> Option<&mut Hash> {
100 match self {
101 Value::Hash(v) => Some(v),
102 _ => None,
103 }
104 }
105
106 /// Convenience method to create a new raw pointer value. Note that pointers are stored as
107 /// arbitrary "byte strings" and any such byte string value can be interpreted as a raw pointer.
108 pub fn new_pointer<T>(s: *mut T) -> Self {
109 Self::new_bytes(&(s as usize).to_ne_bytes())
110 }
111
112 /// Create an actual perl reference to the value. (The equivalent of perl's backslash
113 /// operator).
114 pub fn new_ref<T>(value: &T) -> Self
115 where
116 T: std::ops::Deref<Target = ScalarRef>,
117 {
118 Value::Reference(unsafe { Scalar::from_raw_move(ffi::RSPL_newRV_inc(value.sv())) })
119 }
120
121 /// Create a new empty hash.
122 pub fn new_hash() -> Self {
123 Value::Hash(Hash::new())
124 }
125
126 /// Bless a reference into a package. The `Value` must be a reference.
127 ///
128 /// Note that a blessed value in perl can have a destructor (a `DESTROY` sub), and keeps track
129 /// of references, so one can implement a class package like this:
130 ///
131 /// ```
132 /// // 'lib' and 'file' are optional. We use 'file' here to prevent doc tests from writing out
133 /// // the file.
134 /// #[perlmod::package(name = "RSPM::MyThing", lib = "bless_doctest", file="/dev/null")]
135 /// mod export {
136 /// # use perlmod::{Error, Value};
137 ///
138 /// struct MyThing {
139 /// content: String,
140 /// }
141 ///
142 /// #[export(raw_return)]
143 /// fn new(#[raw] class: Value, content: String) -> Result<Value, Error> {
144 /// let mut ptr = Box::new(MyThing { content });
145 /// // Short version:
146 /// // return Value::bless_box(class, ptr);
147 ///
148 /// // Raw code:
149 ///
150 /// // create a pointer value
151 /// let value = Value::new_pointer::<MyThing>(&mut *ptr);
152 ///
153 /// // create a reference to it:
154 /// let value = Value::new_ref(&value);
155 ///
156 /// // use the the provided class name as perl passes it along when using
157 /// // `RSPM::MyThing->new()`. Alternatively this could be hardcoded and
158 /// // `RSPM::MyThing::new()` (without an arrow) would be used instead.
159 /// let this = value.bless_sv(&class)?;
160 ///
161 /// // From here on out perl will call our destructor defined below, so
162 /// // it's time to drop our reference to it!
163 /// let _perl = Box::leak(ptr);
164 ///
165 /// Ok(this)
166 /// }
167 ///
168 /// #[export]
169 /// fn something(#[raw] this: Value) {
170 /// let _ = this; // see the `DESTROY` sub below for how to access this.
171 /// println!("Example method callable via $foo->something()!");
172 /// }
173 ///
174 /// #[export(name = "DESTROY")]
175 /// fn destroy(#[raw] this: Value) {
176 /// match this
177 /// .dereference()
178 /// .ok_or_else(|| Error::new("not a reference"))
179 /// .and_then(|this| Ok(this.pv_raw()?))
180 /// {
181 /// Ok(ptr) => {
182 /// let value = unsafe { Box::<MyThing>::from_raw(ptr) };
183 /// println!("Dropping value {:?}", value.content);
184 /// }
185 /// Err(err) => {
186 /// println!("DESTROY called with invalid pointer: {}", err);
187 /// }
188 /// }
189 /// }
190 /// }
191 /// ```
192 pub fn bless(&self, package: &str) -> Result<Value, Error> {
193 let pkgsv = Scalar::new_string(package);
194 self.bless_sv(&pkgsv)
195 }
196
197 /// Same as [`bless`](Self::bless()) but the package string is already a perl [`ScalarRef`].
198 pub fn bless_sv(&self, pkgsv: &ScalarRef) -> Result<Value, Error> {
199 let stash = unsafe { ffi::RSPL_gv_stashsv(pkgsv.sv(), 0) };
200 if stash.is_null() {
201 return Err(Error(format!(
202 "failed to find package {:?}",
203 pkgsv.pv_string_utf8()
204 )));
205 }
206
207 let value = unsafe { ffi::RSPL_sv_bless(self.sv(), stash) };
208 if value.is_null() {
209 return Err(Error(format!(
210 "failed to bless value into package {:?}",
211 pkgsv.pv_string_utf8()
212 )));
213 }
214
215 Ok(Value::Reference(unsafe { Scalar::from_raw_ref(value) }))
216 }
217
218 /// Take over a raw `SV` value, assuming that we then own a reference to it.
219 ///
220 /// # Safety
221 ///
222 /// This does not change the value's reference count, it is assumed that we're taking ownership
223 /// of one reference.
224 ///
225 /// The caller must ensure that it is safe to decrease the reference count later on, or use
226 /// `into_raw()` instead of letting the `Value` get dropped.
227 pub unsafe fn from_raw_move(ptr: *mut SV) -> Self {
228 Self::from_scalar(unsafe { Scalar::from_raw_move(ptr as *mut SV) })
229 }
230
231 /// Create a new reference to an existing `SV` value. This will increase the value's reference
232 /// count.
233 ///
234 /// # Safety
235 ///
236 /// The caller may still need to decrease the reference count for the `ptr` source value.
237 pub unsafe fn from_raw_ref(ptr: *mut SV) -> Self {
238 Self::from_scalar(unsafe { Scalar::from_raw_ref(ptr as *mut SV) })
239 }
240
241 /// Convert a [`Scalar`] to a [`Value`].
242 pub fn from_scalar(scalar: Scalar) -> Self {
243 Self::from(scalar)
244 }
245
246 /// Create a new reference to this value.
247 pub fn clone_ref(&self) -> Self {
248 match self {
249 Value::Scalar(v) => Value::Scalar(v.clone_ref()),
250 Value::Reference(v) => Value::Reference(v.clone_ref()),
251 Value::Array(v) => Value::Array(v.clone_ref()),
252 Value::Hash(v) => Value::Hash(v.clone_ref()),
253 }
254 }
255
256 /// Dereference this reference value.
257 pub fn dereference(&self) -> Option<Value> {
258 match self {
259 Value::Reference(v) => v.dereference().map(Value::from_scalar),
260 _ => None,
261 }
262 }
263
264 /// Turn this into a raw `SV` transferring control of one reference count.
265 pub fn into_raw(self) -> *mut SV {
266 match self {
267 Value::Scalar(v) => v.into_raw(),
268 Value::Reference(v) => v.into_raw(),
269 Value::Array(v) => v.into_scalar().into_raw(),
270 Value::Hash(v) => v.into_scalar().into_raw(),
271 }
272 }
273
274 /// Turn this into a "mortal" value in perl.
275 pub fn into_mortal(self) -> crate::scalar::Mortal {
276 match self {
277 Value::Scalar(v) => v.into_mortal(),
278 Value::Reference(v) => v.into_mortal(),
279 Value::Array(v) => v.into_scalar().into_mortal(),
280 Value::Hash(v) => v.into_scalar().into_mortal(),
281 }
282 }
283
284 /// If this is value is an array, get the value at the specified index.
285 pub fn get(&self, index: usize) -> Option<Value> {
286 if let Value::Array(a) = self {
287 a.get(index)
288 } else {
289 None
290 }
291 }
292
293 /// Check that the value is a reference and if so, assume it is a reference to a boxed rust
294 /// type and return a reference to it.
295 ///
296 /// # Safety
297 ///
298 /// This is mainly a helper to be used for blessed values. This only checks that the value
299 /// itself is any kind of reference, then assumes it contains something resembling a pointer
300 /// (see [`ScalarRef::pv_raw`](ScalarRef::pv_raw())), and if so, simply casts it to `T`.
301 pub unsafe fn from_ref_box<T>(&self) -> Result<&T, Error> {
302 let ptr = self
303 .dereference()
304 .ok_or_else(|| Error::new("not a reference"))?
305 .pv_raw()?;
306 Ok(unsafe { &*(ptr as *const T) })
307 }
308
309 /// Check that the value is a reference and blessed into a particular package name. If so,
310 /// assume it is a referenced to a boxed rust type and return a reference to it.
311 ///
312 /// # Safety
313 ///
314 /// See [`Value::from_ref_box`]. This additionally uses [`reftype`](ScalarRef::reftype) to
315 /// check that the passed value was indeed blessed into the provided `package` name. Other than
316 /// that, it cannot verify the the contained pointer is truly a `T`.
317 pub unsafe fn from_blessed_box<'a, T>(&'a self, package: &'_ str) -> Result<&'a T, Error> {
318 let ptr = self
319 .dereference()
320 .ok_or_else(|| Error::new("not a reference"))?;
321
322 let reftype = ptr.reftype(true);
323 if reftype != package {
324 return Err(Error::new_owned(format!(
325 "value not blessed into {:?} (`ref` returned {:?})",
326 package, reftype,
327 )));
328 }
329
330 Ok(unsafe { &*(ptr.pv_raw()? as *const T) })
331 }
332
333 /// Take ownership of a boxed value and create a perl value blessed into a package name.
334 ///
335 /// Note that this leaks the box. To let perl properly drop the value, the class name (which
336 /// must be a valid perl class name), must include a `DESTROY` sub implementing a compatible
337 /// destructor. Like so:
338 ///
339 /// ```
340 /// #[perlmod::package(name = "My::Thing")]
341 /// mod export {
342 /// use std::sync::Mutex;
343 ///
344 /// use perlmod::{Error, Value};
345 ///
346 /// const CLASSNAME: &str = "My::Thing";
347 ///
348 /// /// Some thing.
349 /// pub struct Thing {
350 /// stuff: String,
351 /// }
352 ///
353 /// /// Constructor for a Thing.
354 /// /// Prior to perlmod 0.6, the `raw_return` and `#[raw]` attributes were necessary, now
355 /// /// they're optional but produce slightly less code.
356 /// #[export(raw_return)]
357 /// pub fn new(#[raw] class: Value, stuff: String) -> Result<Value, Error> {
358 /// Value::bless_box(class, Box::new(Thing { stuff }))
359 /// }
360 ///
361 /// /// Destructor for a Thing.
362 /// #[export(name = "DESTROY")]
363 /// fn destroy(#[raw] this: Value) {
364 /// perlmod::destructor!(this, Thing: CLASSNAME);
365 /// }
366 ///
367 /// /// Convenience helper for generating methods more quickly.
368 /// impl<'a> TryFrom<&'a Value> for &'a Thing {
369 /// type Error = Error;
370 ///
371 /// fn try_from(value: &'a Value) -> Result<&'a Thing, Error> {
372 /// Ok(unsafe { value.from_blessed_box(CLASSNAME)? })
373 /// }
374 /// }
375 ///
376 /// /// The `try_from_ref` attribute tells perlmod to use `TryFrom::try_from(&value)` to
377 /// /// pass to `say_hello`. The conversion will `die` in perl on failure.
378 /// #[export]
379 /// pub fn say_hello(#[try_from_ref] this: &Thing) {
380 /// println!("Hello, {}", this.stuff);
381 /// }
382 /// }
383 /// ```
384 pub fn bless_box<T>(class: Value, mut box_: Box<T>) -> Result<Value, Error> {
385 let value = Value::new_pointer::<T>(&mut *box_);
386 let value = Value::new_ref(&value);
387 let this = value.bless_sv(&class)?;
388 let _perl = Box::leak(box_);
389 Ok(this)
390 }
391
392 /// Attempt to create a substring, provided the contained value is actually a string.
393 pub fn substr<I>(&self, index: I) -> Result<Value, Error>
394 where
395 I: std::slice::SliceIndex<[u8], Output = [u8]>,
396 {
397 match self {
398 Value::Scalar(s) => s.substr(index).map(Value::Scalar),
399 _ => Err(Error::new("substr called on non-scalar")),
400 }
401 }
402 }
403
404 impl From<Scalar> for Value {
405 fn from(scalar: Scalar) -> Self {
406 unsafe {
407 if ffi::RSPL_is_array(scalar.sv()) {
408 Value::Array(Array::from_scalar(scalar))
409 } else if ffi::RSPL_is_hash(scalar.sv()) {
410 Value::Hash(Hash::from_scalar(scalar))
411 } else if ffi::RSPL_is_reference(scalar.sv()) {
412 Value::Reference(scalar)
413 } else {
414 Value::Scalar(scalar)
415 }
416 }
417 }
418 }
419
420 impl From<Hash> for Value {
421 fn from(hash: Hash) -> Self {
422 Value::Hash(hash)
423 }
424 }
425
426 impl From<Array> for Value {
427 fn from(array: Array) -> Self {
428 Value::Array(array)
429 }
430 }
431
432 impl From<raw_value::RawValue> for Value {
433 fn from(value: raw_value::RawValue) -> Self {
434 value.into_inner()
435 }
436 }
437
438 impl fmt::Debug for Value {
439 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
440 use fmt::Debug;
441 match self {
442 Value::Scalar(v) => Debug::fmt(v, f),
443 Value::Reference(v) => Debug::fmt(v, f),
444 Value::Array(v) => Debug::fmt(v, f),
445 Value::Hash(v) => Debug::fmt(v, f),
446 }
447 }
448 }
449
450 impl core::ops::Deref for Value {
451 type Target = ScalarRef;
452
453 fn deref(&self) -> &Self::Target {
454 match self {
455 Value::Scalar(v) => &*v,
456 Value::Reference(v) => &*v,
457 Value::Array(v) => &*v,
458 Value::Hash(v) => &*v,
459 }
460 }
461 }
462
463 impl core::ops::DerefMut for Value {
464 fn deref_mut(&mut self) -> &mut Self::Target {
465 match self {
466 Value::Scalar(v) => &mut *v,
467 Value::Reference(v) => &mut *v,
468 Value::Array(v) => &mut *v,
469 Value::Hash(v) => &mut *v,
470 }
471 }
472 }
473
474 impl AsRef<ScalarRef> for Value {
475 #[inline]
476 fn as_ref(&self) -> &ScalarRef {
477 &*self
478 }
479 }
480
481 impl AsMut<ScalarRef> for Value {
482 #[inline]
483 fn as_mut(&mut self) -> &mut ScalarRef {
484 &mut *self
485 }
486 }
487
488 impl Serialize for Value {
489 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
490 where
491 S: serde::Serializer,
492 {
493 use serde::ser::Error;
494
495 if raw_value::is_enabled() {
496 raw_value::serialize_raw(self, serializer)
497 } else {
498 match self {
499 Value::Scalar(this) => this.serialize(serializer),
500 Value::Reference(this) => Value::from(
501 this.dereference()
502 .ok_or_else(|| S::Error::custom("failed to dereference perl value"))?,
503 )
504 .serialize(serializer),
505 Value::Array(value) => value.serialize(serializer),
506 Value::Hash(value) => value.serialize(serializer),
507 }
508 }
509 }
510 }
511
512 impl<'de> Deserialize<'de> for Value {
513 fn deserialize<D>(deserializer: D) -> Result<Value, D::Error>
514 where
515 D: serde::Deserializer<'de>,
516 {
517 use serde::de::Visitor;
518
519 struct ValueVisitor;
520
521 impl<'de> Visitor<'de> for ValueVisitor {
522 type Value = Value;
523
524 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
525 formatter.write_str("any valid PERL value")
526 }
527
528 #[inline]
529 fn visit_bool<E>(self, value: bool) -> Result<Value, E> {
530 Ok(Value::new_int(if value { 1 } else { 0 }))
531 }
532
533 #[inline]
534 fn visit_i64<E>(self, value: i64) -> Result<Value, E> {
535 Ok(Value::new_int(value as isize))
536 }
537
538 #[inline]
539 fn visit_u64<E>(self, value: u64) -> Result<Value, E> {
540 Ok(Value::new_uint(value as usize))
541 }
542
543 #[inline]
544 fn visit_f64<E>(self, value: f64) -> Result<Value, E> {
545 Ok(Value::new_float(value))
546 }
547
548 #[inline]
549 fn visit_str<E>(self, value: &str) -> Result<Value, E>
550 where
551 E: serde::de::Error,
552 {
553 Ok(Value::new_string(value))
554 }
555
556 #[inline]
557 fn visit_string<E>(self, value: String) -> Result<Value, E>
558 where
559 E: serde::de::Error,
560 {
561 self.visit_str(&value)
562 }
563
564 #[inline]
565 fn visit_none<E>(self) -> Result<Value, E>
566 where
567 E: serde::de::Error,
568 {
569 Ok(Value::new_undef())
570 }
571
572 #[inline]
573 fn visit_some<D>(self, deserializer: D) -> Result<Value, D::Error>
574 where
575 D: serde::Deserializer<'de>,
576 {
577 Deserialize::deserialize(deserializer)
578 }
579
580 #[inline]
581 fn visit_unit<E>(self) -> Result<Value, E> {
582 Ok(Value::new_undef())
583 }
584
585 #[inline]
586 fn visit_seq<V>(self, mut visitor: V) -> Result<Value, V::Error>
587 where
588 V: serde::de::SeqAccess<'de>,
589 {
590 let array = Array::new();
591
592 while let Some(elem) = visitor.next_element()? {
593 array.push(elem);
594 }
595
596 Ok(Value::Array(array))
597 }
598
599 fn visit_map<V>(self, mut visitor: V) -> Result<Value, V::Error>
600 where
601 V: serde::de::MapAccess<'de>,
602 {
603 // We use this to hint the deserializer that we're expecting a string-ish value.
604 struct KeyClassifier;
605 struct KeyClass(String);
606
607 impl<'de> serde::de::DeserializeSeed<'de> for KeyClassifier {
608 type Value = KeyClass;
609
610 fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
611 where
612 D: serde::Deserializer<'de>,
613 {
614 deserializer.deserialize_str(self)
615 }
616 }
617
618 impl<'de> Visitor<'de> for KeyClassifier {
619 type Value = KeyClass;
620
621 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
622 formatter.write_str("a string key")
623 }
624
625 fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
626 where
627 E: serde::de::Error,
628 {
629 Ok(KeyClass(s.to_owned()))
630 }
631
632 fn visit_string<E>(self, s: String) -> Result<Self::Value, E>
633 where
634 E: serde::de::Error,
635 {
636 Ok(KeyClass(s))
637 }
638 }
639
640 let hash = Hash::new();
641 while let Some(key) = visitor.next_key_seed(KeyClassifier)? {
642 let value: Value = visitor.next_value()?;
643 hash.insert(&key.0, value);
644 }
645 Ok(Value::from(hash))
646 }
647 }
648
649 if raw_value::is_enabled() {
650 raw_value::RawValue::deserialize(deserializer).map(Value::from)
651 } else {
652 deserializer.deserialize_any(ValueVisitor)
653 }
654 }
655 }