From 9b1868bede2ce24ae72cc57ea4e08237962c48af Mon Sep 17 00:00:00 2001 From: John-John Tedro Date: Fri, 18 Aug 2023 16:56:45 +0200 Subject: [PATCH 1/6] Finish result documentation --- crates/rune/src/modules/result.rs | 110 ++++++++++++++++++++++++++++-- 1 file changed, 104 insertions(+), 6 deletions(-) diff --git a/crates/rune/src/modules/result.rs b/crates/rune/src/modules/result.rs index 675ba901e..e9f32dce0 100644 --- a/crates/rune/src/modules/result.rs +++ b/crates/rune/src/modules/result.rs @@ -21,10 +21,11 @@ pub fn module() -> Result { .static_docs(&["Contains the error value"]); module.function_meta(ok)?; - module.associated_function("is_ok", is_ok)?; - module.associated_function("is_err", is_err)?; - module.associated_function("unwrap", unwrap_impl)?; - module.associated_function("unwrap_or", Result::::unwrap_or)?; + module.function_meta(is_ok)?; + module.function_meta(is_err)?; + module.function_meta(unwrap)?; + module.function_meta(unwrap_or)?; + module.function_meta(unwrap_or_else)?; module.function_meta(expect)?; module.function_meta(and_then)?; module.function_meta(map)?; @@ -40,24 +41,121 @@ fn ok(result: &Result) -> Option { result.as_ref().ok().cloned() } +/// Returns `true` if the result is [`Ok`]. +/// +/// # Examples +/// +/// ```rune +/// let x = Ok(-3); +/// assert_eq!(x.is_ok(), true); +/// +/// let x = Err("Some error message"); +/// assert_eq!(x.is_ok(), false); +/// ``` +#[rune::function(instance)] fn is_ok(result: &Result) -> bool { result.is_ok() } +/// Returns `true` if the result is [`Err`]. +/// +/// # Examples +/// +/// ```rune +/// let x = Ok(-3); +/// assert_eq!(x.is_err(), false); +/// +/// let x = Err("Some error message"); +/// assert_eq!(x.is_err(), true); +/// ``` +#[rune::function(instance)] fn is_err(result: &Result) -> bool { result.is_err() } -fn unwrap_impl(result: Result) -> VmResult { +/// Returns the contained [`Ok`] value, consuming the `self` value. +/// +/// Because this function may panic, its use is generally discouraged. Instead, +/// prefer to use pattern matching and handle the [`Err`] case explicitly, or +/// call [`unwrap_or`], [`unwrap_or_else`], or [`unwrap_or_default`]. +/// +/// [`unwrap_or`]: Result::unwrap_or +/// [`unwrap_or_else`]: Result::unwrap_or_else +/// [`unwrap_or_default`]: Result::unwrap_or_default +/// +/// # Panics +/// +/// Panics if the value is an [`Err`], with a panic message provided by the +/// [`Err`]'s value. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ```rune +/// let x = Ok(2); +/// assert_eq!(x.unwrap(), 2); +/// ``` +/// +/// ```rune,should_panic +/// let x = Err("emergency failure"); +/// x.unwrap(); // panics with `emergency failure` +/// ``` +#[rune::function(instance)] +fn unwrap(result: Result) -> VmResult { match result { Ok(value) => VmResult::Ok(value), Err(err) => VmResult::err(Panic::msg(format_args!( - "called `Result::unwrap()` on an `Err` value: {:?}", + "Called `Result::unwrap()` on an `Err` value: {:?}", err ))), } } +/// Returns the contained [`Ok`] value or a provided default. +/// +/// Arguments passed to `unwrap_or` are eagerly evaluated; if you are passing +/// the result of a function call, it is recommended to use [`unwrap_or_else`], +/// which is lazily evaluated. +/// +/// [`unwrap_or_else`]: Result::unwrap_or_else +/// +/// # Examples +/// +/// ```rune +/// let default_value = 2; +/// let x = Ok(9); +/// assert_eq!(x.unwrap_or(default_value), 9); +/// +/// let x = Err("error"); +/// assert_eq!(x.unwrap_or(default_value), default_value); +/// ``` +#[rune::function(instance)] +fn unwrap_or(this: Result, default: Value) -> Value { + this.unwrap_or(default) +} + +/// Returns the contained [`Ok`] value or computes it from a closure. +/// +/// +/// # Examples +/// +/// ```rune +/// fn count(x) { +/// x.len() +/// } +/// +/// assert_eq!(Ok(2).unwrap_or_else(count), 2); +/// assert_eq!(Err("foo").unwrap_or_else(count), 3); +/// ``` +#[rune::function(instance)] +fn unwrap_or_else(this: Result, default: Function) -> VmResult { + match this { + Ok(value) => VmResult::Ok(value), + Err(error) => default.call::<_, Value>((error,)), + } +} + /// Returns the contained [`Ok`] value, consuming the `self` value. /// /// Because this function may panic, its use is generally discouraged. Instead, From c999a53fe6b4c0d6ecf1eaabe107973eb6c4ee93 Mon Sep 17 00:00:00 2001 From: John-John Tedro Date: Fri, 18 Aug 2023 17:02:51 +0200 Subject: [PATCH 2/6] Fix clippy --- crates/rune/src/module.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/rune/src/module.rs b/crates/rune/src/module.rs index 5d1056ce1..2aa1e8efe 100644 --- a/crates/rune/src/module.rs +++ b/crates/rune/src/module.rs @@ -312,7 +312,7 @@ impl ItemFnMut<'_> { } /// Mark the given item as an async function. - pub fn is_async(self, is_async: bool) -> Self { + pub fn is_async(self, #[cfg_attr(not(feature = "doc"), allow(unused))] is_async: bool) -> Self { #[cfg(feature = "doc")] { *self.is_async = is_async; @@ -322,7 +322,7 @@ impl ItemFnMut<'_> { } /// Indicate the number of arguments this function accepts. - pub fn args(self, args: usize) -> Self { + pub fn args(self, #[cfg_attr(not(feature = "doc"), allow(unused))] args: usize) -> Self { #[cfg(feature = "doc")] { *self.args = Some(args); @@ -345,7 +345,10 @@ impl ItemFnMut<'_> { } /// Set argument types. - pub fn argument_types(self, arguments: [Option; N]) -> Self { + pub fn argument_types( + self, + #[cfg_attr(not(feature = "doc"), allow(unused))] arguments: [Option; N], + ) -> Self { #[cfg(feature = "doc")] { *self.argument_types = Box::from(arguments.into_iter().collect::>()); From 9f4c77fef10d7017a5dea4d6938833d58f92edd8 Mon Sep 17 00:00:00 2001 From: John-John Tedro Date: Fri, 18 Aug 2023 17:30:23 +0200 Subject: [PATCH 3/6] Rework how expect message is formatted --- crates/rune/src/modules/result.rs | 22 ++++++++++++++++++++-- crates/rune/src/runtime/value.rs | 3 ++- crates/rune/src/tests/result.rs | 8 +++----- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/crates/rune/src/modules/result.rs b/crates/rune/src/modules/result.rs index e9f32dce0..cd3ab9ed9 100644 --- a/crates/rune/src/modules/result.rs +++ b/crates/rune/src/modules/result.rs @@ -1,6 +1,9 @@ //! The `std::result` module. +use core::fmt; + use crate as rune; +use crate::no_std::prelude::*; use crate::runtime::{Function, Panic, Value, VmResult}; use crate::{ContextError, Module}; @@ -186,10 +189,25 @@ fn unwrap_or_else(this: Result, default: Function) -> VmResult, message: &str) -> VmResult { +fn expect(result: Result, message: Value) -> VmResult { match result { Ok(value) => VmResult::Ok(value), - Err(err) => VmResult::err(Panic::msg(format_args!("{}: {:?}", message, err))), + Err(err) => { + let mut s = String::new(); + let mut buf = String::new(); + + if let Err(fmt::Error) = vm_try!(message.string_display(&mut s, &mut buf)) { + return VmResult::err(Panic::msg("failed to format message")); + } + + s.push_str(": "); + + if let Err(fmt::Error) = vm_try!(err.string_debug(&mut s)) { + return VmResult::err(Panic::msg("failed to format error")); + } + + VmResult::err(Panic::custom(s)) + } } } diff --git a/crates/rune/src/runtime/value.rs b/crates/rune/src/runtime/value.rs index 858704b62..536496fce 100644 --- a/crates/rune/src/runtime/value.rs +++ b/crates/rune/src/runtime/value.rs @@ -352,7 +352,8 @@ impl Value { return VmResult::Ok(write!(s, "{}", bool)); } Value::Byte(byte) => { - return VmResult::Ok(write!(s, "{:#04X}", byte)); + let mut buffer = itoa::Buffer::new(); + s.push_str(buffer.format(*byte)); } value => { let b = Shared::new(take(s)); diff --git a/crates/rune/src/tests/result.rs b/crates/rune/src/tests/result.rs index 7d60b9be5..e421ecc1a 100644 --- a/crates/rune/src/tests/result.rs +++ b/crates/rune/src/tests/result.rs @@ -47,15 +47,13 @@ fn test_expect_some() { #[test] fn test_expect() { - assert_vm_error!( - r#" + assert_vm_error!(r#" pub fn main() { Err("Error").expect("Err('Error')") } "#, - Panic { reason} => { - assert_eq!(reason.to_string(), - "Err('Error'): \"Error\"") + Panic { reason } => { + assert_eq!(reason.to_string(), "Err('Error'): \"Error\"") } ); } From 70d0c04828ec2f1f5f91d53bbbc5b5eafe3b5403 Mon Sep 17 00:00:00 2001 From: John-John Tedro Date: Fri, 18 Aug 2023 17:39:13 +0200 Subject: [PATCH 4/6] test depends on miri --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1972f08a5..c1ab772eb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -108,7 +108,7 @@ jobs: test: runs-on: ubuntu-latest - needs: [no_default_features, build_feature, docs, msrv, wasm] + needs: [no_default_features, build_feature, docs, msrv, miri, wasm] steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@stable From 36b1ef607f0d059ed51a37c037dc629d9ee28068 Mon Sep 17 00:00:00 2001 From: John-John Tedro Date: Fri, 18 Aug 2023 18:52:13 +0200 Subject: [PATCH 5/6] Way more documentation --- benches/benches/bench_main.rs | 4 +- crates/rune/src/cli.rs | 2 +- crates/rune/src/cli/loader.rs | 4 +- crates/rune/src/compile/context.rs | 12 + crates/rune/src/compile/meta.rs | 3 + crates/rune/src/doc/build.rs | 4 + crates/rune/src/doc/build/type_.rs | 2 + crates/rune/src/doc/context.rs | 13 +- crates/rune/src/doc/static/function.html.hbs | 1 + crates/rune/src/doc/static/runedoc.css.hbs | 17 + crates/rune/src/doc/static/type.html.hbs | 1 + crates/rune/src/module.rs | 22 ++ crates/rune/src/module/function_meta.rs | 10 + crates/rune/src/module/module.rs | 12 + crates/rune/src/modules/core.rs | 108 ++++-- crates/rune/src/modules/option.rs | 346 +++++++++++++++--- crates/rune/src/modules/result.rs | 4 +- crates/rune/src/modules/string.rs | 35 +- crates/rune/src/modules/test.rs | 4 +- crates/rune/src/query/query.rs | 6 + crates/rune/src/tests.rs | 2 +- crates/rune/src/tests/option.rs | 2 +- crates/rune/src/tests/result.rs | 2 +- crates/rune/src/tests/stmt_reordering.rs | 2 +- crates/rune/src/tests/tuple.rs | 2 +- crates/rune/src/tests/vm_general.rs | 2 +- examples/examples/proxy.rs | 2 +- scripts/book/primitives/primitives.rn | 2 +- site/content/posts/2020-12-07-faster-tests.md | 2 +- tools/builder/src/main.rs | 2 +- 30 files changed, 523 insertions(+), 107 deletions(-) diff --git a/benches/benches/bench_main.rs b/benches/benches/bench_main.rs index 1d8102a93..4530b00c3 100644 --- a/benches/benches/bench_main.rs +++ b/benches/benches/bench_main.rs @@ -23,10 +23,10 @@ pub(crate) fn sources(source: &str) -> Sources { macro_rules! rune_vm { ($($tt:tt)*) => {{ - let context = rune::Context::with_default_modules().expect("failed to build context"); + let context = rune::Context::with_default_modules().expect("Failed to build context"); let mut diagnostics = Default::default(); let mut sources = $crate::sources(stringify!($($tt)*)); - $crate::vm(&context, &mut sources, &mut diagnostics).expect("program to compile successfully") + $crate::vm(&context, &mut sources, &mut diagnostics).expect("Program to compile successfully") }}; } diff --git a/crates/rune/src/cli.rs b/crates/rune/src/cli.rs index c41a45655..d39fe1975 100644 --- a/crates/rune/src/cli.rs +++ b/crates/rune/src/cli.rs @@ -118,7 +118,7 @@ impl<'a> Entry<'a> { let runtime = tokio::runtime::Builder::new_current_thread() .enable_all() .build() - .expect("failed to build runtime"); + .expect("Failed to build runtime"); match runtime.block_on(self.inner()) { Ok(exit_code) => { diff --git a/crates/rune/src/cli/loader.rs b/crates/rune/src/cli/loader.rs index 562a5ffab..499888b6c 100644 --- a/crates/rune/src/cli/loader.rs +++ b/crates/rune/src/cli/loader.rs @@ -45,11 +45,11 @@ pub(super) fn load( match bincode::deserialize_from::<_, Unit>(f) { Ok(unit) => { - tracing::trace!("using cache: {}", bytecode_path.display()); + tracing::trace!("Using cache: {}", bytecode_path.display()); Some(Arc::new(unit)) } Err(e) => { - tracing::error!("failed to deserialize: {}: {}", bytecode_path.display(), e); + tracing::error!("Failed to deserialize: {}: {}", bytecode_path.display(), e); None } } diff --git a/crates/rune/src/compile/context.rs b/crates/rune/src/compile/context.rs index 6bf4e6db6..c311b10b8 100644 --- a/crates/rune/src/compile/context.rs +++ b/crates/rune/src/compile/context.rs @@ -438,6 +438,8 @@ impl Context { #[cfg(feature = "doc")] is_async: false, #[cfg(feature = "doc")] + deprecated: None, + #[cfg(feature = "doc")] args: Some(match fields { Fields::Named(names) => names.len(), Fields::Unnamed(args) => *args, @@ -500,6 +502,8 @@ impl Context { #[cfg(feature = "doc")] is_async: false, #[cfg(feature = "doc")] + deprecated: None, + #[cfg(feature = "doc")] args: Some(match fields { Fields::Named(names) => names.len(), Fields::Unnamed(args) => *args, @@ -615,6 +619,8 @@ impl Context { #[cfg(feature = "doc")] is_async: f.is_async, #[cfg(feature = "doc")] + deprecated: f.deprecated.clone(), + #[cfg(feature = "doc")] args: f.args, #[cfg(feature = "doc")] return_type: f.return_type.as_ref().map(|f| f.hash), @@ -722,6 +728,8 @@ impl Context { #[cfg(feature = "doc")] is_async: assoc.is_async, #[cfg(feature = "doc")] + deprecated: assoc.deprecated.clone(), + #[cfg(feature = "doc")] args: assoc.args, #[cfg(feature = "doc")] return_type: assoc.return_type.as_ref().map(|f| f.hash), @@ -802,6 +810,8 @@ impl Context { #[cfg(feature = "doc")] is_async: false, #[cfg(feature = "doc")] + deprecated: None, + #[cfg(feature = "doc")] args: Some(0), #[cfg(feature = "doc")] return_type: Some(hash), @@ -892,6 +902,8 @@ impl Context { #[cfg(feature = "doc")] is_async: false, #[cfg(feature = "doc")] + deprecated: None, + #[cfg(feature = "doc")] args: Some(match fields { Fields::Named(names) => names.len(), Fields::Unnamed(args) => *args, diff --git a/crates/rune/src/compile/meta.rs b/crates/rune/src/compile/meta.rs index ad540c00c..4a94aef8a 100644 --- a/crates/rune/src/compile/meta.rs +++ b/crates/rune/src/compile/meta.rs @@ -326,6 +326,9 @@ pub struct Signature { /// An asynchronous function. #[cfg(feature = "doc")] pub(crate) is_async: bool, + /// Deprecation notice. + #[cfg(feature = "doc")] + pub(crate) deprecated: Option>, /// Arguments. #[cfg(feature = "doc")] pub(crate) args: Option, diff --git a/crates/rune/src/doc/build.rs b/crates/rune/src/doc/build.rs index 9636c42b1..9249ed23e 100644 --- a/crates/rune/src/doc/build.rs +++ b/crates/rune/src/doc/build.rs @@ -812,6 +812,7 @@ fn module<'m>(cx: &mut Ctxt<'_, 'm>, meta: Meta<'m>, queue: &mut VecDeque { is_async: bool, + deprecated: Option<&'a str>, path: RelativePathBuf, #[serde(serialize_with = "serialize_item")] item: ItemBuf, @@ -895,6 +896,7 @@ fn module<'m>(cx: &mut Ctxt<'_, 'm>, meta: Meta<'m>, queue: &mut VecDeque(cx: &mut Ctxt<'_, 'm>, meta: Meta<'m>) -> Result, module: String, is_async: bool, + deprecated: Option<&'a str>, #[serde(serialize_with = "serialize_item")] item: &'a Item, #[serde(serialize_with = "serialize_component_ref")] @@ -1011,6 +1014,7 @@ fn build_function<'m>(cx: &mut Ctxt<'_, 'm>, meta: Meta<'m>) -> Result { #[derive(Serialize)] pub(super) struct Method<'a> { is_async: bool, + deprecated: Option<&'a str>, name: &'a str, args: String, parameters: Option, @@ -90,6 +91,7 @@ pub(super) fn build_assoc_fns<'m>( methods.push(Method { is_async: assoc.is_async, + deprecated: assoc.deprecated, name, args: cx.args_to_string( assoc.arg_names, diff --git a/crates/rune/src/doc/context.rs b/crates/rune/src/doc/context.rs index 916694af9..4e3c4590d 100644 --- a/crates/rune/src/doc/context.rs +++ b/crates/rune/src/doc/context.rs @@ -33,6 +33,7 @@ pub(crate) struct Meta<'a> { #[derive(Debug, Clone, Copy)] pub(crate) struct Function<'a> { pub(crate) is_async: bool, + pub(crate) deprecated: Option<&'a str>, pub(crate) arg_names: Option<&'a [String]>, pub(crate) args: Option, pub(crate) signature: Signature, @@ -64,6 +65,7 @@ pub(crate) struct AssocVariant<'a> { pub(crate) struct AssocFn<'a> { pub(crate) kind: AssocFnKind<'a>, pub(crate) is_async: bool, + pub(crate) deprecated: Option<&'a str>, pub(crate) return_type: Option, pub(crate) argument_types: &'a [Option], pub(crate) docs: &'a [String], @@ -125,12 +127,13 @@ impl<'a> Context<'a> { Some(associated.iter().flat_map(move |hash| { let data = visitor.data.get(hash)?; - let (is_async, kind) = match data.kind { + let (is_async, deprecated, kind) = match data.kind { Some(meta::Kind::Function { signature: ref f, .. }) => ( f.is_async, + f.deprecated.as_deref(), AssocFnKind::Method(data.item.last()?.as_str()?, f.args, Signature::Function), ), Some(meta::Kind::AssociatedFunction { @@ -138,6 +141,7 @@ impl<'a> Context<'a> { .. }) => ( f.is_async, + f.deprecated.as_deref(), AssocFnKind::Method(data.item.last()?.as_str()?, f.args, Signature::Instance), ), Some(meta::Kind::Variant { .. }) => { @@ -152,6 +156,7 @@ impl<'a> Context<'a> { Some(Assoc::Fn(AssocFn { kind, is_async, + deprecated, return_type: None, argument_types: &[], docs: &data.docs, @@ -184,6 +189,7 @@ impl<'a> Context<'a> { Some(Assoc::Fn(AssocFn { kind, is_async: signature.is_async, + deprecated: signature.deprecated.as_deref(), return_type: signature.return_type, argument_types: &signature .argument_types, @@ -198,6 +204,7 @@ impl<'a> Context<'a> { Some(Assoc::Fn(AssocFn { kind, is_async: signature.is_async, + deprecated: signature.deprecated.as_deref(), return_type: signature.return_type, argument_types: &signature .argument_types, @@ -288,6 +295,7 @@ impl<'a> Context<'a> { } => { Kind::Function(Function { is_async: f.is_async, + deprecated: f.deprecated.as_deref(), signature: Signature::Function, arg_names: meta.docs.args(), args: f.args, @@ -301,6 +309,7 @@ impl<'a> Context<'a> { } => { Kind::Function(Function { is_async: f.is_async, + deprecated: f.deprecated.as_deref(), signature: Signature::Instance, arg_names: meta.docs.args(), args: f.args, @@ -343,6 +352,7 @@ fn visitor_meta_to_meta<'a>(base: &'a Item, data: &'a VisitorData) -> Meta<'a> { Some(meta::Kind::Enum { .. }) => Kind::Enum, Some(meta::Kind::Function { signature: f, .. }) => Kind::Function(Function { is_async: f.is_async, + deprecated: f.deprecated.as_deref(), arg_names: None, args: f.args, signature: Signature::Function, @@ -351,6 +361,7 @@ fn visitor_meta_to_meta<'a>(base: &'a Item, data: &'a VisitorData) -> Meta<'a> { }), Some(meta::Kind::AssociatedFunction { signature: f, .. }) => Kind::Function(Function { is_async: f.is_async, + deprecated: f.deprecated.as_deref(), arg_names: None, args: f.args, signature: Signature::Instance, diff --git a/crates/rune/src/doc/static/function.html.hbs b/crates/rune/src/doc/static/function.html.hbs index 6c2e24804..fadead5fb 100644 --- a/crates/rune/src/doc/static/function.html.hbs +++ b/crates/rune/src/doc/static/function.html.hbs @@ -4,5 +4,6 @@
{{#if is_async}}async {{/if}} fn {{name}}({{literal args}}){{#if this.return_type}} -> {{literal this.return_type}}{{/if}}
+{{#if deprecated}}
Deprecated:{{deprecated}}
{{/if}} {{#if doc}}{{literal doc}}{{/if}} {{/layout}} diff --git a/crates/rune/src/doc/static/runedoc.css.hbs b/crates/rune/src/doc/static/runedoc.css.hbs index 779c20bbd..41f3faed8 100644 --- a/crates/rune/src/doc/static/runedoc.css.hbs +++ b/crates/rune/src/doc/static/runedoc.css.hbs @@ -18,6 +18,8 @@ --keyword-async-color: rgb(70, 43, 241); --code-background-color: rgb(42, 42, 42); --code-block-background-color: #2a2a2a; + --deprecated-color: #39261c; + --deprecated-background-color: #da9347; } @font-face { @@ -174,6 +176,21 @@ pre, .signature { padding: 14px; } +.deprecated { + color: var(--deprecated-color); + background-color: var(--deprecated-background-color); + border-radius: 3px; + padding: 0.1em 0.2em; + font-size: 80%; + margin-top: 0.5em; + margin-bottom: 0.5em; +} + +.deprecated .heading { + font-weight: bold; + margin-right: 0.5em; +} + p > code { background-color: var(--code-block-background-color); border-radius: 3px; diff --git a/crates/rune/src/doc/static/type.html.hbs b/crates/rune/src/doc/static/type.html.hbs index c2f3de16a..705a4a7b2 100644 --- a/crates/rune/src/doc/static/type.html.hbs +++ b/crates/rune/src/doc/static/type.html.hbs @@ -9,6 +9,7 @@
{{#if this.is_async}}async {{/if}}fn {{this.name}}{{#if this.parameters}}<{{literal this.parameters}}>{{/if}}({{literal this.args}}){{#if this.return_type}} -> {{literal this.return_type}}{{/if}} + {{#if this.deprecated}}
Deprecated:{{this.deprecated}}
{{/if}}
{{#if this.doc}}{{literal this.doc}}{{/if}}
diff --git a/crates/rune/src/module.rs b/crates/rune/src/module.rs index 2aa1e8efe..c99f7ddfc 100644 --- a/crates/rune/src/module.rs +++ b/crates/rune/src/module.rs @@ -200,6 +200,8 @@ pub(crate) struct ModuleFunction { #[cfg(feature = "doc")] pub(crate) is_async: bool, #[cfg(feature = "doc")] + pub(crate) deprecated: Option>, + #[cfg(feature = "doc")] pub(crate) args: Option, #[cfg(feature = "doc")] pub(crate) return_type: Option, @@ -217,6 +219,8 @@ pub(crate) struct ModuleAssociated { #[cfg(feature = "doc")] pub(crate) is_async: bool, #[cfg(feature = "doc")] + pub(crate) deprecated: Option>, + #[cfg(feature = "doc")] pub(crate) args: Option, #[cfg(feature = "doc")] pub(crate) return_type: Option, @@ -291,6 +295,8 @@ pub struct ItemFnMut<'a> { #[cfg(feature = "doc")] is_async: &'a mut bool, #[cfg(feature = "doc")] + deprecated: &'a mut Option>, + #[cfg(feature = "doc")] args: &'a mut Option, #[cfg(feature = "doc")] return_type: &'a mut Option, @@ -321,6 +327,22 @@ impl ItemFnMut<'_> { self } + /// Mark the given item as deprecated. + pub fn deprecated( + self, + #[cfg_attr(not(feature = "doc"), allow(unused))] deprecated: S, + ) -> Self + where + S: AsRef, + { + #[cfg(feature = "doc")] + { + *self.deprecated = Some(deprecated.as_ref().into()); + } + + self + } + /// Indicate the number of arguments this function accepts. pub fn args(self, #[cfg_attr(not(feature = "doc"), allow(unused))] args: usize) -> Self { #[cfg(feature = "doc")] diff --git a/crates/rune/src/module/function_meta.rs b/crates/rune/src/module/function_meta.rs index ebb7c2389..ec22f1c09 100644 --- a/crates/rune/src/module/function_meta.rs +++ b/crates/rune/src/module/function_meta.rs @@ -54,6 +54,8 @@ pub struct FunctionData { #[cfg(feature = "doc")] pub(crate) is_async: bool, #[cfg(feature = "doc")] + pub(crate) deprecated: Option>, + #[cfg(feature = "doc")] pub(crate) args: Option, #[cfg(feature = "doc")] pub(crate) return_type: Option, @@ -78,6 +80,8 @@ impl FunctionData { #[cfg(feature = "doc")] is_async: K::is_async(), #[cfg(feature = "doc")] + deprecated: None, + #[cfg(feature = "doc")] args: Some(F::args()), #[cfg(feature = "doc")] return_type: F::Return::maybe_type_of(), @@ -212,6 +216,8 @@ pub struct AssociatedFunctionData { #[cfg(feature = "doc")] pub(crate) is_async: bool, #[cfg(feature = "doc")] + pub(crate) deprecated: Option>, + #[cfg(feature = "doc")] pub(crate) args: Option, #[cfg(feature = "doc")] pub(crate) return_type: Option, @@ -236,6 +242,8 @@ impl AssociatedFunctionData { #[cfg(feature = "doc")] is_async: K::is_async(), #[cfg(feature = "doc")] + deprecated: None, + #[cfg(feature = "doc")] args: Some(F::args()), #[cfg(feature = "doc")] return_type: F::Return::maybe_type_of(), @@ -337,6 +345,8 @@ where #[cfg(feature = "doc")] is_async: K::is_async(), #[cfg(feature = "doc")] + deprecated: None, + #[cfg(feature = "doc")] args: Some(F::args()), #[cfg(feature = "doc")] return_type: F::Return::maybe_type_of(), diff --git a/crates/rune/src/module/module.rs b/crates/rune/src/module/module.rs index 77eec0fd0..f0567fe3d 100644 --- a/crates/rune/src/module/module.rs +++ b/crates/rune/src/module/module.rs @@ -1125,6 +1125,8 @@ impl Module { #[cfg(feature = "doc")] is_async: false, #[cfg(feature = "doc")] + deprecated: None, + #[cfg(feature = "doc")] args: None, #[cfg(feature = "doc")] return_type: None, @@ -1140,6 +1142,8 @@ impl Module { #[cfg(feature = "doc")] is_async: &mut last.is_async, #[cfg(feature = "doc")] + deprecated: &mut last.deprecated, + #[cfg(feature = "doc")] args: &mut last.args, #[cfg(feature = "doc")] return_type: &mut last.return_type, @@ -1168,6 +1172,8 @@ impl Module { #[cfg(feature = "doc")] is_async: data.is_async, #[cfg(feature = "doc")] + deprecated: data.deprecated, + #[cfg(feature = "doc")] args: data.args, #[cfg(feature = "doc")] return_type: data.return_type, @@ -1183,6 +1189,8 @@ impl Module { #[cfg(feature = "doc")] is_async: &mut last.is_async, #[cfg(feature = "doc")] + deprecated: &mut last.deprecated, + #[cfg(feature = "doc")] args: &mut last.args, #[cfg(feature = "doc")] return_type: &mut last.return_type, @@ -1234,6 +1242,8 @@ impl Module { #[cfg(feature = "doc")] is_async: data.is_async, #[cfg(feature = "doc")] + deprecated: data.deprecated, + #[cfg(feature = "doc")] args: data.args, #[cfg(feature = "doc")] return_type: data.return_type, @@ -1249,6 +1259,8 @@ impl Module { #[cfg(feature = "doc")] is_async: &mut last.is_async, #[cfg(feature = "doc")] + deprecated: &mut last.deprecated, + #[cfg(feature = "doc")] args: &mut last.args, #[cfg(feature = "doc")] return_type: &mut last.return_type, diff --git a/crates/rune/src/modules/core.rs b/crates/rune/src/modules/core.rs index 02f290839..e25066e3e 100644 --- a/crates/rune/src/modules/core.rs +++ b/crates/rune/src/modules/core.rs @@ -43,38 +43,102 @@ fn panic(message: &str) -> VmResult<()> { } /// Test if the given `value` is readable. +/// +/// A value is writable if can be acquired for shared access, such as producing +/// an immutable reference. +/// +/// A value that is moved is no longer considered readable. +/// +/// # Examples +/// +/// ```rune +/// let value = Some(42); +/// assert!(is_readable(value)); +/// let value2 = value.map(|v| v + 1); +/// assert!(!is_readable(value)); +/// assert_eq!(value2, Some(43)); +/// ``` #[rune::function] fn is_readable(value: Value) -> bool { match value { - Value::Any(any) => any.is_readable(), - Value::String(string) => string.is_readable(), - Value::Bytes(bytes) => bytes.is_readable(), - Value::Vec(vec) => vec.is_readable(), - Value::Tuple(tuple) => tuple.is_readable(), - Value::Object(object) => object.is_readable(), - Value::UnitStruct(empty) => empty.is_readable(), - Value::TupleStruct(tuple) => tuple.is_readable(), - Value::Struct(object) => object.is_readable(), - Value::Variant(variant) => variant.is_readable(), - _ => true, + Value::Unit => true, + Value::Bool(_) => true, + Value::Byte(_) => true, + Value::Char(_) => true, + Value::Integer(_) => true, + Value::Float(_) => true, + Value::Type(_) => true, + Value::Ordering(_) => true, + Value::StaticString(_) => true, + Value::String(value) => value.is_readable(), + Value::Bytes(value) => value.is_readable(), + Value::Vec(value) => value.is_readable(), + Value::Tuple(value) => value.is_readable(), + Value::Object(value) => value.is_readable(), + Value::Range(value) => value.is_readable(), + Value::Future(value) => value.is_readable(), + Value::Stream(value) => value.is_readable(), + Value::Generator(value) => value.is_readable(), + Value::GeneratorState(value) => value.is_readable(), + Value::Option(value) => value.is_readable(), + Value::Result(value) => value.is_readable(), + Value::UnitStruct(value) => value.is_readable(), + Value::TupleStruct(value) => value.is_readable(), + Value::Struct(value) => value.is_readable(), + Value::Variant(value) => value.is_readable(), + Value::Function(value) => value.is_readable(), + Value::Format(_) => true, + Value::Iterator(value) => value.is_readable(), + Value::Any(value) => value.is_readable(), } } /// Test if the given `value` is writable. +/// +/// A value is writable if can be acquired for exclusive access, such as +/// producing a mutable reference or taking ownership. +/// +/// # Examples +/// +/// ```rune +/// let value = Some(42); +/// assert!(is_writable(value)); +/// let value2 = value.map(|v| v + 1); +/// assert!(!is_writable(value)); +/// assert_eq!(value2, Some(43)); +/// ``` #[rune::function] fn is_writable(value: Value) -> bool { match value { - Value::Any(any) => any.is_writable(), - Value::String(string) => string.is_writable(), - Value::Bytes(bytes) => bytes.is_writable(), - Value::Vec(vec) => vec.is_writable(), - Value::Tuple(tuple) => tuple.is_writable(), - Value::Object(object) => object.is_writable(), - Value::UnitStruct(empty) => empty.is_writable(), - Value::TupleStruct(tuple) => tuple.is_writable(), - Value::Struct(object) => object.is_writable(), - Value::Variant(variant) => variant.is_writable(), - _ => true, + Value::Unit => true, + Value::Bool(_) => true, + Value::Byte(_) => true, + Value::Char(_) => true, + Value::Integer(_) => true, + Value::Float(_) => true, + Value::Type(_) => true, + Value::Ordering(_) => true, + Value::StaticString(_) => false, + Value::String(value) => value.is_writable(), + Value::Bytes(value) => value.is_writable(), + Value::Vec(value) => value.is_writable(), + Value::Tuple(value) => value.is_writable(), + Value::Object(value) => value.is_writable(), + Value::Range(value) => value.is_writable(), + Value::Future(value) => value.is_writable(), + Value::Stream(value) => value.is_writable(), + Value::Generator(value) => value.is_writable(), + Value::GeneratorState(value) => value.is_writable(), + Value::Option(value) => value.is_writable(), + Value::Result(value) => value.is_writable(), + Value::UnitStruct(value) => value.is_writable(), + Value::TupleStruct(value) => value.is_writable(), + Value::Struct(value) => value.is_writable(), + Value::Variant(value) => value.is_writable(), + Value::Function(value) => value.is_writable(), + Value::Format(_) => false, + Value::Iterator(value) => value.is_writable(), + Value::Any(value) => value.is_writable(), } } diff --git a/crates/rune/src/modules/option.rs b/crates/rune/src/modules/option.rs index c042b10e0..9d023e9fe 100644 --- a/crates/rune/src/modules/option.rs +++ b/crates/rune/src/modules/option.rs @@ -1,8 +1,9 @@ //! The `std::option` module. -use crate::no_std::prelude::*; +use core::fmt; use crate as rune; +use crate::no_std::prelude::*; use crate::runtime::{Function, Iterator, Panic, Protocol, Shared, Value, VmResult}; use crate::{ContextError, Module}; @@ -11,42 +12,203 @@ pub fn module() -> Result { let mut module = Module::with_crate_item("std", ["option"]); module.option(["Option"])?; // Sorted for ease of finding - module.associated_function("and_then", and_then_impl)?; - module.associated_function("expect", expect_impl)?; - module.associated_function("is_none", Option::::is_none)?; - module.associated_function("is_some", Option::::is_some)?; - module.associated_function("iter", option_iter)?; - module.associated_function("map", map_impl)?; - module.associated_function("take", take_impl)?; - module.associated_function("transpose", transpose_impl)?; - module.associated_function("unwrap", unwrap_impl)?; - module.associated_function("unwrap_or", Option::::unwrap_or)?; - module.associated_function("ok_or", Option::::ok_or::)?; + module.function_meta(and_then)?; + module.function_meta(expect)?; + module.function_meta(unwrap)?; + module.function_meta(unwrap_or)?; module.function_meta(unwrap_or_else)?; - module.associated_function(Protocol::INTO_ITER, option_iter)?; + module.function_meta(is_some)?; + module.function_meta(is_none)?; + module.function_meta(iter)?; + module.function_meta(map)?; + module.function_meta(take)?; + module.function_meta(transpose)?; + module.function_meta(ok_or)?; + module.function_meta(ok_or_else)?; + module.associated_function(Protocol::INTO_ITER, __rune_fn__iter)?; Ok(module) } -/// Returns the contained `Some` value or computes it from a closure. +#[rune::function(instance)] +fn and_then(option: &Option, then: Function) -> VmResult> { + match option { + // no need to clone v, passing the same reference forward + Some(v) => VmResult::Ok(vm_try!(then.call::<_, _>((v,)))), + None => VmResult::Ok(None), + } +} + +/// Returns the contained [`Some`] value, consuming the `self` value. +/// +/// # Panics +/// +/// Panics if the value is a [`None`] with a custom panic message provided by +/// `msg`. /// /// # Examples /// /// ```rune -/// let k = 10; -/// assert_eq!(Some(4).unwrap_or_else(|| 2 * k), 4); -/// assert_eq!(None.unwrap_or_else(|| 2 * k), 20); +/// let x = Some("value"); +/// assert_eq!(x.expect("fruits are healthy"), "value"); +/// ``` +/// +/// ```rune,should_panic +/// let x = None; +/// x.expect("fruits are healthy"); // panics with `fruits are healthy` +/// ``` +/// +/// # Recommended Message Style +/// +/// We recommend that `expect` messages are used to describe the reason you +/// _expect_ the `Option` should be `Some`. +/// +/// ```rune,should_panic +/// # let slice = []; +/// let item = slice.get(0).expect("slice should not be empty"); +/// ``` +/// +/// **Hint**: If you're having trouble remembering how to phrase expect error +/// messages remember to focus on the word "should" as in "env variable should +/// be set by blah" or "the given binary should be available and executable by +/// the current user". +/// +/// For more detail on expect message styles and the reasoning behind our +/// recommendation please refer to the section on ["Common Message +/// Styles"](../../std/error/index.html#common-message-styles) in the +/// [`std::error`](../../std/error/index.html) module docs. +#[rune::function(instance)] +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(); + + if let Err(fmt::Error) = vm_try!(message.string_display(&mut s, &mut buf)) { + return VmResult::err(Panic::msg("Failed to format message")); + } + + VmResult::err(Panic::custom(s)) + } + } +} + +/// Returns `true` if the option is a [`Some`] value. +/// +/// # Examples +/// +/// ```rune +/// let x = Some(2); +/// assert_eq!(x.is_some(), true); +/// +/// let x = None; +/// assert_eq!(x.is_some(), false); +/// ``` +#[rune::function(instance)] +fn is_some(this: &Option) -> bool { + this.is_some() +} + +/// Returns `true` if the option is a [`None`] value. +/// +/// # Examples +/// +/// ```rune +/// let x = Some(2); +/// assert_eq!(x.is_none(), false); +/// +/// let x = None; +/// assert_eq!(x.is_none(), true); +/// ``` +#[rune::function(instance)] +fn is_none(this: &Option) -> bool { + this.is_none() +} + +/// Construct an iterator over an optional value. +/// +/// # Examples +/// +/// ```rune +/// let value = Some(1); +/// let it = value.iter(); +/// +/// assert_eq!(Some(1), it.next()); +/// assert_eq!(None, it.next()); +/// +/// let value = None; +/// let it = value.iter(); +/// +/// assert_eq!(None, it.next()); +/// ``` +#[rune::function(instance)] +fn iter(option: Option) -> Iterator { + Iterator::from_double_ended("std::option::Iter", option.into_iter()) +} + +/// Maps an `Option` to `Option` by applying a function to a contained +/// value (if `Some`) or returns `None` (if `None`). +/// +/// # Examples +/// +/// Calculates the length of an `Option<[String]>` as an +/// `Option<[usize]>`, consuming the original: +/// +/// [String]: ../../std/string/struct.String.html "String" +/// +/// ```rune +/// let maybe_some_string = Some(String::from("Hello, World!")); +/// // `Option::map` takes self *by value*, consuming `maybe_some_string` +/// let maybe_some_len = maybe_some_string.map(|s| s.len()); +/// assert_eq!(maybe_some_len, Some(13)); +/// +/// let x = None; +/// assert_eq!(x.map(|s| s.len()), None); +/// ``` +#[rune::function(instance)] +fn map(option: Option, then: Function) -> VmResult> { + match option { + // no need to clone v, passing the same reference forward + Some(v) => VmResult::Ok(Some(vm_try!(then.call::<_, _>((v,))))), + None => VmResult::Ok(None), + } +} + +/// Takes the value out of the option, leaving a [`None`] in its place. +/// +/// # Examples +/// +/// ```rune +/// let x = Some(2); +/// let y = x.take(); +/// assert_eq!(x, None); +/// assert_eq!(y, Some(2)); +/// +/// let x = None; +/// let y = x.take(); +/// assert_eq!(x, None); +/// assert_eq!(y, None); /// ``` #[rune::function(instance)] -fn unwrap_or_else(this: &Option, default: Function) -> VmResult { - VmResult::Ok(if let Some(this) = this { - this.clone() - } else { - vm_try!(default.call(())) - }) +fn take(option: &mut Option) -> Option { + option.take() } -/// Transpose functions, translates an Option> into a `Result, E>`. -fn transpose_impl(this: &Option) -> VmResult { +/// Transposes an `Option` of a [`Result`] into a [`Result`] of an `Option`. +/// +/// [`None`] will be mapped to `[Ok]\([None])`. `[Some]\([Ok]\(\_))` and +/// `[Some]\([Err]\(\_))` will be mapped to `[Ok]\([Some]\(\_))` and +/// `[Err]\(\_)`. +/// +/// # Examples +/// +/// ```rune +/// let x = Ok(Some(5)); +/// let y = Some(Ok(5)); +/// assert_eq!(x, y.transpose()); +/// ``` +#[rune::function(instance)] +fn transpose(this: Option) -> VmResult { let value = match this { Some(value) => value, None => { @@ -56,10 +218,9 @@ fn transpose_impl(this: &Option) -> VmResult { } }; - let result = vm_try!(value.as_result()); - let result = vm_try!(result.borrow_ref()); + let result = vm_try!(vm_try!(value.into_result()).take()); - match &*result { + match result { Ok(ok) => { let some = Value::from(Shared::new(Option::::Some(ok.clone()))); let result = Value::from(Shared::new(Result::::Ok(some))); @@ -72,40 +233,121 @@ fn transpose_impl(this: &Option) -> VmResult { } } -fn option_iter(option: &Option) -> Iterator { - Iterator::from_double_ended("std::option::Iter", option.clone().into_iter()) -} - -fn unwrap_impl(option: Option) -> VmResult { +/// Returns the contained [`Some`] value, consuming the `self` value. +/// +/// Because this function may panic, its use is generally discouraged. Instead, +/// prefer to use pattern matching and handle the [`None`] case explicitly, or +/// call [`unwrap_or`], [`unwrap_or_else`], or [`unwrap_or_default`]. +/// +/// [`unwrap_or`]: Option::unwrap_or +/// [`unwrap_or_else`]: Option::unwrap_or_else +/// [`unwrap_or_default`]: Option::unwrap_or_default +/// +/// # Panics +/// +/// Panics if the self value equals [`None`]. +/// +/// # Examples +/// +/// ```rune +/// let x = Some("air"); +/// assert_eq!(x.unwrap(), "air"); +/// ``` +/// +/// ```rune,should_panic +/// let x = None; +/// assert_eq!(x.unwrap(), "air"); // fails +/// ``` +#[rune::function(instance)] +fn unwrap(option: Option) -> VmResult { match option { Some(some) => VmResult::Ok(some), - None => VmResult::err(Panic::custom("called `Option::unwrap()` on a `None` value")), + None => VmResult::err(Panic::custom("Called `Option::unwrap()` on a `None` value")), } } -fn expect_impl(option: Option, message: &str) -> VmResult { - match option { - Some(some) => VmResult::Ok(some), - None => VmResult::err(Panic::custom(message.to_owned())), - } +/// Returns the contained [`Some`] value or a provided `default`. +/// +/// Arguments passed to `unwrap_or` are eagerly evaluated; if you are passing +/// the result of a function call, it is recommended to use [`unwrap_or_else`], +/// which is lazily evaluated. +/// +/// [`unwrap_or_else`]: Option::unwrap_or_else +/// +/// # Examples +/// +/// ```rune +/// assert_eq!(Some("car").unwrap_or("bike"), "car"); +/// assert_eq!(None.unwrap_or("bike"), "bike"); +/// ``` +#[rune::function(instance)] +fn unwrap_or(this: Option, default: Value) -> Value { + this.unwrap_or(default) } -fn map_impl(option: &Option, then: Function) -> VmResult> { - match option { - // no need to clone v, passing the same reference forward - Some(v) => VmResult::Ok(Some(vm_try!(then.call::<_, _>((v,))))), - None => VmResult::Ok(None), +/// Returns the contained [`Some`] value or computes it from a closure. +/// +/// # Examples +/// +/// ```rune +/// let k = 10; +/// assert_eq!(Some(4).unwrap_or_else(|| 2 * k), 4); +/// assert_eq!(None.unwrap_or_else(|| 2 * k), 20); +/// ``` +#[rune::function(instance)] +fn unwrap_or_else(this: Option, default: Function) -> VmResult { + match this { + Some(value) => VmResult::Ok(value), + None => default.call(()), } } -fn and_then_impl(option: &Option, then: Function) -> VmResult> { - match option { - // no need to clone v, passing the same reference forward - Some(v) => VmResult::Ok(vm_try!(then.call::<_, _>((v,)))), - None => VmResult::Ok(None), - } +/// Transforms the `Option` into a [`Result`], mapping [`Some(v)`] to +/// [`Ok(v)`] and [`None`] to [`Err(err)`]. +/// +/// Arguments passed to `ok_or` are eagerly evaluated; if you are passing the +/// result of a function call, it is recommended to use [`ok_or_else`], which is +/// lazily evaluated. +/// +/// [`Ok(v)`]: Ok +/// [`Err(err)`]: Err +/// [`Some(v)`]: Some +/// [`ok_or_else`]: Option::ok_or_else +/// +/// # Examples +/// +/// ```rune +/// let x = Some("foo"); +/// assert_eq!(x.ok_or(0), Ok("foo")); +/// +/// let x = None; +/// assert_eq!(x.ok_or(0), Err(0)); +/// ``` +#[rune::function(instance)] +fn ok_or(this: Option, err: Value) -> Result { + this.ok_or(err) } -fn take_impl(option: &mut Option) -> Option { - option.take() +/// Transforms the `Option` into a [`Result`], mapping [`Some(v)`] to +/// [`Ok(v)`] and [`None`] to [`Err(err())`]. +/// +/// [`Ok(v)`]: Ok +/// [`Err(err())`]: Err +/// [`Some(v)`]: Some +/// +/// # Examples +/// +/// ```rune +/// let x = Some("foo"); +/// assert_eq!(x.ok_or_else(|| 0), Ok("foo")); +/// +/// let x = None; +/// assert_eq!(x.ok_or_else(|| 0), Err(0)); +/// ``` +#[rune::function(instance)] +fn ok_or_else(this: Option, err: Function) -> VmResult> { + match this { + Some(value) => VmResult::Ok(Ok(value)), + None => VmResult::Ok(Err(vm_try!(err.call(())))), + } } diff --git a/crates/rune/src/modules/result.rs b/crates/rune/src/modules/result.rs index cd3ab9ed9..a51afab54 100644 --- a/crates/rune/src/modules/result.rs +++ b/crates/rune/src/modules/result.rs @@ -197,13 +197,13 @@ fn expect(result: Result, message: Value) -> VmResult { let mut buf = String::new(); if let Err(fmt::Error) = vm_try!(message.string_display(&mut s, &mut buf)) { - return VmResult::err(Panic::msg("failed to format message")); + return VmResult::err(Panic::msg("Failed to format message")); } s.push_str(": "); if let Err(fmt::Error) = vm_try!(err.string_debug(&mut s)) { - return VmResult::err(Panic::msg("failed to format error")); + return VmResult::err(Panic::msg("Failed to format error")); } VmResult::err(Panic::custom(s)) diff --git a/crates/rune/src/modules/string.rs b/crates/rune/src/modules/string.rs index 77edb34c9..a2f051334 100644 --- a/crates/rune/src/modules/string.rs +++ b/crates/rune/src/modules/string.rs @@ -19,7 +19,10 @@ pub fn module() -> Result { module.ty::()?; - module.function_meta(string_from_str)?; + module.function_meta(string_from)?; + module + .function_meta(string_from_str)? + .deprecated("Use String::from instead"); module.function_meta(string_new)?; module.function_meta(string_with_capacity)?; module.function_meta(cmp)?; @@ -40,8 +43,9 @@ pub fn module() -> Result { module.function_meta(shrink_to_fit)?; module.function_meta(char_at)?; module.function_meta(split)?; - // TODO: deprecate this variant. - module.associated_function("split_str", __rune_fn__split)?; + module + .associated_function("split_str", __rune_fn__split)? + .deprecated("Use String::split instead"); module.function_meta(trim)?; module.function_meta(trim_end)?; module.function_meta(replace)?; @@ -138,7 +142,7 @@ fn from_utf8(bytes: &[u8]) -> Result { /// Basic usage: /// /// ```rune -/// let s = String::from_str("hello"); +/// let s = String::from("hello"); /// /// assert_eq!(b"hello", s.as_bytes()); /// assert!(is_readable(s)); @@ -155,12 +159,17 @@ fn as_bytes(s: &str) -> Bytes { /// Basic usage: /// /// ```rune -/// let s = String::from_str("hello"); +/// let s = String::from("hello"); /// assert_eq!(s, "hello"); /// ``` +#[rune::function(path = String::from)] +fn string_from(value: &str) -> String { + String::from(value) +} + #[rune::function(path = String::from_str)] -fn string_from_str(s: &str) -> String { - String::from(s) +fn string_from_str(value: &str) -> String { + String::from(value) } /// Creates a new empty `String`. @@ -332,7 +341,7 @@ fn capacity(this: &String) -> usize { /// Basic usage: /// /// ```rune -/// let s = String::from_str("foo"); +/// let s = String::from("foo"); /// /// s.clear(); /// @@ -378,7 +387,7 @@ fn contains(this: &str, other: &str) -> bool { /// Basic usage: /// /// ```rune -/// let s = String::from_str("abc"); +/// let s = String::from("abc"); /// /// s.push('1'); /// s.push('2'); @@ -398,7 +407,7 @@ fn push(this: &mut String, c: char) { /// Basic usage: /// /// ```rune -/// let s = String::from_str("foo"); +/// let s = String::from("foo"); /// /// s.push_str("bar"); /// @@ -512,7 +521,7 @@ fn reserve_exact(this: &mut String, additional: usize) { /// Basic usage: /// /// ```rune -/// let s = String::from_str("hello"); +/// let s = String::from("hello"); /// /// assert_eq!(b"hello", s.into_bytes()); /// assert!(!is_readable(s)); @@ -579,7 +588,7 @@ fn char_at(s: &str, index: usize) -> Option { /// Basic usage: /// /// ```rune -/// let a = String::from_str("h"); +/// let a = String::from("h"); /// let b = a; /// b.push('i'); /// @@ -603,7 +612,7 @@ fn clone(s: &String) -> String { /// Basic usage: /// /// ```rune -/// let s = String::from_str("foo"); +/// let s = String::from("foo"); /// /// s.reserve(100); /// assert!(s.capacity() >= 100); diff --git a/crates/rune/src/modules/test.rs b/crates/rune/src/modules/test.rs index 4bc8b4246..37166ed57 100644 --- a/crates/rune/src/modules/test.rs +++ b/crates/rune/src/modules/test.rs @@ -146,7 +146,7 @@ pub(crate) fn assert_eq( let right = #right; if !(left == right) { - let message = String::from_str(#message); + let message = String::from(#message); message += format!("\nleft: {:?}", left); message += format!("\nright: {:?}", right); panic(message); @@ -208,7 +208,7 @@ pub(crate) fn assert_ne( let right = #right; if !(left != right) { - let message = String::from_str(#message); + let message = String::from(#message); message += format!("\nleft: {:?}", left); message += format!("\nright: {:?}", right); panic(message); diff --git a/crates/rune/src/query/query.rs b/crates/rune/src/query/query.rs index 594274e44..315267ef1 100644 --- a/crates/rune/src/query/query.rs +++ b/crates/rune/src/query/query.rs @@ -1236,6 +1236,8 @@ impl<'a, 'arena> Query<'a, 'arena> { #[cfg(feature = "doc")] is_async: matches!(f.call, Call::Async | Call::Stream), #[cfg(feature = "doc")] + deprecated: None, + #[cfg(feature = "doc")] args: Some(0), #[cfg(feature = "doc")] return_type: None, @@ -1261,6 +1263,8 @@ impl<'a, 'arena> Query<'a, 'arena> { #[cfg(feature = "doc")] is_async: matches!(f.call, Call::Async | Call::Stream), #[cfg(feature = "doc")] + deprecated: None, + #[cfg(feature = "doc")] args: Some(f.ast.args.len()), #[cfg(feature = "doc")] return_type: None, @@ -1287,6 +1291,8 @@ impl<'a, 'arena> Query<'a, 'arena> { #[cfg(feature = "doc")] is_async: f.ast.async_token.is_some(), #[cfg(feature = "doc")] + deprecated: None, + #[cfg(feature = "doc")] args: Some(f.ast.args.len()), #[cfg(feature = "doc")] return_type: None, diff --git a/crates/rune/src/tests.rs b/crates/rune/src/tests.rs index b2617e21d..6e266e7e6 100644 --- a/crates/rune/src/tests.rs +++ b/crates/rune/src/tests.rs @@ -203,7 +203,7 @@ macro_rules! rune_s { macro_rules! rune_n { ($module:expr, $args:expr, $ty:ty => $($tt:tt)*) => {{ let mut context = $crate::Context::with_default_modules().expect("Failed to build context"); - context.install($module).expect("failed to install native module"); + context.install($module).expect("Failed to install native module"); $crate::tests::run::<_, _, $ty>(&context, stringify!($($tt)*), ["main"], $args).expect("Program ran unsuccessfully") }}; } diff --git a/crates/rune/src/tests/option.rs b/crates/rune/src/tests/option.rs index 08316cb08..59fbc68d5 100644 --- a/crates/rune/src/tests/option.rs +++ b/crates/rune/src/tests/option.rs @@ -67,7 +67,7 @@ fn test_unwrap() { "#, Panic { reason} => { assert_eq!(reason.to_string(), - "called `Option::unwrap()` on a `None` value") + "Called `Option::unwrap()` on a `None` value") } ); } diff --git a/crates/rune/src/tests/result.rs b/crates/rune/src/tests/result.rs index e421ecc1a..b1c55837c 100644 --- a/crates/rune/src/tests/result.rs +++ b/crates/rune/src/tests/result.rs @@ -78,7 +78,7 @@ fn test_unwrap() { "#, Panic { reason } => { assert_eq!(reason.to_string(), - "called `Result::unwrap()` on an `Err` value: \"Error\"") + "Called `Result::unwrap()` on an `Err` value: \"Error\"") } ); } diff --git a/crates/rune/src/tests/stmt_reordering.rs b/crates/rune/src/tests/stmt_reordering.rs index ac6522160..725626a56 100644 --- a/crates/rune/src/tests/stmt_reordering.rs +++ b/crates/rune/src/tests/stmt_reordering.rs @@ -5,7 +5,7 @@ fn test_stmt_reordering() { let len: i64 = rune! { pub fn main() { let len = 0; - let value = String::from_str("Hello"); + let value = String::from("Hello"); len = value.len(); let value2 = drop(value); len diff --git a/crates/rune/src/tests/tuple.rs b/crates/rune/src/tests/tuple.rs index b8783a602..24d64180b 100644 --- a/crates/rune/src/tests/tuple.rs +++ b/crates/rune/src/tests/tuple.rs @@ -14,7 +14,7 @@ fn make_module() -> Result { /// nor its arguments are taken over by the receiving function. #[test] fn test_tuple_ownership() { - let m = make_module().expect("failed to make module"); + let m = make_module().expect("Failed to make module"); rune_n! { &m, diff --git a/crates/rune/src/tests/vm_general.rs b/crates/rune/src/tests/vm_general.rs index c8648ddb4..d4bdcd6d5 100644 --- a/crates/rune/src/tests/vm_general.rs +++ b/crates/rune/src/tests/vm_general.rs @@ -432,7 +432,7 @@ fn test_break_label() { fn test_string_concat() { let out: String = rune! { pub fn main() { - let foo = String::from_str("foo"); + let foo = String::from("foo"); foo += "/bar" + "/baz"; foo } diff --git a/examples/examples/proxy.rs b/examples/examples/proxy.rs index 72b921dac..b330e91d3 100644 --- a/examples/examples/proxy.rs +++ b/examples/examples/proxy.rs @@ -21,7 +21,7 @@ fn main() -> rune::Result<()> { let mut sources = rune::sources! { entry => { pub fn passthrough(my_bytes) { - #{field: String::from_str("hello world"), my_bytes} + #{field: String::from("hello world"), my_bytes} } } }; diff --git a/scripts/book/primitives/primitives.rn b/scripts/book/primitives/primitives.rn index 820efa2a5..87609672e 100644 --- a/scripts/book/primitives/primitives.rn +++ b/scripts/book/primitives/primitives.rn @@ -1,5 +1,5 @@ pub fn main() { - let a = String::from_str("Hello"); + let a = String::from("Hello"); let b = a; a.push_str(" World"); println(a); diff --git a/site/content/posts/2020-12-07-faster-tests.md b/site/content/posts/2020-12-07-faster-tests.md index 648c29a45..35774cc1d 100644 --- a/site/content/posts/2020-12-07-faster-tests.md +++ b/site/content/posts/2020-12-07-faster-tests.md @@ -169,7 +169,7 @@ fn discover_tests() -> io::Result<()> { } fn main() { - discover_tests().expect("failed to discover tests"); + discover_tests().expect("Failed to discover tests"); } ``` diff --git a/tools/builder/src/main.rs b/tools/builder/src/main.rs index 8ff5dd1cf..773e1180e 100644 --- a/tools/builder/src/main.rs +++ b/tools/builder/src/main.rs @@ -140,7 +140,7 @@ where let status = Command::new("cargo").args(args.as_ref()).status()?; if !status.success() { - bail!("failed to run cargo"); + bail!("Failed to run cargo"); } Ok(()) From f64b86d8da47f73b8cec7c0cea97545f2106b090 Mon Sep 17 00:00:00 2001 From: John-John Tedro Date: Fri, 18 Aug 2023 19:45:08 +0200 Subject: [PATCH 6/6] More missing docs --- crates/rune/src/module/module.rs | 8 ++++-- crates/rune/src/modules/core.rs | 14 ++++----- crates/rune/src/modules/i64.rs | 19 ++++++++++++ crates/rune/src/modules/option.rs | 48 ++++++++++++++++++++++++------- 4 files changed, 70 insertions(+), 19 deletions(-) diff --git a/crates/rune/src/module/module.rs b/crates/rune/src/module/module.rs index f0567fe3d..cbf9d0226 100644 --- a/crates/rune/src/module/module.rs +++ b/crates/rune/src/module/module.rs @@ -334,7 +334,7 @@ impl Module { /// let mut module = Module::with_crate("nonstd"); /// module.unit("unit")?; /// # Ok::<_, rune::Error>(()) - pub fn unit(&mut self, name: N) -> Result<(), ContextError> + pub fn unit(&mut self, name: N) -> Result, ContextError> where N: AsRef, { @@ -348,7 +348,11 @@ impl Module { docs: Docs::EMPTY, }); - Ok(()) + let last = self.unit_type.as_mut().unwrap(); + + Ok(ItemMut { + docs: &mut last.docs, + }) } /// Construct the type information for the `GeneratorState` type. diff --git a/crates/rune/src/modules/core.rs b/crates/rune/src/modules/core.rs index e25066e3e..6110ddc9b 100644 --- a/crates/rune/src/modules/core.rs +++ b/crates/rune/src/modules/core.rs @@ -13,13 +13,13 @@ use crate::{ContextError, Module}; pub fn module() -> Result { let mut module = Module::with_crate("std").with_unique("std"); - module.unit("unit")?; - module.ty::()?; - module.ty::()?; - module.ty::()?; - module.ty::()?; - module.ty::()?; - module.ty::()?; + module.unit("unit")?.docs(["The primitive unit type."]); + module.ty::()?.docs(["The primitive boolean type."]); + module.ty::()?.docs(["The primitive character type."]); + module.ty::()?.docs(["The primitive byte type."]); + module.ty::()?.docs(["The primitive float type."]); + module.ty::()?.docs(["The primitive integer type."]); + module.ty::()?.docs(["The tuple type."]); module.function_meta(panic)?; module.function_meta(is_readable)?; diff --git a/crates/rune/src/modules/i64.rs b/crates/rune/src/modules/i64.rs index f759295ec..0478b8e39 100644 --- a/crates/rune/src/modules/i64.rs +++ b/crates/rune/src/modules/i64.rs @@ -4,6 +4,7 @@ use core::cmp::Ordering; use core::num::ParseIntError; use crate as rune; +use crate::no_std::prelude::*; use crate::runtime::{VmErrorKind, VmResult}; use crate::{ContextError, Module}; @@ -44,6 +45,8 @@ pub fn module() -> Result { module.function_meta(is_positive)?; module.function_meta(is_negative)?; + module.function_meta(to_string)?; + module.constant(["MIN"], i64::MIN)?.docs([ "The smallest value that can be represented by this integer type", "(−263).", @@ -532,4 +535,20 @@ fn is_negative(this: i64) -> bool { i64::is_negative(this) } +/// Returns the number as a string. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ```rune +/// assert_eq!((-10).to_string(), "-10"); +/// assert_eq!(10.to_string(), "10"); +/// ``` +#[rune::function(instance)] +#[inline] +fn to_string(this: i64) -> String { + this.to_string() +} + crate::__internal_impl_any!(::std::i64, ParseIntError); diff --git a/crates/rune/src/modules/option.rs b/crates/rune/src/modules/option.rs index 9d023e9fe..802d717f3 100644 --- a/crates/rune/src/modules/option.rs +++ b/crates/rune/src/modules/option.rs @@ -12,7 +12,6 @@ pub fn module() -> Result { let mut module = Module::with_crate_item("std", ["option"]); module.option(["Option"])?; // Sorted for ease of finding - module.function_meta(and_then)?; module.function_meta(expect)?; module.function_meta(unwrap)?; module.function_meta(unwrap_or)?; @@ -20,6 +19,7 @@ pub fn module() -> Result { module.function_meta(is_some)?; module.function_meta(is_none)?; module.function_meta(iter)?; + module.function_meta(and_then)?; module.function_meta(map)?; module.function_meta(take)?; module.function_meta(transpose)?; @@ -29,15 +29,6 @@ pub fn module() -> Result { Ok(module) } -#[rune::function(instance)] -fn and_then(option: &Option, then: Function) -> VmResult> { - match option { - // no need to clone v, passing the same reference forward - Some(v) => VmResult::Ok(vm_try!(then.call::<_, _>((v,)))), - None => VmResult::Ok(None), - } -} - /// Returns the contained [`Some`] value, consuming the `self` value. /// /// # Panics @@ -146,6 +137,43 @@ fn iter(option: Option) -> Iterator { Iterator::from_double_ended("std::option::Iter", option.into_iter()) } +/// Returns [`None`] if the option is [`None`], otherwise calls `f` with the +/// wrapped value and returns the result. +/// +/// Some languages call this operation flatmap. +/// +/// # Examples +/// +/// ```rune +/// fn sq_then_to_string(x) { +/// x.checked_mul(x).map(|sq| sq.to_string()) +/// } +/// +/// assert_eq!(Some(2).and_then(sq_then_to_string), Some(4.to_string())); +/// assert_eq!(Some(1_000_000_000_000_000_000).and_then(sq_then_to_string), None); // overflowed! +/// assert_eq!(None.and_then(sq_then_to_string), None); +/// ``` +/// +/// Often used to chain fallible operations that may return [`None`]. +/// +/// ```rune +/// let arr_2d = [["A0", "A1"], ["B0", "B1"]]; +/// +/// let item_0_1 = arr_2d.get(0).and_then(|row| row.get(1)); +/// assert_eq!(item_0_1, Some("A1")); +/// +/// let item_2_0 = arr_2d.get(2).and_then(|row| row.get(0)); +/// assert_eq!(item_2_0, None); +/// ``` +#[rune::function(instance)] +fn and_then(option: Option, then: Function) -> VmResult> { + match option { + // no need to clone v, passing the same reference forward + Some(v) => VmResult::Ok(vm_try!(then.call::<_, _>((v,)))), + None => VmResult::Ok(None), + } +} + /// Maps an `Option` to `Option` by applying a function to a contained /// value (if `Some`) or returns `None` (if `None`). ///