koto_runtime/types/
object.rs

1use crate::{Borrow, BorrowMut, ErrorKind, PtrMut, Result, prelude::*};
2use std::{any::Any, fmt, marker::PhantomData, ops::Deref};
3
4/// A trait for specifying a Koto object's type
5///
6/// Using `#[derive(KotoType)]` is recommended.
7pub trait KotoType {
8    /// The Object's type as a static string
9    fn type_static() -> &'static str
10    where
11        Self: Sized;
12
13    /// The type of the Object as a [KString]
14    ///
15    /// This should defer to the type returned by [KotoType::type_static],
16    /// and will be called whenever the object's type is needed by the runtime,
17    /// e.g. when a script calls `koto.type`, so caching the result is a good idea.
18    /// `#[derive(KotoType)]` takes care of the details here.
19    fn type_string(&self) -> KString;
20}
21
22/// A trait for defining how objects should behave when copied in the Koto runtime
23///
24/// Use `#[derive(KotoCopy)]` for simple objects that don't need a custom implementation of
25/// [KotoCopy::deep_copy].
26pub trait KotoCopy {
27    /// How the object should behave when called from `koto.copy`
28    ///
29    /// A default implementation can't be provided here, but a typical implementation will look
30    /// similar to: `Object::from(self.clone())`
31    fn copy(&self) -> KObject;
32
33    /// How the object should behave when called from `koto.deep_copy`
34    ///
35    /// Deep copies should ensure that deep copies are performed for any Koto values that are owned
36    /// by the object (see [KValue::deep_copy]).
37    fn deep_copy(&self) -> KObject {
38        self.copy()
39    }
40}
41
42/// A trait that allows objects to support '.' accesses
43///
44/// This is the mechanism for attaching custom methods to objects in the Koto runtime.
45///
46/// The `#[koto_impl]` macro provides an easy way to declare methods that should be made available
47/// via '.' access by using the `#[koto_method]` attribute, and then derives an appropriate
48/// implementation of [KotoEntries].
49pub trait KotoEntries {
50    /// Returns an optional [KMap] containing entries that can be accessed via the '.' operator.
51    ///
52    /// Implementations should return a clone of a cached map. `None` is returned by default.
53    fn entries(&self) -> Option<KMap> {
54        None
55    }
56}
57
58/// A trait for implementing objects that can be added to the Koto runtime
59///
60/// [`KotoObject`]s are added to the Koto runtime by the [KObject] type, and stored as
61/// [`KValue::Object`]s.
62///
63/// ## Example
64///
65/// ```
66/// use koto_runtime::{derive::*, prelude::*, Result};
67///
68/// #[derive(Clone, Default, KotoType, KotoCopy)]
69/// pub struct Foo {
70///     data: i32,
71/// }
72///
73/// // The `#[koto_impl]` macro derives an implementation of [KotoEntries] containing wrapper
74/// // functions for each impl function tagged with `#[koto_method]`.
75/// #[koto_impl(runtime = koto_runtime)]
76/// impl Foo {
77///     // Simple methods tagged with `#[koto_method]` can use a `&self` argument.
78///     #[koto_method(alias = "data")]
79///     fn get_data(&self) -> KValue {
80///         self.data.into()
81///     }
82///
83///     // An example of a more complex method that makes use of [MethodContext] to return the
84///     // instance as the result, which allows for chaining of setter operations.  e.g.:
85///     // ```koto
86///     // make_foo(42)
87///     //  .set_data(99)
88///     //  .set_data(-1)
89///     //  .get_data()
90///     // # -1
91///     // ```
92///     #[koto_method]
93///     fn set_data(ctx: MethodContext<Self>) -> Result<KValue> {
94///         match ctx.args {
95///             [KValue::Number(n)] => ctx.instance_mut()?.data = n.into(),
96///             unexpected => return unexpected_args("|Number|", unexpected),
97///         }
98///
99///         // Return the object instance as the result of the setter operation
100///         ctx.instance_result()
101///     }
102/// }
103///
104/// impl KotoObject for Foo {
105///     fn display(&self, ctx: &mut DisplayContext) -> Result<()> {
106///         ctx.append(format!("Foo({})", self.data));
107///         Ok(())
108///     }
109/// }
110/// ```
111///
112/// See also: [KObject].
113pub trait KotoObject: KotoType + KotoCopy + KotoEntries + KotoSend + KotoSync + Any {
114    /// Called when the object should be displayed as a string, e.g. by `io.print`
115    ///
116    /// By default, the object's type is used as the display string.
117    ///
118    /// The [`DisplayContext`] is used to append strings to the result, and provides information
119    /// about how the contents should be formatted,
120    /// e.g. the value is in a container, or the result should be displayed with debug information.
121    fn display(&self, ctx: &mut DisplayContext) -> Result<()> {
122        ctx.append(self.type_string());
123        Ok(())
124    }
125
126    /// Called for indexing operations, e.g. `x[0]`
127    ///
128    /// See also: [KotoObject::size]
129    fn index(&self, index: &KValue) -> Result<KValue> {
130        let _ = index;
131        unimplemented_error("@index", self.type_string())
132    }
133
134    /// Called when mutating an object via indexing, e.g. `x[0] = 99`
135    ///
136    /// See also: [KotoObject::size]
137    fn index_mut(&mut self, index: &KValue, value: &KValue) -> Result<()> {
138        let _ = (index, value);
139        unimplemented_error("@index_mut", self.type_string())
140    }
141
142    /// Called when checking for the number of elements contained in the object
143    ///
144    /// The size should represent the maximum valid index that can be passed to
145    /// [`KotoObject::index`].
146    ///
147    /// The runtime defers to this function when the 'size' of an object is needed,
148    /// e.g. when `koto.size` is called, or when unpacking function arguments.
149    ///
150    /// The `Indexable` type hint will pass for objects with a defined size.
151    ///
152    /// See also: [`KotoObject::index`]
153    fn size(&self) -> Option<usize> {
154        None
155    }
156
157    /// Declares to the runtime whether or not the object is callable
158    ///
159    /// The `Callable` type hint defers to the function, expecting `true` to be returned for objects
160    /// that implement [`KotoObject::call`].
161    fn is_callable(&self) -> bool {
162        false
163    }
164
165    /// Allows the object to behave as a function
166    ///
167    /// Objects that implement `call` should return `true` from [`KotoObject::is_callable`].
168    fn call(&mut self, ctx: &mut CallContext) -> Result<KValue> {
169        let _ = ctx;
170        unimplemented_error("@||", self.type_string())
171    }
172
173    /// Defines the behavior of negation (e.g. `-x`)
174    fn negate(&self) -> Result<KValue> {
175        unimplemented_error("@negate", self.type_string())
176    }
177
178    /// The `+` addition operator
179    ///
180    /// This will be called by the runtime when the object is on the LHS.
181    ///
182    /// To specialize the behaviour of `+` when the object is on the RHS, see [Self::add_rhs].
183    fn add(&self, other: &KValue) -> Result<KValue> {
184        let _ = other;
185        unimplemented_error("@+", self.type_string())
186    }
187
188    /// The `+` addition operator when the object is on the RHS
189    ///
190    /// This will be called when the value on the LHS doesn't implement the operation.
191    fn add_rhs(&self, other: &KValue) -> Result<KValue> {
192        let _ = other;
193        unimplemented_error("@+", self.type_string())
194    }
195
196    /// The `-` subtraction operator
197    ///
198    /// This will be called by the runtime when the object is on the LHS of the operation,
199    /// or as a fallback if the value on the LHS doesn't support the operation.
200    ///
201    /// To specialize the behaviour of `-` when the object is on the RHS, see [Self::subtract_rhs].
202    fn subtract(&self, other: &KValue) -> Result<KValue> {
203        let _ = other;
204        unimplemented_error("@-", self.type_string())
205    }
206
207    /// The `-` subtraction operator when the object is on the RHS
208    ///
209    /// This will be called when the value on the LHS doesn't implement the operation.
210    fn subtract_rhs(&self, other: &KValue) -> Result<KValue> {
211        let _ = other;
212        unimplemented_error("@-", self.type_string())
213    }
214
215    /// The `*` multiplication operator
216    ///
217    /// This will be called by the runtime when the object is on the LHS.
218    ///
219    /// To specialize the behaviour of `*` when the object is on the RHS, see [Self::multiply_rhs].
220    fn multiply(&self, other: &KValue) -> Result<KValue> {
221        let _ = other;
222        unimplemented_error("@*", self.type_string())
223    }
224
225    /// The `*` multiplication operator when the object is on the RHS
226    ///
227    /// This will be called when the value on the LHS doesn't implement the operation.
228    fn multiply_rhs(&self, other: &KValue) -> Result<KValue> {
229        let _ = other;
230        unimplemented_error("@*", self.type_string())
231    }
232
233    /// The `/` division operator
234    fn divide(&self, other: &KValue) -> Result<KValue> {
235        let _ = other;
236        unimplemented_error("@/", self.type_string())
237    }
238
239    /// The `/` division operator when the object is on the RHS
240    ///
241    /// This will be called when the value on the LHS doesn't implement the operation.
242    fn divide_rhs(&self, other: &KValue) -> Result<KValue> {
243        let _ = other;
244        unimplemented_error("@/", self.type_string())
245    }
246
247    /// The `%` remainder operator
248    fn remainder(&self, other: &KValue) -> Result<KValue> {
249        let _ = other;
250        unimplemented_error("@%", self.type_string())
251    }
252
253    /// The `%` remainder operator when the object is on the RHS
254    ///
255    /// This will be called when the value on the LHS doesn't implement the operation.
256    fn remainder_rhs(&self, other: &KValue) -> Result<KValue> {
257        let _ = other;
258        unimplemented_error("@%", self.type_string())
259    }
260
261    /// The `^` power operator
262    fn power(&self, other: &KValue) -> Result<KValue> {
263        let _ = other;
264        unimplemented_error("@^", self.type_string())
265    }
266
267    /// The `^` power operator when the object is on the RHS
268    ///
269    /// This will be called when the value on the LHS doesn't implement the operation.
270    fn power_rhs(&self, other: &KValue) -> Result<KValue> {
271        let _ = other;
272        unimplemented_error("@^", self.type_string())
273    }
274
275    /// The `+=` in-place addition operator
276    fn add_assign(&mut self, other: &KValue) -> Result<()> {
277        let _ = other;
278        unimplemented_error("@+=", self.type_string())
279    }
280
281    /// The `-=` in-place subtraction operator
282    fn subtract_assign(&mut self, other: &KValue) -> Result<()> {
283        let _ = other;
284        unimplemented_error("@-=", self.type_string())
285    }
286
287    /// The `*=` in-place multiplication operator
288    fn multiply_assign(&mut self, other: &KValue) -> Result<()> {
289        let _ = other;
290        unimplemented_error("@*=", self.type_string())
291    }
292
293    /// The `/=` in-place division operator
294    fn divide_assign(&mut self, other: &KValue) -> Result<()> {
295        let _ = other;
296        unimplemented_error("@/=", self.type_string())
297    }
298
299    /// The `%=` in-place remainder operator
300    fn remainder_assign(&mut self, other: &KValue) -> Result<()> {
301        let _ = other;
302        unimplemented_error("@%=", self.type_string())
303    }
304
305    /// The `^=` in-place remainder operator
306    fn power_assign(&mut self, other: &KValue) -> Result<()> {
307        let _ = other;
308        unimplemented_error("@^=", self.type_string())
309    }
310
311    /// The `<` less-than operator
312    fn less(&self, other: &KValue) -> Result<bool> {
313        let _ = other;
314        unimplemented_error("@<", self.type_string())
315    }
316
317    /// The `<=` less-than-or-equal operator
318    ///
319    /// The default implementation derives its result from [Self::less] and [Self::equal].
320    fn less_or_equal(&self, other: &KValue) -> Result<bool> {
321        match self.less(other) {
322            Ok(true) => Ok(true),
323            Ok(false) => match self.equal(other) {
324                Ok(result) => Ok(result),
325                Err(error) if error.is_unimplemented_error() => {
326                    unimplemented_error("@<=", self.type_string())
327                }
328                error => error,
329            },
330            Err(error) if error.is_unimplemented_error() => {
331                unimplemented_error("@<=", self.type_string())
332            }
333            error => error,
334        }
335    }
336
337    /// The `>` greater-than operator
338    ///
339    /// The default implementation derives its result from [Self::less] and [Self::equal].
340    fn greater(&self, other: &KValue) -> Result<bool> {
341        match self.less(other) {
342            Ok(true) => Ok(false),
343            Ok(false) => match self.equal(other) {
344                Ok(result) => Ok(!result),
345                Err(error) if error.is_unimplemented_error() => {
346                    unimplemented_error("@>", self.type_string())
347                }
348                error => error,
349            },
350            Err(error) if error.is_unimplemented_error() => {
351                unimplemented_error("@>", self.type_string())
352            }
353            error => error,
354        }
355    }
356
357    /// The `>=` greater-than-or-equal operator
358    ///
359    /// The default implementation derives its result from [Self::less].
360    fn greater_or_equal(&self, other: &KValue) -> Result<bool> {
361        match self.less(other) {
362            Ok(result) => Ok(!result),
363            Err(error) if error.is_unimplemented_error() => {
364                unimplemented_error("@>=", self.type_string())
365            }
366            error => error,
367        }
368    }
369
370    /// The `==` equality operator
371    fn equal(&self, other: &KValue) -> Result<bool> {
372        let _ = other;
373        unimplemented_error("@==", self.type_string())
374    }
375
376    /// The `!=` inequality operator
377    ///
378    /// The default implementation derives its result from [Self::equal].
379    fn not_equal(&self, other: &KValue) -> Result<bool> {
380        match self.equal(other) {
381            Ok(result) => Ok(!result),
382            Err(error) if error.is_unimplemented_error() => {
383                unimplemented_error("@!=", self.type_string())
384            }
385            error => error,
386        }
387    }
388
389    /// Declares to the runtime whether or not the object is iterable
390    ///
391    /// The `Iterable` type hint defers to this function,
392    /// accepting anything other than `IsIterable::NotIterable`.
393    fn is_iterable(&self) -> IsIterable {
394        IsIterable::NotIterable
395    }
396
397    /// Returns an iterator that iterates over the objects contents
398    ///
399    /// If [`IsIterable::Iterable`] is returned from [`is_iterable`](Self::is_iterable),
400    /// then the runtime will call this function when the object is used in iterable contexts,
401    /// expecting a [`KIterator`] to be returned.
402    fn make_iterator(&self, vm: &mut KotoVm) -> Result<KIterator> {
403        let _ = vm;
404        unimplemented_error("@iterator", self.type_string())
405    }
406
407    /// Gets the object's next value in an iteration
408    ///
409    /// If either [`ForwardIterator`][IsIterable::ForwardIterator] or
410    /// [`BidirectionalIterator`][IsIterable::BidirectionalIterator] is returned from
411    /// [is_iterable](Self::is_iterable), then the object will be wrapped in a [`KIterator`]
412    /// whenever it's used in an iterable context. This function will then be called each time
413    /// [`KIterator::next`] is invoked.
414    fn iterator_next(&mut self, vm: &mut KotoVm) -> Option<KIteratorOutput> {
415        let _ = vm;
416        None
417    }
418
419    /// Gets the object's next value from the end of an iteration
420    ///
421    /// If [`BidirectionalIterator`][IsIterable::BidirectionalIterator] is returned from
422    /// [`is_iterable`](Self::is_iterable), then the object will be wrapped in a [`KIterator`]
423    /// whenever it's used in an iterable context. This function will then be called each time
424    /// [`KIterator::next_back`] is invoked.
425    fn iterator_next_back(&mut self, vm: &mut KotoVm) -> Option<KIteratorOutput> {
426        let _ = vm;
427        None
428    }
429
430    /// Converts the object into a serializable [KValue]
431    ///
432    /// This is called by `koto_serde`'s serialize implementation when the object is encountered
433    /// during serialization.
434    ///
435    /// The object should prepare a [KValue] that best represents the object's properties.
436    fn serialize(&self) -> Result<KValue> {
437        unimplemented_error("serialize", self.type_string())
438    }
439}
440
441/// A [`KotoObject`] wrapper used in the Koto runtime
442#[derive(Clone)]
443pub struct KObject {
444    object: PtrMut<dyn KotoObject>,
445}
446
447impl KObject {
448    /// Checks if the object is of the given type
449    pub fn is_a<T: KotoObject>(&self) -> bool {
450        match self.object.try_borrow() {
451            Some(object) => (object.deref() as &dyn Any).is::<T>(),
452            None => false,
453        }
454    }
455
456    /// Attempts to borrow the underlying object immutably
457    pub fn try_borrow(&self) -> Result<Borrow<'_, dyn KotoObject>> {
458        self.object
459            .try_borrow()
460            .ok_or_else(|| ErrorKind::UnableToBorrowObject.into())
461    }
462
463    /// Attempts to borrow the underlying object mutably
464    pub fn try_borrow_mut(&self) -> Result<BorrowMut<'_, dyn KotoObject>> {
465        self.object
466            .try_borrow_mut()
467            .ok_or_else(|| ErrorKind::UnableToBorrowObject.into())
468    }
469
470    /// Attempts to immutably borrow and cast the underlying object to the specified type
471    pub fn cast<T: KotoObject>(&self) -> Result<Borrow<'_, T>> {
472        Borrow::filter_map(self.try_borrow()?, |object| {
473            (object as &dyn Any).downcast_ref::<T>()
474        })
475        .map_err(|_| match self.try_borrow() {
476            Ok(object) => ErrorKind::UnexpectedObjectType {
477                expected: T::type_static(),
478                unexpected: object.type_string(),
479            }
480            .into(),
481            Err(e) => e,
482        })
483    }
484
485    /// Attempts to mutably borrow and cast the underlying object to the specified type
486    pub fn cast_mut<T: KotoObject>(&self) -> Result<BorrowMut<'_, T>> {
487        BorrowMut::filter_map(self.try_borrow_mut()?, |object| {
488            (object as &mut dyn Any).downcast_mut::<T>()
489        })
490        .map_err(|_| match self.try_borrow() {
491            Ok(object) => ErrorKind::UnexpectedObjectType {
492                expected: T::type_static(),
493                unexpected: object.type_string(),
494            }
495            .into(),
496            Err(e) => e,
497        })
498    }
499
500    /// Returns true if the provided object occupies the same memory address
501    pub fn is_same_instance(&self, other: &Self) -> bool {
502        PtrMut::ptr_eq(&self.object, &other.object)
503    }
504
505    /// Returns the number of references currently held to the object
506    pub fn ref_count(&self) -> usize {
507        PtrMut::ref_count(&self.object)
508    }
509}
510
511impl<T: KotoObject> From<T> for KObject {
512    fn from(object: T) -> Self {
513        Self {
514            object: make_ptr_mut!(object),
515        }
516    }
517}
518
519impl fmt::Debug for KObject {
520    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
521        write!(f, "KObject ({:?})", PtrMut::address(&self.object))
522    }
523}
524
525/// A trait that represents the basic requirements of fields in a type that implements [`KotoObject`]
526///
527/// This is useful for reducing repetitive duplication in bounds when implementing a generic
528/// [KotoObject] type.
529pub trait KotoField: Clone + KotoSend + KotoSync + 'static {}
530impl<T> KotoField for T where T: Clone + KotoSend + KotoSync + 'static {}
531
532/// Context provided to a function that implements an object method
533///
534/// This is used by the `#[koto_impl]` macro when generating wrappers for functions tagged with
535/// `#[koto_method]`. A native function is called with a [CallContext], and for functions that
536/// implement object methods a [MethodContext] is produced when the first call argument is a
537/// [KObject].
538pub struct MethodContext<'a, T> {
539    /// The method call arguments
540    pub args: &'a [KValue],
541    /// A VM that can be used by the method for operations that require a runtime
542    //
543    // Q. Why isn't this a mutable reference like in CallContext?
544    // A. Because the arguments (including the object instance) have already been retrieved by
545    //    reference from the VM, disallowing a mutable reference.
546    pub vm: &'a KotoVm,
547    // The instance of the object for the method call,
548    // accessible via the context's `instance`/`instance_mut` functions
549    object: &'a KObject,
550    // We want to be able to cast to `T`.
551    _phantom: PhantomData<T>,
552}
553
554impl<'a, T: KotoObject> MethodContext<'a, T> {
555    /// Makes a new method context
556    pub fn new(object: &'a KObject, args: &'a [KValue], vm: &'a KotoVm) -> Self {
557        Self {
558            object,
559            args,
560            vm,
561            _phantom: PhantomData,
562        }
563    }
564
565    /// Attempts to immutably borrow the object instance
566    pub fn instance(&self) -> Result<Borrow<'_, T>> {
567        self.object.cast::<T>()
568    }
569
570    /// Attempts to mutably borrow the object instance
571    pub fn instance_mut(&self) -> Result<BorrowMut<'_, T>> {
572        self.object.cast_mut::<T>()
573    }
574
575    /// Returns a clone of the instance as a [KValue]
576    ///
577    /// This is useful for builder methods.
578    /// e.g.
579    ///
580    /// ```koto
581    /// make_foo()
582    ///   .set_x 99
583    ///   .set_y 123
584    /// ```
585    ///
586    /// Here `set_x` and `set_y` would use `instance_result` to allow the builder chain to continue.
587    pub fn instance_result(&self) -> Result<KValue> {
588        Ok(self.object.clone().into())
589    }
590}
591
592/// Creates an error that describes an unimplemented method
593fn unimplemented_error<T>(fn_name: &'static str, object_type: KString) -> Result<T> {
594    runtime_error!(ErrorKind::Unimplemented {
595        fn_name,
596        object_type
597    })
598}
599
600/// An enum that indicates to the runtime if a [`KotoObject`] is iterable
601pub enum IsIterable {
602    /// The object is not iterable
603    NotIterable,
604    /// The object is iterable
605    ///
606    /// An iterable object is not itself an iterator, but provides an implementation of
607    /// [KotoObject::make_iterator] that is used to make an iterator when one is needed by the
608    /// runtime.
609    Iterable,
610    /// The object is a forward-only iterator
611    ///
612    /// A forward iterator provides an implementation of [KotoObject::iterator_next],
613    /// but not [KotoObject::iterator_next_back].
614    ForwardIterator,
615    /// The object is a bidirectional iterator.
616    ///
617    /// A bidirectional iterator provides an implementation of [KotoObject::iterator_next] and
618    /// [KotoObject::iterator_next_back].
619    BidirectionalIterator,
620}