diff --git a/crates/rune-modules/src/http.rs b/crates/rune-modules/src/http.rs index 428698d89..12682d2a2 100644 --- a/crates/rune-modules/src/http.rs +++ b/crates/rune-modules/src/http.rs @@ -49,7 +49,7 @@ //! ``` use rune::{Any, Module, Value, ContextError}; -use rune::runtime::{Bytes, Protocol, Ref}; +use rune::runtime::{Bytes, Ref, Formatter}; use std::fmt; use std::fmt::Write; @@ -77,8 +77,8 @@ pub fn module(_stdio: bool) -> Result { module.function_meta(RequestBuilder::header)?; module.function_meta(RequestBuilder::body_bytes)?; - module.associated_function(Protocol::STRING_DISPLAY, Error::display)?; - module.associated_function(Protocol::STRING_DISPLAY, StatusCode::display)?; + module.function_meta(Error::string_display)?; + module.function_meta(StatusCode::string_display)?; Ok(module) } @@ -95,8 +95,9 @@ impl From for Error { } impl Error { - fn display(&self, buf: &mut String) -> fmt::Result { - write!(buf, "{}", self.inner) + #[rune::function(instance, protocol = STRING_DISPLAY)] + fn string_display(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{}", self.inner) } } @@ -144,8 +145,9 @@ pub struct StatusCode { } impl StatusCode { - fn display(&self, buf: &mut String) -> fmt::Result { - write!(buf, "{}", self.inner) + #[rune::function(instance, protocol = STRING_DISPLAY)] + fn string_display(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{}", self.inner) } } diff --git a/crates/rune-modules/src/process.rs b/crates/rune-modules/src/process.rs index 93b33c802..0136e8fb1 100644 --- a/crates/rune-modules/src/process.rs +++ b/crates/rune-modules/src/process.rs @@ -30,7 +30,7 @@ //! ``` use rune::{Any, Module, ContextError}; -use rune::runtime::{Bytes, Shared, Value, Protocol, VmResult}; +use rune::runtime::{Bytes, Shared, Value, VmResult, Formatter}; use std::fmt; use std::io; use tokio::process; @@ -43,13 +43,13 @@ pub fn module(_stdio: bool) -> Result { module.ty::()?; module.ty::()?; - module.function(["Command", "new"], Command::new)?; - module.associated_function("spawn", Command::spawn)?; - module.associated_function("arg", Command::arg)?; - module.associated_function("args", Command::args)?; - module.associated_function("wait_with_output", Child::wait_with_output)?; - module.associated_function(Protocol::STRING_DISPLAY, ExitStatus::display)?; - module.associated_function("code", ExitStatus::code)?; + module.function_meta(Command::new)?; + module.function_meta(Command::spawn)?; + module.function_meta(Command::arg)?; + module.function_meta(Command::args)?; + module.function_meta(Child::wait_with_output)?; + module.function_meta(ExitStatus::string_display)?; + module.function_meta(ExitStatus::code)?; Ok(module) } @@ -61,6 +61,7 @@ struct Command { impl Command { /// Construct a new command. + #[rune::function(path = Self::new)] fn new(command: &str) -> Self { Self { inner: process::Command::new(command), @@ -68,6 +69,7 @@ impl Command { } /// Add arguments. + #[rune::function(instance)] fn args(&mut self, args: &[Value]) -> VmResult<()> { for arg in args { match arg { @@ -84,11 +86,13 @@ impl Command { } /// Add an argument. + #[rune::function(instance)] fn arg(&mut self, arg: &str) { self.inner.arg(arg); } /// Spawn the command. + #[rune::function(instance)] fn spawn(mut self) -> io::Result { Ok(Child { inner: Some(self.inner.spawn()?), @@ -109,6 +113,7 @@ struct Child { impl Child { // Returns a future that will resolve to an Output, containing the exit // status, stdout, and stderr of the child process. + #[rune::function(instance)] async fn wait_with_output(self) -> VmResult> { let inner = match self.inner { Some(inner) => inner, @@ -148,11 +153,13 @@ struct ExitStatus { } impl ExitStatus { - fn display(&self, buf: &mut String) -> fmt::Result { + #[rune::function(protocol = STRING_DISPLAY)] + fn string_display(&self, f: &mut Formatter) -> fmt::Result { use std::fmt::Write as _; - write!(buf, "{}", self.status) + write!(f, "{}", self.status) } + #[rune::function] fn code(&self) -> Option { self.status.code() } diff --git a/crates/rune/src/lib.rs b/crates/rune/src/lib.rs index 6555fe464..a30042ca1 100644 --- a/crates/rune/src/lib.rs +++ b/crates/rune/src/lib.rs @@ -431,6 +431,7 @@ pub(crate) use rune_macros::__internal_impl_any; /// /// ``` /// use rune::{Any, Module, ContextError}; +/// use rune::runtime::Formatter; /// use std::fmt::{self, Write}; /// /// #[derive(Any)] @@ -471,8 +472,8 @@ pub(crate) use rune_macros::__internal_impl_any; /// /// assert_eq!(format!("{}", string), "hello"); /// /// ``` /// #[rune::function(protocol = STRING_DISPLAY)] -/// fn display(&self, buffer: &mut std::string::String) -> fmt::Result { -/// write!(buffer, "{}", self.inner) +/// fn display(&self, f: &mut Formatter) -> fmt::Result { +/// write!(f, "{}", self.inner) /// } /// } /// diff --git a/crates/rune/src/modules/any.rs b/crates/rune/src/modules/any.rs index 27ea0c539..2790da49c 100644 --- a/crates/rune/src/modules/any.rs +++ b/crates/rune/src/modules/any.rs @@ -5,7 +5,7 @@ use core::fmt::{self, Write}; use crate::no_std::prelude::*; use crate as rune; -use crate::runtime::{Type, Value, VmResult}; +use crate::runtime::{Formatter, Type, Value, VmResult}; use crate::{ContextError, Module}; /// Utilities for dynamic typing or type reflection. @@ -53,8 +53,8 @@ fn type_of_val(value: Value) -> VmResult { /// assert_eq!(format!("{}", any::Type::of_val(42)), "Type(0x1cad9186c9641c4f)"); /// ``` #[rune::function(instance, protocol = STRING_DISPLAY)] -fn format_type(ty: Type, buf: &mut String) -> fmt::Result { - write!(buf, "{:?}", ty) +fn format_type(ty: Type, f: &mut Formatter) -> fmt::Result { + write!(f, "{:?}", ty) } /// Get the type name of a value. diff --git a/crates/rune/src/modules/cmp.rs b/crates/rune/src/modules/cmp.rs index e27910436..d06b5f98e 100644 --- a/crates/rune/src/modules/cmp.rs +++ b/crates/rune/src/modules/cmp.rs @@ -4,8 +4,7 @@ use core::cmp::Ordering; use core::fmt::{self, Write}; use crate as rune; -use crate::no_std::prelude::*; -use crate::runtime::{Protocol, Value, VmResult}; +use crate::runtime::{Formatter, Protocol, Value, VmResult}; use crate::{ContextError, Module}; /// Construct the `std::cmp` module. @@ -115,6 +114,6 @@ fn min(v1: Value, v2: Value) -> VmResult { /// assert_eq!(format!("{:?}", Ordering::Less), "Less"); /// ``` #[rune::function(instance, protocol = STRING_DEBUG)] -fn ordering_string_debug(this: Ordering, s: &mut String) -> fmt::Result { +fn ordering_string_debug(this: Ordering, s: &mut Formatter) -> fmt::Result { write!(s, "{:?}", this) } diff --git a/crates/rune/src/modules/collections/hash_map.rs b/crates/rune/src/modules/collections/hash_map.rs index a7ab4c38f..95897276d 100644 --- a/crates/rune/src/modules/collections/hash_map.rs +++ b/crates/rune/src/modules/collections/hash_map.rs @@ -5,7 +5,8 @@ use crate::no_std::prelude::*; use crate as rune; use crate::runtime::{ - EnvProtocolCaller, FromValue, Iterator, Key, ProtocolCaller, Value, VmErrorKind, VmResult, + EnvProtocolCaller, Formatter, FromValue, Iterator, Key, ProtocolCaller, Value, VmErrorKind, + VmResult, }; use crate::{Any, ContextError, Module}; @@ -455,32 +456,32 @@ impl HashMap { /// assert_eq!(format!("{:?}", map), "{1: \"a\"}"); /// ``` #[rune::function(protocol = STRING_DEBUG)] - fn string_debug(&self, s: &mut String) -> VmResult { - self.string_debug_with(s, &mut EnvProtocolCaller) + fn string_debug(&self, f: &mut Formatter) -> VmResult { + self.string_debug_with(f, &mut EnvProtocolCaller) } pub(crate) fn string_debug_with( &self, - s: &mut String, + f: &mut Formatter, caller: &mut impl ProtocolCaller, ) -> VmResult { - vm_write!(s, "{{"); + vm_write!(f, "{{"); let mut it = self.map.iter().peekable(); while let Some((key, value)) = it.next() { - vm_write!(s, "{:?}: ", key); + vm_write!(f, "{:?}: ", key); - if let Err(fmt::Error) = vm_try!(value.string_debug_with(s, caller)) { + if let Err(fmt::Error) = vm_try!(value.string_debug_with(f, caller)) { return VmResult::Ok(Err(fmt::Error)); } if it.peek().is_some() { - vm_write!(s, ", "); + vm_write!(f, ", "); } } - vm_write!(s, "}}"); + vm_write!(f, "}}"); VmResult::Ok(Ok(())) } diff --git a/crates/rune/src/modules/collections/hash_set.rs b/crates/rune/src/modules/collections/hash_set.rs index 4d4f1e425..7de74ea43 100644 --- a/crates/rune/src/modules/collections/hash_set.rs +++ b/crates/rune/src/modules/collections/hash_set.rs @@ -3,10 +3,10 @@ use core::iter; use crate as rune; use crate::no_std::collections; -use crate::no_std::prelude::*; use crate::runtime::{ - EnvProtocolCaller, Iterator, IteratorTrait, Key, Protocol, ProtocolCaller, Ref, Value, VmResult, + EnvProtocolCaller, Formatter, Iterator, IteratorTrait, Key, ProtocolCaller, Ref, Value, + VmResult, }; use crate::{Any, ContextError, Module}; @@ -28,10 +28,10 @@ pub(super) fn setup(module: &mut Module) -> Result<(), ContextError> { module.function_meta(HashSet::iter)?; module.function_meta(clone)?; module.function_meta(from)?; - module.associated_function(Protocol::INTO_ITER, HashSet::__rune_fn__iter)?; + module.function_meta(HashSet::into_iter)?; module.function_meta(HashSet::string_debug)?; - module.associated_function(Protocol::PARTIAL_EQ, HashSet::partial_eq)?; - module.associated_function(Protocol::EQ, HashSet::eq)?; + module.function_meta(HashSet::partial_eq)?; + module.function_meta(HashSet::eq)?; Ok(()) } @@ -301,6 +301,18 @@ impl HashSet { VmResult::Ok(Iterator::from("std::collections::set::Union", iter)) } + /// Iterate over the hash set. + /// + /// # Examples + /// + /// ```rune + /// use std::collections::HashSet; + /// + /// let set = HashSet::from([3, 2, 1]); + /// let vec = set.iter().collect::(); + /// vec.sort(); + /// assert_eq!(vec, [1, 2, 3]); + /// ``` #[rune::function] fn iter(&self) -> Iterator { let iter = self.set.clone().into_iter(); @@ -320,6 +332,28 @@ impl HashSet { VmResult::Ok(()) } + /// Convert the set into an iterator. + /// + /// # Examples + /// + /// ```rune + /// use std::collections::HashSet; + /// + /// let set = HashSet::from([3, 2, 1]); + /// let vec = []; + /// + /// for value in set { + /// vec.push(value); + /// } + /// + /// vec.sort(); + /// assert_eq!(vec, [1, 2, 3]); + /// ``` + #[rune::function(protocol = INTO_ITER)] + fn into_iter(&self) -> Iterator { + self.__rune_fn__iter() + } + /// Write a debug representation to a string. /// /// This calls the [`STRING_DEBUG`] protocol over all elements of the @@ -334,28 +368,28 @@ impl HashSet { /// println!("{:?}", set); /// ``` #[rune::function(protocol = STRING_DEBUG)] - fn string_debug(&self, s: &mut String) -> VmResult { - self.string_debug_with(s, &mut EnvProtocolCaller) + fn string_debug(&self, f: &mut Formatter) -> VmResult { + self.string_debug_with(f, &mut EnvProtocolCaller) } fn string_debug_with( &self, - s: &mut String, + f: &mut Formatter, _: &mut impl ProtocolCaller, ) -> VmResult { - vm_write!(s, "{{"); + vm_write!(f, "{{"); let mut it = self.set.iter().peekable(); while let Some(value) = it.next() { - vm_write!(s, "{:?}", value); + vm_write!(f, "{:?}", value); if it.peek().is_some() { - vm_write!(s, ", "); + vm_write!(f, ", "); } } - vm_write!(s, "}}"); + vm_write!(f, "}}"); VmResult::Ok(Ok(())) } @@ -369,10 +403,37 @@ impl HashSet { VmResult::Ok(HashSet { set }) } + /// Perform a partial equality test between two sets. + /// + /// # Examples + /// + /// # Examples + /// + /// ```rune + /// use std::collections::HashSet; + /// + /// let set = HashSet::from([1, 2, 3]); + /// assert_eq!(set, HashSet::from([1, 2, 3])); + /// assert_ne!(set, HashSet::from([2, 3, 4])); + /// ``` + #[rune::function(protocol = PARTIAL_EQ)] fn partial_eq(&self, other: &Self) -> bool { self.set == other.set } + /// Perform a total equality test between two sets. + /// + /// # Examples + /// + /// ```rune + /// use std::ops::eq; + /// use std::collections::HashSet; + /// + /// let set = HashSet::from([1, 2, 3]); + /// assert!(eq(set, HashSet::from([1, 2, 3]))); + /// assert!(!eq(set, HashSet::from([2, 3, 4]))); + /// ``` + #[rune::function(protocol = EQ)] fn eq(&self, other: &Self) -> bool { self.set == other.set } diff --git a/crates/rune/src/modules/collections/vec_deque.rs b/crates/rune/src/modules/collections/vec_deque.rs index a734a609b..8a85797f3 100644 --- a/crates/rune/src/modules/collections/vec_deque.rs +++ b/crates/rune/src/modules/collections/vec_deque.rs @@ -3,10 +3,9 @@ use core::fmt::{self, Write}; use crate as rune; use crate::no_std::collections; -use crate::no_std::prelude::*; use crate::runtime::{ - EnvProtocolCaller, Iterator, Protocol, ProtocolCaller, Value, VmErrorKind, VmResult, + EnvProtocolCaller, Formatter, Iterator, Protocol, ProtocolCaller, Value, VmErrorKind, VmResult, }; use crate::{Any, ContextError, Module}; @@ -520,31 +519,31 @@ impl VecDeque { /// assert_eq!(format!("{:?}", deque), "[1, 2, 3]"); /// ``` #[rune::function(protocol = STRING_DEBUG)] - fn string_debug(&self, s: &mut String) -> VmResult { - self.string_debug_with(s, &mut EnvProtocolCaller) + fn string_debug(&self, f: &mut Formatter) -> VmResult { + self.string_debug_with(f, &mut EnvProtocolCaller) } #[inline] fn string_debug_with( &self, - s: &mut String, + f: &mut Formatter, caller: &mut impl ProtocolCaller, ) -> VmResult { let mut it = self.inner.iter().peekable(); - vm_write!(s, "["); + vm_write!(f, "["); while let Some(value) = it.next() { - if let Err(fmt::Error) = vm_try!(value.string_debug_with(s, caller)) { + if let Err(fmt::Error) = vm_try!(value.string_debug_with(f, caller)) { return VmResult::Ok(Err(fmt::Error)); } if it.peek().is_some() { - vm_write!(s, ", "); + vm_write!(f, ", "); } } - vm_write!(s, "]"); + vm_write!(f, "]"); VmResult::Ok(Ok(())) } diff --git a/crates/rune/src/modules/fmt.rs b/crates/rune/src/modules/fmt.rs index e48ae92ec..90696bec1 100644 --- a/crates/rune/src/modules/fmt.rs +++ b/crates/rune/src/modules/fmt.rs @@ -2,28 +2,27 @@ use core::fmt::{self, Write}; -use crate::no_std::prelude::*; - use crate as rune; use crate::compile; use crate::macros::{FormatArgs, MacroContext, TokenStream}; use crate::parse::Parser; -use crate::runtime::{Format, Protocol}; +use crate::runtime::{Format, Formatter}; use crate::{ContextError, Module}; /// Construct the `std::fmt` module. pub fn module() -> Result { let mut module = Module::with_crate_item("std", ["fmt"]).with_unique("std::fmt"); + module.ty::()?; + module.ty::()?; module.ty::()?; - module.associated_function(Protocol::STRING_DISPLAY, format_fmt_error)?; + module.function_meta(fmt_error_string_display)?; module.macro_meta(format)?; - - module.ty::()?; Ok(module) } -fn format_fmt_error(error: &fmt::Error, buf: &mut String) -> fmt::Result { - write!(buf, "{}", error) +#[rune::function(instance, protocol = STRING_DISPLAY)] +fn fmt_error_string_display(error: &fmt::Error, f: &mut Formatter) -> fmt::Result { + write!(f, "{}", error) } /// Format a string using a format specifier. diff --git a/crates/rune/src/modules/io.rs b/crates/rune/src/modules/io.rs index 7968607a1..9b9236cbf 100644 --- a/crates/rune/src/modules/io.rs +++ b/crates/rune/src/modules/io.rs @@ -3,13 +3,11 @@ use std::fmt::{self, Write as _}; use std::io::{self, Write as _}; -use crate::no_std::prelude::*; - use crate as rune; use crate::compile; use crate::macros::{quote, FormatArgs, MacroContext, TokenStream}; use crate::parse::Parser; -use crate::runtime::{Panic, Protocol, Stack, Value, VmResult}; +use crate::runtime::{Formatter, Panic, Stack, Value, VmResult}; use crate::{ContextError, Module}; /// Construct the `std::io` module. @@ -33,7 +31,7 @@ pub fn module(stdio: bool) -> Result { ]); module.ty::()?; - module.associated_function(Protocol::STRING_DISPLAY, format_io_error)?; + module.function_meta(io_error_string_display)?; if stdio { module.function_meta(print_impl)?; @@ -68,8 +66,9 @@ pub fn module(stdio: bool) -> Result { Ok(module) } -fn format_io_error(error: &io::Error, buf: &mut String) -> fmt::Result { - write!(buf, "{}", error) +#[rune::function(instance, protocol = STRING_DISPLAY)] +fn io_error_string_display(error: &io::Error, f: &mut Formatter) -> fmt::Result { + write!(f, "{}", error) } fn dbg_impl(stack: &mut Stack, args: usize) -> VmResult<()> { diff --git a/crates/rune/src/modules/option.rs b/crates/rune/src/modules/option.rs index bbe2bb10e..7ecaddd45 100644 --- a/crates/rune/src/modules/option.rs +++ b/crates/rune/src/modules/option.rs @@ -3,8 +3,7 @@ use core::fmt; use crate as rune; -use crate::no_std::prelude::*; -use crate::runtime::{Function, Iterator, Panic, Shared, Value, VmResult}; +use crate::runtime::{Formatter, Function, Iterator, Panic, Shared, Value, VmResult}; use crate::{ContextError, Module}; /// Construct the `std::option` module. @@ -72,14 +71,13 @@ fn expect(option: Option, message: Value) -> VmResult { match option { Some(some) => VmResult::Ok(some), None => { - let mut s = String::new(); - let mut buf = String::new(); + let mut f = Formatter::new(); - if let Err(fmt::Error) = vm_try!(message.string_display(&mut s, &mut buf)) { + if let Err(fmt::Error) = vm_try!(message.string_display(&mut f)) { return VmResult::err(Panic::msg("Failed to format message")); } - VmResult::err(Panic::custom(s)) + VmResult::err(Panic::custom(f.into_string())) } } } diff --git a/crates/rune/src/modules/result.rs b/crates/rune/src/modules/result.rs index a51afab54..ab0733f15 100644 --- a/crates/rune/src/modules/result.rs +++ b/crates/rune/src/modules/result.rs @@ -3,8 +3,7 @@ use core::fmt; use crate as rune; -use crate::no_std::prelude::*; -use crate::runtime::{Function, Panic, Value, VmResult}; +use crate::runtime::{Formatter, Function, Panic, Value, VmResult}; use crate::{ContextError, Module}; /// Construct the `std::result` module. @@ -193,20 +192,19 @@ fn expect(result: Result, message: Value) -> VmResult { match result { Ok(value) => VmResult::Ok(value), Err(err) => { - let mut s = String::new(); - let mut buf = String::new(); + let mut f = Formatter::new(); - if let Err(fmt::Error) = vm_try!(message.string_display(&mut s, &mut buf)) { + if let Err(fmt::Error) = vm_try!(message.string_display(&mut f)) { return VmResult::err(Panic::msg("Failed to format message")); } - s.push_str(": "); + f.push_str(": "); - if let Err(fmt::Error) = vm_try!(err.string_debug(&mut s)) { + if let Err(fmt::Error) = vm_try!(err.string_debug(&mut f)) { return VmResult::err(Panic::msg("Failed to format error")); } - VmResult::err(Panic::custom(s)) + VmResult::err(Panic::custom(f.into_string())) } } } diff --git a/crates/rune/src/modules/string.rs b/crates/rune/src/modules/string.rs index cd29571fc..f577acaa6 100644 --- a/crates/rune/src/modules/string.rs +++ b/crates/rune/src/modules/string.rs @@ -10,7 +10,7 @@ use alloc::string::FromUtf8Error; use crate::no_std::prelude::*; use crate as rune; -use crate::runtime::{Bytes, Iterator, Panic, Protocol, Value, VmErrorKind, VmResult}; +use crate::runtime::{Bytes, Formatter, Iterator, Panic, Protocol, Value, VmErrorKind, VmResult}; use crate::{Any, ContextError, Module}; /// Construct the `std::string` module. @@ -66,12 +66,13 @@ pub fn module() -> Result { struct NotCharBoundary(()); impl NotCharBoundary { - fn string_display(&self, s: &mut String) -> fmt::Result { - write!(s, "index outside of character boundary") + #[rune::function(instance, protocol = STRING_DISPLAY)] + fn string_display(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "index outside of character boundary") } fn install(m: &mut Module) -> Result<(), ContextError> { - m.associated_function(Protocol::STRING_DISPLAY, Self::string_display)?; + m.function_meta(Self::string_display)?; Ok(()) } } diff --git a/crates/rune/src/modules/vec.rs b/crates/rune/src/modules/vec.rs index ff99d6a24..47eee82f3 100644 --- a/crates/rune/src/modules/vec.rs +++ b/crates/rune/src/modules/vec.rs @@ -4,9 +4,9 @@ use core::cmp::Ordering; use core::fmt; use crate as rune; -use crate::no_std::prelude::*; use crate::runtime::{ - EnvProtocolCaller, Function, Iterator, Protocol, Ref, Value, Vec, VmErrorKind, VmResult, + EnvProtocolCaller, Formatter, Function, Iterator, Ref, TypeOf, Value, Vec, VmErrorKind, + VmResult, }; use crate::{ContextError, Module}; @@ -47,7 +47,8 @@ pub fn module() -> Result { m.function_meta(sort_by)?; m.function_meta(sort)?; m.function_meta(into_iter)?; - m.associated_function(Protocol::INDEX_SET, Vec::set)?; + m.function_meta(index_set)?; + m.function_meta(index_get)?; m.function_meta(string_debug)?; m.function_meta(partial_eq)?; m.function_meta(eq)?; @@ -476,6 +477,60 @@ fn into_iter(this: Ref) -> Iterator { Vec::iter_ref(Ref::map(this, |vec| &**vec)) } +/// Returns a reference to an element or subslice depending on the type of +/// index. +/// +/// - If given a position, returns a reference to the element at that position +/// or `None` if out of bounds. +/// - If given a range, returns the subslice corresponding to that range, or +/// `None` if out of bounds. +/// +/// # Panics +/// +/// Panics if the specified `index` is out of range. +/// +/// ```rune,should_panic +/// let v = [10, 40, 30]; +/// assert_eq!(None, v[1..4]); +/// ``` +/// +/// ```rune,should_panic +/// let v = [10, 40, 30]; +/// assert_eq!(None, v[3]); +/// ``` +/// +/// # Examples +/// +/// ```rune +/// let v = [10, 40, 30]; +/// assert_eq!(40, v[1]); +/// assert_eq!([10, 40], v[0..2]); +/// ``` +#[rune::function(instance, protocol = INDEX_GET)] +fn index_get(this: &Vec, index: Value) -> VmResult { + let Some(value) = vm_try!(Vec::index_get(this, index)) else { + return VmResult::err(VmErrorKind::MissingIndex { + target: Vec::type_info(), + }); + }; + + VmResult::Ok(value) +} + +/// Inserts a value into the vector. +/// +/// # Examples +/// +/// ```rune +/// let vec = [1, 2, 3]; +/// vec[0] = "a"; +/// assert_eq!(vec, ["a", 2, 3]); +/// ``` +#[rune::function(instance, protocol = INDEX_SET)] +fn index_set(this: &mut Vec, index: usize, value: Value) -> VmResult<()> { + Vec::set(this, index, value) +} + /// Write a debug representation to a string. /// /// This calls the [`STRING_DEBUG`] protocol over all elements of the @@ -488,8 +543,8 @@ fn into_iter(this: Ref) -> Iterator { /// assert_eq!(format!("{:?}", vec), "[1, 2, 3]"); /// ``` #[rune::function(instance, protocol = STRING_DEBUG)] -fn string_debug(this: &Vec, s: &mut String) -> VmResult { - Vec::string_debug_with(this, s, &mut EnvProtocolCaller) +fn string_debug(this: &Vec, f: &mut Formatter) -> VmResult { + Vec::string_debug_with(this, f, &mut EnvProtocolCaller) } /// Perform a partial equality check with this vector. diff --git a/crates/rune/src/runtime.rs b/crates/rune/src/runtime.rs index 72eb9e2bc..a1c96b28d 100644 --- a/crates/rune/src/runtime.rs +++ b/crates/rune/src/runtime.rs @@ -176,3 +176,6 @@ pub use self::vm_execution::{ExecutionState, VmExecution, VmSendExecution}; mod vm_halt; pub(crate) use self::vm_halt::VmHalt; pub use self::vm_halt::VmHaltInfo; + +mod fmt; +pub use self::fmt::Formatter; diff --git a/crates/rune/src/runtime/fmt.rs b/crates/rune/src/runtime/fmt.rs new file mode 100644 index 000000000..ae5d06884 --- /dev/null +++ b/crates/rune/src/runtime/fmt.rs @@ -0,0 +1,82 @@ +use core::fmt; + +use crate as rune; + +use crate::no_std::string::String; +use crate::Any; + +/// A formatter for the rune virtual machine. +/// +/// This is used as a receiver to functions implementing the [`STRING_DEBUG`] +/// and [`STRING_DISPLAY`] protocols. +/// +/// [`STRING_DEBUG`]: crate::runtime::Protocol::STRING_DEBUG +/// [`STRING_DISPLAY`]: crate::runtime::Protocol::STRING_DISPLAY +#[derive(Any, Default)] +#[rune(item = ::std::fmt)] +pub struct Formatter { + pub(crate) string: String, + pub(crate) buf: String, +} + +impl Formatter { + #[inline] + pub(crate) fn new() -> Self { + Self { + string: String::new(), + buf: String::new(), + } + } + + #[inline] + pub(crate) fn with_capacity(capacity: usize) -> Self { + Self { + string: String::with_capacity(capacity), + buf: String::new(), + } + } + + #[inline] + pub(crate) fn parts_mut(&mut self) -> (&mut String, &str) { + (&mut self.string, &self.buf) + } + + #[inline] + pub(crate) fn buf_mut(&mut self) -> &mut String { + &mut self.buf + } + + #[inline] + pub(crate) fn push(&mut self, c: char) { + self.string.push(c); + } + + #[inline] + pub(crate) fn push_str(&mut self, s: &str) { + self.string.push_str(s); + } + + #[inline] + pub(crate) fn into_string(self) -> String { + self.string + } + + #[inline] + pub(crate) fn as_str(&self) -> &str { + &self.string + } +} + +impl fmt::Write for Formatter { + #[inline] + fn write_str(&mut self, s: &str) -> fmt::Result { + self.string.push_str(s); + Ok(()) + } + + #[inline] + fn write_char(&mut self, c: char) -> fmt::Result { + self.string.push(c); + Ok(()) + } +} diff --git a/crates/rune/src/runtime/format.rs b/crates/rune/src/runtime/format.rs index e7bbf19b4..0e2298978 100644 --- a/crates/rune/src/runtime/format.rs +++ b/crates/rune/src/runtime/format.rs @@ -14,7 +14,7 @@ use serde::{Deserialize, Serialize}; use crate::compile::Named; use crate::module::InstallWith; -use crate::runtime::{FromValue, ProtocolCaller, RawStr, Value, VmErrorKind, VmResult}; +use crate::runtime::{Formatter, FromValue, ProtocolCaller, RawStr, Value, VmErrorKind, VmResult}; /// Error raised when trying to parse a type string and it fails. #[derive(Debug, Clone, Copy)] @@ -148,22 +148,17 @@ impl FormatSpec { } /// Format fill. - fn format_fill( - &self, - out: &mut String, - buf: &str, - align: Alignment, - fill: char, - sign: Option, - ) { + fn format_fill(&self, f: &mut Formatter, align: Alignment, fill: char, sign: Option) { + let (f, buf) = f.parts_mut(); + if let Some(sign) = sign { - out.push(sign); + f.push(sign); } let mut w = self.width.map(|n| n.get()).unwrap_or_default(); if w == 0 { - out.push_str(buf); + f.push_str(buf); return; } @@ -172,7 +167,7 @@ impl FormatSpec { .saturating_sub(sign.map(|_| 1).unwrap_or_default()); if w == 0 { - out.push_str(buf); + f.push_str(buf); return; } @@ -180,17 +175,17 @@ impl FormatSpec { match align { Alignment::Left => { - out.push_str(buf); - out.extend(filler); + f.push_str(buf); + f.extend(filler); } Alignment::Center => { - out.extend((&mut filler).take(w / 2)); - out.push_str(buf); - out.extend(filler); + f.extend((&mut filler).take(w / 2)); + f.push_str(buf); + f.extend(filler); } Alignment::Right => { - out.extend(filler); - out.push_str(buf); + f.extend(filler); + f.push_str(buf); } } } @@ -198,31 +193,30 @@ impl FormatSpec { fn format_display( &self, value: &Value, - out: &mut String, - buf: &mut String, + f: &mut Formatter, caller: &mut impl ProtocolCaller, ) -> VmResult<()> { match value { Value::Char(c) => { - buf.push(*c); - self.format_fill(out, buf, self.align, self.fill, None); + f.buf_mut().push(*c); + self.format_fill(f, self.align, self.fill, None); } Value::String(s) => { - buf.push_str(&vm_try!(s.borrow_ref())); - self.format_fill(out, buf, self.align, self.fill, None); + f.buf_mut().push_str(&vm_try!(s.borrow_ref())); + self.format_fill(f, self.align, self.fill, None); } Value::Integer(n) => { let (n, align, fill, sign) = self.int_traits(*n); - self.format_number(buf, n); - self.format_fill(out, buf, align, fill, sign); + self.format_number(f.buf_mut(), n); + self.format_fill(f, align, fill, sign); } Value::Float(n) => { let (n, align, fill, sign) = self.float_traits(*n); - vm_try!(self.format_float(buf, n)); - self.format_fill(out, buf, align, fill, sign); + vm_try!(self.format_float(f.buf_mut(), n)); + self.format_fill(f, align, fill, sign); } _ => { - let result = vm_try!(value.string_display_with(out, buf, caller)); + let result = vm_try!(value.string_display_with(f, caller)); vm_try!(result.map_err(|_| VmErrorKind::FormatError)); } } @@ -233,27 +227,26 @@ impl FormatSpec { fn format_debug( &self, value: &Value, - out: &mut String, - buf: &mut String, + f: &mut Formatter, caller: &mut impl ProtocolCaller, ) -> VmResult<()> { match value { Value::String(s) => { let s = vm_try!(s.borrow_ref()); - vm_try!(write!(out, "{:?}", &*s).map_err(|_| VmErrorKind::FormatError)); + vm_try!(write!(f, "{:?}", &*s).map_err(|_| VmErrorKind::FormatError)); } Value::Integer(n) => { let (n, align, fill, sign) = self.int_traits(*n); - self.format_number(buf, n); - self.format_fill(out, buf, align, fill, sign); + self.format_number(f.buf_mut(), n); + self.format_fill(f, align, fill, sign); } Value::Float(n) => { let (n, align, fill, sign) = self.float_traits(*n); - vm_try!(self.format_float(buf, n)); - self.format_fill(out, buf, align, fill, sign); + vm_try!(self.format_float(f.buf_mut(), n)); + self.format_fill(f, align, fill, sign); } value => { - let result = vm_try!(value.string_debug_with(out, caller)); + let result = vm_try!(value.string_debug_with(f, caller)); vm_try!(result.map_err(|_| VmErrorKind::FormatError)); } } @@ -261,17 +254,12 @@ impl FormatSpec { VmResult::Ok(()) } - fn format_upper_hex( - &self, - value: &Value, - out: &mut String, - buf: &mut String, - ) -> Result<(), VmErrorKind> { + fn format_upper_hex(&self, value: &Value, f: &mut Formatter) -> Result<(), VmErrorKind> { match value { Value::Integer(n) => { let (n, align, fill, sign) = self.int_traits(*n); - write!(buf, "{:X}", n).map_err(|_| VmErrorKind::FormatError)?; - self.format_fill(out, buf, align, fill, sign); + write!(f.buf_mut(), "{:X}", n).map_err(|_| VmErrorKind::FormatError)?; + self.format_fill(f, align, fill, sign); } _ => { return Err(VmErrorKind::FormatError); @@ -281,17 +269,12 @@ impl FormatSpec { Ok(()) } - fn format_lower_hex( - &self, - value: &Value, - out: &mut String, - buf: &mut String, - ) -> Result<(), VmErrorKind> { + fn format_lower_hex(&self, value: &Value, f: &mut Formatter) -> Result<(), VmErrorKind> { match value { Value::Integer(n) => { let (n, align, fill, sign) = self.int_traits(*n); - write!(buf, "{:x}", n).map_err(|_| VmErrorKind::FormatError)?; - self.format_fill(out, buf, align, fill, sign); + write!(f.buf_mut(), "{:x}", n).map_err(|_| VmErrorKind::FormatError)?; + self.format_fill(f, align, fill, sign); } _ => { return Err(VmErrorKind::FormatError); @@ -301,17 +284,12 @@ impl FormatSpec { Ok(()) } - fn format_binary( - &self, - value: &Value, - out: &mut String, - buf: &mut String, - ) -> Result<(), VmErrorKind> { + fn format_binary(&self, value: &Value, f: &mut Formatter) -> Result<(), VmErrorKind> { match value { Value::Integer(n) => { let (n, align, fill, sign) = self.int_traits(*n); - write!(buf, "{:b}", n).map_err(|_| VmErrorKind::FormatError)?; - self.format_fill(out, buf, align, fill, sign); + write!(f.buf_mut(), "{:b}", n).map_err(|_| VmErrorKind::FormatError)?; + self.format_fill(f, align, fill, sign); } _ => { return Err(VmErrorKind::FormatError); @@ -321,17 +299,13 @@ impl FormatSpec { Ok(()) } - fn format_pointer( - &self, - value: &Value, - out: &mut String, - buf: &mut String, - ) -> Result<(), VmErrorKind> { + fn format_pointer(&self, value: &Value, f: &mut Formatter) -> Result<(), VmErrorKind> { match value { Value::Integer(n) => { let (n, align, fill, sign) = self.int_traits(*n); - write!(buf, "{:p}", n as *const ()).map_err(|_| VmErrorKind::FormatError)?; - self.format_fill(out, buf, align, fill, sign); + write!(f.buf_mut(), "{:p}", n as *const ()) + .map_err(|_| VmErrorKind::FormatError)?; + self.format_fill(f, align, fill, sign); } _ => { return Err(VmErrorKind::FormatError); @@ -346,17 +320,18 @@ impl FormatSpec { pub(crate) fn format( &self, value: &Value, - out: &mut String, - buf: &mut String, + f: &mut Formatter, caller: &mut impl ProtocolCaller, ) -> VmResult<()> { + f.buf_mut().clear(); + match self.format_type { - Type::Display => vm_try!(self.format_display(value, out, buf, caller)), - Type::Debug => vm_try!(self.format_debug(value, out, buf, caller)), - Type::UpperHex => vm_try!(self.format_upper_hex(value, out, buf)), - Type::LowerHex => vm_try!(self.format_lower_hex(value, out, buf)), - Type::Binary => vm_try!(self.format_binary(value, out, buf)), - Type::Pointer => vm_try!(self.format_pointer(value, out, buf)), + Type::Display => vm_try!(self.format_display(value, f, caller)), + Type::Debug => vm_try!(self.format_debug(value, f, caller)), + Type::UpperHex => vm_try!(self.format_upper_hex(value, f)), + Type::LowerHex => vm_try!(self.format_lower_hex(value, f)), + Type::Binary => vm_try!(self.format_binary(value, f)), + Type::Pointer => vm_try!(self.format_pointer(value, f)), } VmResult::Ok(()) diff --git a/crates/rune/src/runtime/value.rs b/crates/rune/src/runtime/value.rs index de2f4ba75..2bf689aac 100644 --- a/crates/rune/src/runtime/value.rs +++ b/crates/rune/src/runtime/value.rs @@ -4,7 +4,6 @@ use core::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd}; use core::fmt; use core::fmt::Write; use core::hash; -use core::mem::{replace, take}; use crate::no_std::prelude::*; use crate::no_std::sync::Arc; @@ -13,9 +12,9 @@ use crate::no_std::vec; use crate::compile::ItemBuf; use crate::runtime::vm::CallResult; use crate::runtime::{ - AccessKind, AnyObj, Bytes, ConstValue, EnvProtocolCaller, Format, FromValue, FullTypeOf, - Function, Future, Generator, GeneratorState, Iterator, MaybeTypeOf, Mut, Object, OwnedTuple, - Protocol, ProtocolCaller, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, + AccessKind, AnyObj, Bytes, ConstValue, EnvProtocolCaller, Format, Formatter, FromValue, + FullTypeOf, Function, Future, Generator, GeneratorState, Iterator, MaybeTypeOf, Mut, Object, + OwnedTuple, Protocol, ProtocolCaller, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive, RawMut, RawRef, Ref, Shared, Stream, ToValue, Type, TypeInfo, Variant, Vec, Vm, VmError, VmErrorKind, VmIntegerRepr, VmResult, }; @@ -319,53 +318,49 @@ impl Value { /// # Panics /// /// This function will panic if called outside of a virtual machine. - pub fn string_display(&self, s: &mut String, buf: &mut String) -> VmResult { - self.string_display_with(s, buf, &mut EnvProtocolCaller) + pub fn string_display(&self, f: &mut Formatter) -> VmResult { + self.string_display_with(f, &mut EnvProtocolCaller) } /// Internal impl of string_display with a customizable caller. pub(crate) fn string_display_with( &self, - s: &mut String, - buf: &mut String, + f: &mut Formatter, caller: &mut impl ProtocolCaller, ) -> VmResult { match self { Value::Format(format) => { - vm_try!(format.spec.format(&format.value, s, buf, caller)); + vm_try!(format.spec.format(&format.value, f, caller)); } Value::Char(c) => { - s.push(*c); + f.push(*c); } Value::String(string) => { - s.push_str(&vm_try!(string.borrow_ref())); + f.push_str(&vm_try!(string.borrow_ref())); } Value::Integer(integer) => { let mut buffer = itoa::Buffer::new(); - s.push_str(buffer.format(*integer)); + f.push_str(buffer.format(*integer)); } Value::Float(float) => { let mut buffer = ryu::Buffer::new(); - s.push_str(buffer.format(*float)); + f.push_str(buffer.format(*float)); } Value::Bool(bool) => { - return VmResult::Ok(write!(s, "{}", bool)); + return VmResult::Ok(write!(f, "{}", bool)); } Value::Byte(byte) => { let mut buffer = itoa::Buffer::new(); - s.push_str(buffer.format(*byte)); + f.push_str(buffer.format(*byte)); } value => { - let b = Shared::new(take(s)); - let result = vm_try!(caller.call_protocol_fn( Protocol::STRING_DISPLAY, value.clone(), - (Value::from(b.clone()),), + (f,), )); let result = vm_try!(fmt::Result::from_value(result)); - drop(replace(s, vm_try!(b.take()))); return VmResult::Ok(result); } } @@ -373,7 +368,7 @@ impl Value { VmResult::Ok(fmt::Result::Ok(())) } - /// Debug format the value using the [Protocol::STRING_DEBUG] protocol. + /// Debug format the value using the [`STRING_DEBUG`] protocol. /// /// You must use [Vm::with] to specify which virtual machine this function /// is called inside. @@ -381,122 +376,118 @@ impl Value { /// # Panics /// /// This function will panic if called outside of a virtual machine. - pub fn string_debug(&self, s: &mut String) -> VmResult { - self.string_debug_with(s, &mut EnvProtocolCaller) + /// + /// [`STRING_DEBUG`]: Protocol::STRING_DEBUG + pub fn string_debug(&self, f: &mut Formatter) -> VmResult { + self.string_debug_with(f, &mut EnvProtocolCaller) } /// Internal impl of string_debug with a customizable caller. pub(crate) fn string_debug_with( &self, - s: &mut String, + f: &mut Formatter, caller: &mut impl ProtocolCaller, ) -> VmResult { let result = match self { Value::Bool(value) => { - write!(s, "{:?}", value) + write!(f, "{:?}", value) } Value::Byte(value) => { - write!(s, "{:?}", value) + write!(f, "{:?}", value) } Value::Char(value) => { - write!(s, "{:?}", value) + write!(f, "{:?}", value) } Value::Integer(value) => { - write!(s, "{:?}", value) + write!(f, "{:?}", value) } Value::Float(value) => { - write!(s, "{:?}", value) + write!(f, "{:?}", value) } Value::Type(value) => { - write!(s, "{:?}", value) + write!(f, "{:?}", value) } Value::String(value) => { - write!(s, "{:?}", value) + write!(f, "{:?}", value) } Value::Bytes(value) => { - write!(s, "{:?}", value) + write!(f, "{:?}", value) } Value::Vec(value) => { let value = vm_try!(value.borrow_ref()); - vm_try!(Vec::string_debug_with(&value, s, caller)) + vm_try!(Vec::string_debug_with(&value, f, caller)) } Value::EmptyTuple => { - write!(s, "()") + write!(f, "()") } Value::Tuple(value) => { - write!(s, "{:?}", value) + write!(f, "{:?}", value) } Value::Object(value) => { - write!(s, "{:?}", value) + write!(f, "{:?}", value) } Value::RangeFrom(value) => { - write!(s, "{:?}", value) + write!(f, "{:?}", value) } Value::RangeFull(value) => { - write!(s, "{:?}", value) + write!(f, "{:?}", value) } Value::RangeInclusive(value) => { - write!(s, "{:?}", value) + write!(f, "{:?}", value) } Value::RangeToInclusive(value) => { - write!(s, "{:?}", value) + write!(f, "{:?}", value) } Value::RangeTo(value) => { - write!(s, "{:?}", value) + write!(f, "{:?}", value) } Value::Range(value) => { - write!(s, "{:?}", value) + write!(f, "{:?}", value) } Value::Future(value) => { - write!(s, "{:?}", value) + write!(f, "{:?}", value) } Value::Stream(value) => { - write!(s, "{:?}", value) + write!(f, "{:?}", value) } Value::Generator(value) => { - write!(s, "{:?}", value) + write!(f, "{:?}", value) } Value::GeneratorState(value) => { - write!(s, "{:?}", value) + write!(f, "{:?}", value) } Value::Option(value) => { - write!(s, "{:?}", value) + write!(f, "{:?}", value) } Value::Result(value) => { - write!(s, "{:?}", value) + write!(f, "{:?}", value) } Value::EmptyStruct(value) => { - write!(s, "{:?}", value) + write!(f, "{:?}", value) } Value::TupleStruct(value) => { - write!(s, "{:?}", value) + write!(f, "{:?}", value) } Value::Struct(value) => { - write!(s, "{:?}", value) + write!(f, "{:?}", value) } Value::Variant(value) => { - write!(s, "{:?}", value) + write!(f, "{:?}", value) } Value::Function(value) => { - write!(s, "{:?}", value) + write!(f, "{:?}", value) } Value::Format(value) => { - write!(s, "{:?}", value) + write!(f, "{:?}", value) } Value::Iterator(value) => { - write!(s, "{:?}", value) + write!(f, "{:?}", value) } value => { - let b = Shared::new(take(s)); - - let result = vm_try!(caller.call_protocol_fn( - Protocol::STRING_DEBUG, - value.clone(), - (Value::from(b.clone()),), - )); + let result = + vm_try!(caller.call_protocol_fn(Protocol::STRING_DEBUG, value.clone(), (f,),)); let result = vm_try!(fmt::Result::from_value(result)); - drop(replace(s, vm_try!(b.take()))); return VmResult::Ok(result); } }; @@ -1910,14 +1901,14 @@ impl fmt::Debug for Value { write!(f, "{:?}", value)?; } value => { - let mut s = String::new(); + let mut formatter = Formatter::new(); - match value.string_debug(&mut s) { + match value.string_debug(&mut formatter) { VmResult::Ok(result) => result?, - VmResult::Err(error) => return write!(f, "{:?}", error), + VmResult::Err(..) => return Err(fmt::Error), } - f.write_str(&s)?; + f.write_str(formatter.as_str())?; } } diff --git a/crates/rune/src/runtime/vec.rs b/crates/rune/src/runtime/vec.rs index b57558f38..95ba5fe0e 100644 --- a/crates/rune/src/runtime/vec.rs +++ b/crates/rune/src/runtime/vec.rs @@ -13,8 +13,8 @@ use crate::no_std::vec; use crate::compile::Named; use crate::module::InstallWith; use crate::runtime::{ - FromValue, Iterator, ProtocolCaller, RawRef, RawStr, Ref, Shared, ToValue, UnsafeToRef, Value, - VmErrorKind, VmResult, + Formatter, FromValue, Iterator, ProtocolCaller, RawRef, RawStr, Ref, Shared, ToValue, + UnsafeToRef, Value, VmErrorKind, VmResult, }; use self::iter::Iter; @@ -213,23 +213,23 @@ impl Vec { pub(crate) fn string_debug_with( this: &[Value], - s: &mut String, + f: &mut Formatter, caller: &mut impl ProtocolCaller, ) -> VmResult { let mut it = this.iter().peekable(); - vm_write!(s, "["); + vm_write!(f, "["); while let Some(value) = it.next() { - if let Err(fmt::Error) = vm_try!(value.string_debug_with(s, caller)) { + if let Err(fmt::Error) = vm_try!(value.string_debug_with(f, caller)) { return VmResult::Ok(Err(fmt::Error)); } if it.peek().is_some() { - vm_write!(s, ", "); + vm_write!(f, ", "); } } - vm_write!(s, "]"); + vm_write!(f, "]"); VmResult::Ok(Ok(())) } diff --git a/crates/rune/src/runtime/vm.rs b/crates/rune/src/runtime/vm.rs index ca74baab5..599dfb4e9 100644 --- a/crates/rune/src/runtime/vm.rs +++ b/crates/rune/src/runtime/vm.rs @@ -12,12 +12,12 @@ use crate::runtime::budget; use crate::runtime::future::SelectFuture; use crate::runtime::unit::{UnitFn, UnitStorage}; use crate::runtime::{ - self, Args, Awaited, BorrowMut, Bytes, Call, EmptyStruct, Format, FormatSpec, FromValue, - Function, Future, Generator, GuardedArgs, Inst, InstAddress, InstAssignOp, InstOp, InstRange, - InstTarget, InstValue, InstVariant, Object, OwnedTuple, Panic, Protocol, Range, RangeFrom, - RangeFull, RangeInclusive, RangeTo, RangeToInclusive, RuntimeContext, Select, Shared, Stack, - Stream, Struct, Type, TypeCheck, TypeOf, Unit, Value, Variant, VariantData, Vec, VmError, - VmErrorKind, VmExecution, VmHalt, VmIntegerRepr, VmResult, VmSendExecution, + self, Args, Awaited, BorrowMut, Bytes, Call, EmptyStruct, Format, FormatSpec, Formatter, + FromValue, Function, Future, Generator, GuardedArgs, Inst, InstAddress, InstAssignOp, InstOp, + InstRange, InstTarget, InstValue, InstVariant, Object, OwnedTuple, Panic, Protocol, Range, + RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive, RuntimeContext, Select, + Shared, Stack, Stream, Struct, Type, TypeCheck, TypeOf, Unit, Value, Variant, VariantData, Vec, + VmError, VmErrorKind, VmExecution, VmHalt, VmIntegerRepr, VmResult, VmSendExecution, }; /// Small helper function to build errors. @@ -742,14 +742,11 @@ impl Vm { _ => return VmResult::Ok(None), }; - let value = match value { - Some(value) => value, - None => { - return err(VmErrorKind::MissingIndex { - target: vm_try!(target.type_info()), - index: VmIntegerRepr::from(index), - }); - } + let Some(value) = value else { + return err(VmErrorKind::MissingIndexInteger { + target: vm_try!(target.type_info()), + index: VmIntegerRepr::from(index), + }); }; VmResult::Ok(Some(value)) @@ -815,14 +812,11 @@ impl Vm { _ => return VmResult::Ok(None), }; - let value = match value { - Some(value) => value, - None => { - return err(VmErrorKind::MissingIndex { - target: vm_try!(target.type_info()), - index: VmIntegerRepr::from(index), - }); - } + let Some(value) = value else { + return err(VmErrorKind::MissingIndexInteger { + target: vm_try!(target.type_info()), + index: VmIntegerRepr::from(index), + }); }; VmResult::Ok(Some(value)) @@ -2162,14 +2156,11 @@ impl Vm { } } Value::Integer(index) => { - let index = match (*index).try_into() { - Result::Ok(index) => index, - Result::Err(..) => { - return err(VmErrorKind::MissingIndex { - target: vm_try!(target.type_info()), - index: VmIntegerRepr::from(*index), - }); - } + let Ok(index) = (*index).try_into() else { + return err(VmErrorKind::MissingIndexInteger { + target: vm_try!(target.type_info()), + index: VmIntegerRepr::from(*index), + }); }; if let Some(value) = vm_try!(Self::try_tuple_like_index_get(&target, index)) { @@ -2466,20 +2457,16 @@ impl Vm { fn op_string_concat(&mut self, len: usize, size_hint: usize) -> VmResult<()> { let values = vm_try!(self.stack.drain(len)).collect::>(); - let mut out = String::with_capacity(size_hint); - let mut buf = String::with_capacity(16); + let mut f = Formatter::with_capacity(size_hint); for value in values { - buf.clear(); - - if let Result::Err(fmt::Error) = - vm_try!(value.string_display_with(&mut out, &mut buf, &mut *self)) + if let Result::Err(fmt::Error) = vm_try!(value.string_display_with(&mut f, &mut *self)) { return err(VmErrorKind::FormatError); } } - self.stack.push(out); + self.stack.push(f.into_string()); VmResult::Ok(()) } @@ -2982,6 +2969,7 @@ impl Vm { /// /// ```,no_run /// use rune::{Context, Unit}; + /// use rune::runtime::Formatter; /// use std::sync::Arc; /// /// let context = Context::with_default_modules()?; @@ -2998,12 +2986,11 @@ impl Vm { /// // Call the string_display protocol on `output`. This requires /// // access to a virtual machine since it might use functions /// // registered in the unit associated with it. - /// let mut s = String::new(); - /// let mut buf = String::new(); + /// let mut f = Formatter::default(); /// /// // Note: We do an extra unwrap because the return value is /// // `fmt::Result`. - /// vm.with(|| output.string_display(&mut s, &mut buf)).into_result()?.expect("formatting should succeed"); + /// vm.with(|| output.string_display(&mut f)).into_result()?.expect("formatting should succeed"); /// # Ok::<_, rune::Error>(()) /// ``` pub fn with(&mut self, f: F) -> T diff --git a/crates/rune/src/runtime/vm_error.rs b/crates/rune/src/runtime/vm_error.rs index 6b86528fc..8a91a1120 100644 --- a/crates/rune/src/runtime/vm_error.rs +++ b/crates/rune/src/runtime/vm_error.rs @@ -524,6 +524,9 @@ pub(crate) enum VmErrorKind { }, MissingIndex { target: TypeInfo, + }, + MissingIndexInteger { + target: TypeInfo, index: VmIntegerRepr, }, MissingIndexKey { @@ -715,7 +718,10 @@ impl fmt::Display for VmErrorKind { VmErrorKind::ObjectIndexMissing { slot } => { write!(f, "Missing index by static string slot `{slot}`",) } - VmErrorKind::MissingIndex { target, index } => { + VmErrorKind::MissingIndex { target } => { + write!(f, "Type `{target}` missing index",) + } + VmErrorKind::MissingIndexInteger { target, index } => { write!(f, "Type `{target}` missing index `{index}`",) } VmErrorKind::MissingIndexKey { target, index } => {