diff --git a/book/src/template_literals.md b/book/src/template_literals.md index d1de9f243..559851595 100644 --- a/book/src/template_literals.md +++ b/book/src/template_literals.md @@ -30,7 +30,7 @@ It expects a function with the signature `fn(&self, buf: &mut String) -> fmt::Re ```rust,noplaypen use rune::{ContextError, Module}; -use rune::runtime::Protocol; +use rune::runtime::{Protocol, Formatter}; use std::fmt::Write as _; use std::fmt; @@ -40,14 +40,15 @@ pub struct StatusCode { } impl StatusCode { - fn display(&self, buf: &mut String) -> fmt::Result { - write!(buf, "{}", self.inner) + #[rune::function(protocol = STRING_DISPLAY)] + fn string_display(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{}", self.inner) } } pub fn module() -> Result { let mut module = Module::new(["http"]); - module.associated_function(Protocol::STRING_DISPLAY, StatusCode::display)?; + module.function_meta(StatusCode::string_display)?; Ok(module) } ``` diff --git a/crates/rune-core/src/protocol.rs b/crates/rune-core/src/protocol.rs index 007e07b75..186c63dca 100644 --- a/crates/rune-core/src/protocol.rs +++ b/crates/rune-core/src/protocol.rs @@ -97,374 +97,385 @@ macro_rules! define { }; )* ) => { - $( - $(#[$($meta)*])* - $vis const $ident: Protocol = Protocol { - name: $name, - hash: Hash::new($hash), - #[cfg(feature = "doc")] - repr: $repr, - #[cfg(feature = "doc")] - doc: &$doc, - }; + impl Protocol { + $( + $(#[$($meta)*])* + $vis const $ident: Protocol = Protocol { + name: $name, + hash: Hash::new($hash), + #[cfg(feature = "doc")] + repr: $repr, + #[cfg(feature = "doc")] + doc: &$doc, + }; + + $vis const $hash_ident: Hash = Hash::new($hash); + )* + + /// Look up protocol for the given hash. + pub fn from_hash(hash: Hash) -> Option { + match hash { + $( + Self::$hash_ident => { + Some(Self::$ident) + }, + )* + _ => None, + } + } + } - $vis const $hash_ident: Hash = Hash::new($hash); - )* + #[test] + fn ensure_unique_hashes() { + let mut map = ::std::collections::HashMap::<_, &'static str>::new(); - /// Look up protocol for the given hash. - pub fn from_hash(hash: Hash) -> Option { - match hash { - $( - Self::$hash_ident => { - Some(Self::$ident) - }, - )* - _ => None, - } + $( + if let Some(ident) = map.insert($hash, stringify!($ident)) { + panic!("Trying to define protocol hash `{}` for `{}`, but it's already defined for {ident}", $hash, stringify!($ident)); + } + )* } } } -impl Protocol { - define! { - /// The function to access a field. - pub const [GET, GET_HASH]: Protocol = Protocol { - name: "get", - hash: 0x504007af1a8485a4, - repr: Some("let output = $value"), - doc: ["Allows a get operation to work."], - }; - - /// The function to set a field. - pub const [SET, SET_HASH]: Protocol = Protocol { - name: "set", - hash: 0x7d13d47fd8efef5a, - repr: Some("$value = input"), - doc: ["Allows a set operation to work."], - }; - - /// The function to access an index. - pub const [INDEX_GET, INDEX_GET_HASH]: Protocol = Protocol { - name: "index_get", - hash: 0xadb5b27e2a4d2dec, - repr: Some("let output = $value[index]"), - doc: ["Allows an indexing get operation to work."], - }; - - /// The function to set an index. - pub const [INDEX_SET, INDEX_SET_HASH]: Protocol = Protocol { - name: "index_set", - hash: 0x162943f7bd03ad36, - repr: Some("$value[index] = input"), - doc: ["Allows an indexing set operation to work."], - }; - - /// Check two types for partial equality. - pub const [PARTIAL_EQ, PARTIAL_EQ_HASH]: Protocol = Protocol { - name: "partial_eq", - hash: 0x4b6bc4701445e318, - repr: Some("if $value == b { }"), - doc: ["Allows for partial equality operations to work."], - }; - - /// Check two types for total equality. - pub const [EQ, EQ_HASH]: Protocol = Protocol { - name: "eq", - hash: 0x418f5becbf885806, - repr: Some("if $value == b { }"), - doc: ["Allows an equality operation to work."], - }; - - /// Perform an partial comparison between two values. - pub const [PARTIAL_CMP, PARTIAL_CMP_HASH]: Protocol = Protocol { - name: "partial_cmp", - hash: 0x8d4430991253343c, - repr: Some("if $value < b { }"), - doc: ["Allows for partial ordering to work."], - }; - - /// Perform an total comparison between two values. - pub const [CMP, CMP_HASH]: Protocol = Protocol { - name: "cmp", - hash: 0x240f1b75466cd1a3, - repr: Some("if $value < b { }"), - doc: ["Allows for total ordering to work."], - }; - - /// The function to implement for the addition operation. - pub const [ADD, ADD_HASH]: Protocol = Protocol { - name: "add", - hash: 0xe4ecf51fa0bf1076, - repr: Some("let output = $value + b"), - doc: [ - "Allows the `+` operator to apply to values of this type, where the current type is the left-hand side." - ], - }; - - /// The function to implement for the addition assign operation. - pub const [ADD_ASSIGN, ADD_ASSIGN_HASH]: Protocol = Protocol { - name: "add_assign", - hash: 0x42451ccb0a2071a9, - repr: Some("$value += b"), - doc: [ - "Allows the `+=` operator to apply to values of this type, where the current type is the left-hand side." - ], - }; - - /// The function to implement for the subtraction operation. - pub const [SUB, SUB_HASH]: Protocol = Protocol { - name: "sub", - hash: 0x6fa86a5f18d0bf71, - repr: Some("let output = $value - b"), - doc: [ - "Allows the `-` operator to apply to values of this type, where the current type is the left-hand side." - ], - }; - - /// The function to implement for the subtraction assign operation. - pub const [SUB_ASSIGN, SUB_ASSIGN_HASH]: Protocol = Protocol { - name: "sub_assign", - hash: 0x5939bb56a1415284, - repr: Some("$value -= b"), - doc: [ - "Allows the `-=` operator to apply to values of this type, where the current type is the left-hand side." - ], - }; - - /// The function to implement for the multiply operation. - pub const [MUL, MUL_HASH]: Protocol = Protocol { - name: "mul", - hash: 0xb09e99dc94091d1c, - repr: Some("let output = $value * b"), - doc: [ - "Allows the `*` operator to apply to values of this type, where the current type is the left-hand side." - ], - }; - - /// The function to implement for the multiply assign operation. - pub const [MUL_ASSIGN, MUL_ASSIGN_HASH]: Protocol = Protocol { - name: "mul_assign", - hash: 0x29a54b727f980ebf, - repr: Some("$value *= b"), - doc: [ - "Allows the `*=` operator to apply to values of this type, where the current type is the left-hand side." - ], - }; - - /// The function to implement for the division operation. - pub const [DIV, DIV_HASH]: Protocol = Protocol { - name: "div", - hash: 0xf26d6eea1afca6e8, - repr: Some("let output = $value / b"), - doc: [ - "Allows the `/` operator to apply to values of this type, where the current type is the left-hand side." - ], - }; - - /// The function to implement for the division assign operation. - pub const [DIV_ASSIGN, DIV_ASSIGN_HASH]: Protocol = Protocol { - name: "div_assign", - hash: 0x4dd087a8281c04e6, - repr: Some("$value /= b"), - doc: [ - "Allows the `/=` operator to apply to values of this type, where the current type is the left-hand side." - ], - }; - - /// The function to implement for the remainder operation. - pub const [REM, REM_HASH]: Protocol = Protocol { - name: "rem", - hash: 0x5c6293639c74e671, - repr: Some("let output = $value % b"), - doc: [ - "Allows the `%` operator to apply to values of this type, where the current type is the left-hand side." - ], - }; - - /// The function to implement for the remainder assign operation. - pub const [REM_ASSIGN, REM_ASSIGN_HASH]: Protocol = Protocol { - name: "rem_assign", - hash: 0x3a8695980e77baf4, - repr: Some("$value %= b"), - doc: [ - "Allows the `%=` operator to apply to values of this type, where the current type is the left-hand side." - ], - }; - - /// The function to implement for the bitwise and operation. - pub const [BIT_AND, BIT_AND_HASH]: Protocol = Protocol { - name: "bit_and", - hash: 0x0e11f20d940eebe8, - repr: Some("let output = $value & b"), - doc: [ - "Allows the `&` operator to apply to values of this type, where the current type is the left-hand side." - ], - }; - - /// The function to implement for the bitwise and assign operation. - pub const [BIT_AND_ASSIGN, BIT_AND_ASSIGN_HASH]: Protocol = Protocol { - name: "bit_and_assign", - hash: 0x95cb1ba235dfb5ec, - repr: Some("$value &= b"), - doc: [ - "Allows the `&=` operator to apply to values of this type, where the current type is the left-hand side." - ], - }; - - /// The function to implement for the bitwise xor operation. - pub const [BIT_XOR, BIT_XOR_HASH]: Protocol = Protocol { - name: "bit_xor", - hash: 0xa3099c54e1de4cbf, - repr: Some("let output = $value ^ b"), - doc: [ - "Allows the `^` operator to apply to values of this type, where the current type is the left-hand side." - ], - }; - - /// The function to implement for the bitwise xor assign operation. - pub const [BIT_XOR_ASSIGN, BIT_XOR_ASSIGN_HASH]: Protocol = Protocol { - name: "bit_xor_assign", - hash: 0x01fa9706738f9867, - repr: Some("$value ^= b"), - doc: [ - "Allows the `^=` operator to apply to values of this type, where the current type is the left-hand side." - ], - }; - - /// The function to implement for the bitwise or operation. - pub const [BIT_OR, BIT_OR_HASH]: Protocol = Protocol { - name: "bit_or", - hash: 0x05010afceb4a03d0, - repr: Some("let output = $value | b"), - doc: [ - "Allows the `|` operator to apply to values of this type, where the current type is the left-hand side." - ], - }; - - /// The function to implement for the bitwise xor assign operation. - pub const [BIT_OR_ASSIGN, BIT_OR_ASSIGN_HASH]: Protocol = Protocol { - name: "bit_or_assign", - hash: 0x606d79ff1750a7ec, - repr: Some("$value |= b"), - doc: [ - "Allows the `|=` operator to apply to values of this type, where the current type is the left-hand side." - ], - }; - - /// The function to implement for the bitwise shift left operation. - pub const [SHL, SHL_HASH]: Protocol = Protocol { - name: "shl", - hash: 0x6845f7d0cc9e002d, - repr: Some("let output = $value << b"), - doc: [ - "Allows the `<<` operator to apply to values of this type, where the current type is the left-hand side." - ], - }; - - /// The function to implement for the bitwise shift left assign operation. - pub const [SHL_ASSIGN, SHL_ASSIGN_HASH]: Protocol = Protocol { - name: "shl_assign", - hash: 0xdc4702d0307ba27b, - repr: Some("$value <<= b"), - doc: [ - "Allows the `<<=` operator to apply to values of this type, where the current type is the left-hand side." - ], - }; - - /// The function to implement for the bitwise shift right operation. - pub const [SHR, SHR_HASH]: Protocol = Protocol { - name: "shr", - hash: 0x6b485e8e6e58fbc8, - repr: Some("let output = $value >> b"), - doc: [ - "Allows the `>>` operator to apply to values of this type, where the current type is the left-hand side." - ], - }; - - /// The function to implement for the bitwise shift right assign operation. - pub const [SHR_ASSIGN, SHR_ASSIGN_HASH]: Protocol = Protocol { - name: "shr_assign", - hash: 0x61ff7c46ff00e74a, - repr: Some("$value >>= b"), - doc: [ - "Allows the `>>=` operator to apply to values of this type, where the current type is the left-hand side." - ], - }; - - /// Protocol function used by template strings. - pub const [STRING_DISPLAY, STRING_DISPLAY_HASH]: Protocol = Protocol { - name: "string_display", - hash: 0x811b62957ea9d9f9, - repr: Some("println(\"{}\", $value)"), - doc: ["Allows the value to be display printed."], - }; - - /// Protocol function used by custom debug impls. - pub const [STRING_DEBUG, STRING_DEBUG_HASH]: Protocol = Protocol { - name: "string_debug", - hash: 0x4064e3867aaa0717, - repr: Some("println(\"{:?}\", $value)"), - doc: ["Allows the value to be debug printed."], - }; - - /// Function used to convert an argument into an iterator. - pub const [INTO_ITER, INTO_ITER_HASH]: Protocol = Protocol { - name: "into_iter", - hash: 0x15a85c8d774b4065, - repr: Some("for item in $value { }"), - doc: ["Allows the value to be converted into an iterator in a for-loop."], - }; - - /// The function to call to continue iteration. - pub const [NEXT, NEXT_HASH]: Protocol = Protocol { - name: "next", - hash: 0xc3cde069de2ba320, - repr: None, - doc: ["Allows iteration to be advanced for the type, this is used for iterators."], - }; - - /// Function used to convert an argument into a future. - /// - /// Signature: `fn(Value) -> Future`. - pub const [INTO_FUTURE, INTO_FUTURE_HASH]: Protocol = Protocol { - name: "into_future", - hash: 0x596e6428deabfda2, - repr: Some("value.await"), - doc: ["This protocol allows the type to be converted into a future by awaiting them."], - }; - - /// Coerce a value into a type name. This is stored as a constant. - pub const [INTO_TYPE_NAME, INTO_TYPE_NAME_HASH]: Protocol = Protocol { - name: "into_type_name", - hash: 0xbffd08b816c24682, - repr: None, - doc: [ - "This protocol allows the type to be converted into a string which represents the type name.", - ], - }; - - /// Function used to test if a value is a specific variant. - /// - /// Signature: `fn(self, usize) -> bool`. - pub const [IS_VARIANT, IS_VARIANT_HASH]: Protocol = Protocol { - name: "is_variant", - hash: 0xc030d82bbd4dabe8, - repr: None, - doc: ["Test if the provided argument is a variant."], - }; - - /// Function used for the question mark operation. - /// - /// Signature: `fn(self) -> Result`. - /// - /// Note that it uses the `Result` like [`std::ops::Try`] uses - /// [`ControlFlow`](std::ops::ControlFlow) i.e., for `Result::` - /// it should return `Result>` - pub const [TRY, TRY_HASH]: Protocol = Protocol { - name: "try", - hash: 0x5da1a80787003354, - repr: Some("value?"), - doc: ["Allows the `?` operator to apply to values of this type."], - }; - } +define! { + /// The function to access a field. + pub const [GET, GET_HASH]: Protocol = Protocol { + name: "get", + hash: 0x504007af1a8485a4u64, + repr: Some("let output = $value"), + doc: ["Allows a get operation to work."], + }; + + /// The function to set a field. + pub const [SET, SET_HASH]: Protocol = Protocol { + name: "set", + hash: 0x7d13d47fd8efef5au64, + repr: Some("$value = input"), + doc: ["Allows a set operation to work."], + }; + + /// The function to access an index. + pub const [INDEX_GET, INDEX_GET_HASH]: Protocol = Protocol { + name: "index_get", + hash: 0xadb5b27e2a4d2decu64, + repr: Some("let output = $value[index]"), + doc: ["Allows an indexing get operation to work."], + }; + + /// The function to set an index. + pub const [INDEX_SET, INDEX_SET_HASH]: Protocol = Protocol { + name: "index_set", + hash: 0x162943f7bd03ad36u64, + repr: Some("$value[index] = input"), + doc: ["Allows an indexing set operation to work."], + }; + + /// Check two types for partial equality. + pub const [PARTIAL_EQ, PARTIAL_EQ_HASH]: Protocol = Protocol { + name: "partial_eq", + hash: 0x4b6bc4701445e318u64, + repr: Some("if $value == b { }"), + doc: ["Allows for partial equality operations to work."], + }; + + /// Check two types for total equality. + pub const [EQ, EQ_HASH]: Protocol = Protocol { + name: "eq", + hash: 0x418f5becbf885806u64, + repr: Some("if $value == b { }"), + doc: ["Allows an equality operation to work."], + }; + + /// Perform an partial comparison between two values. + pub const [PARTIAL_CMP, PARTIAL_CMP_HASH]: Protocol = Protocol { + name: "partial_cmp", + hash: 0x8d4430991253343cu64, + repr: Some("if $value < b { }"), + doc: ["Allows for partial ordering to work."], + }; + + /// Perform an total comparison between two values. + pub const [CMP, CMP_HASH]: Protocol = Protocol { + name: "cmp", + hash: 0x240f1b75466cd1a3u64, + repr: Some("if $value < b { }"), + doc: ["Allows for total ordering to work."], + }; + + /// The function to implement for the addition operation. + pub const [ADD, ADD_HASH]: Protocol = Protocol { + name: "add", + hash: 0xe4ecf51fa0bf1076u64, + repr: Some("let output = $value + b"), + doc: [ + "Allows the `+` operator to apply to values of this type, where the current type is the left-hand side." + ], + }; + + /// The function to implement for the addition assign operation. + pub const [ADD_ASSIGN, ADD_ASSIGN_HASH]: Protocol = Protocol { + name: "add_assign", + hash: 0x42451ccb0a2071a9u64, + repr: Some("$value += b"), + doc: [ + "Allows the `+=` operator to apply to values of this type, where the current type is the left-hand side." + ], + }; + + /// The function to implement for the subtraction operation. + pub const [SUB, SUB_HASH]: Protocol = Protocol { + name: "sub", + hash: 0x6fa86a5f18d0bf71u64, + repr: Some("let output = $value - b"), + doc: [ + "Allows the `-` operator to apply to values of this type, where the current type is the left-hand side." + ], + }; + + /// The function to implement for the subtraction assign operation. + pub const [SUB_ASSIGN, SUB_ASSIGN_HASH]: Protocol = Protocol { + name: "sub_assign", + hash: 0x5939bb56a1415284u64, + repr: Some("$value -= b"), + doc: [ + "Allows the `-=` operator to apply to values of this type, where the current type is the left-hand side." + ], + }; + + /// The function to implement for the multiply operation. + pub const [MUL, MUL_HASH]: Protocol = Protocol { + name: "mul", + hash: 0xb09e99dc94091d1cu64, + repr: Some("let output = $value * b"), + doc: [ + "Allows the `*` operator to apply to values of this type, where the current type is the left-hand side." + ], + }; + + /// The function to implement for the multiply assign operation. + pub const [MUL_ASSIGN, MUL_ASSIGN_HASH]: Protocol = Protocol { + name: "mul_assign", + hash: 0x29a54b727f980ebfu64, + repr: Some("$value *= b"), + doc: [ + "Allows the `*=` operator to apply to values of this type, where the current type is the left-hand side." + ], + }; + + /// The function to implement for the division operation. + pub const [DIV, DIV_HASH]: Protocol = Protocol { + name: "div", + hash: 0xf26d6eea1afca6e8u64, + repr: Some("let output = $value / b"), + doc: [ + "Allows the `/` operator to apply to values of this type, where the current type is the left-hand side." + ], + }; + + /// The function to implement for the division assign operation. + pub const [DIV_ASSIGN, DIV_ASSIGN_HASH]: Protocol = Protocol { + name: "div_assign", + hash: 0x4dd087a8281c04e6u64, + repr: Some("$value /= b"), + doc: [ + "Allows the `/=` operator to apply to values of this type, where the current type is the left-hand side." + ], + }; + + /// The function to implement for the remainder operation. + pub const [REM, REM_HASH]: Protocol = Protocol { + name: "rem", + hash: 0x5c6293639c74e671u64, + repr: Some("let output = $value % b"), + doc: [ + "Allows the `%` operator to apply to values of this type, where the current type is the left-hand side." + ], + }; + + /// The function to implement for the remainder assign operation. + pub const [REM_ASSIGN, REM_ASSIGN_HASH]: Protocol = Protocol { + name: "rem_assign", + hash: 0x3a8695980e77baf4u64, + repr: Some("$value %= b"), + doc: [ + "Allows the `%=` operator to apply to values of this type, where the current type is the left-hand side." + ], + }; + + /// The function to implement for the bitwise and operation. + pub const [BIT_AND, BIT_AND_HASH]: Protocol = Protocol { + name: "bit_and", + hash: 0x0e11f20d940eebe8u64, + repr: Some("let output = $value & b"), + doc: [ + "Allows the `&` operator to apply to values of this type, where the current type is the left-hand side." + ], + }; + + /// The function to implement for the bitwise and assign operation. + pub const [BIT_AND_ASSIGN, BIT_AND_ASSIGN_HASH]: Protocol = Protocol { + name: "bit_and_assign", + hash: 0x95cb1ba235dfb5ecu64, + repr: Some("$value &= b"), + doc: [ + "Allows the `&=` operator to apply to values of this type, where the current type is the left-hand side." + ], + }; + + /// The function to implement for the bitwise xor operation. + pub const [BIT_XOR, BIT_XOR_HASH]: Protocol = Protocol { + name: "bit_xor", + hash: 0xa3099c54e1de4cbfu64, + repr: Some("let output = $value ^ b"), + doc: [ + "Allows the `^` operator to apply to values of this type, where the current type is the left-hand side." + ], + }; + + /// The function to implement for the bitwise xor assign operation. + pub const [BIT_XOR_ASSIGN, BIT_XOR_ASSIGN_HASH]: Protocol = Protocol { + name: "bit_xor_assign", + hash: 0x01fa9706738f9867u64, + repr: Some("$value ^= b"), + doc: [ + "Allows the `^=` operator to apply to values of this type, where the current type is the left-hand side." + ], + }; + + /// The function to implement for the bitwise or operation. + pub const [BIT_OR, BIT_OR_HASH]: Protocol = Protocol { + name: "bit_or", + hash: 0x05010afceb4a03d0u64, + repr: Some("let output = $value | b"), + doc: [ + "Allows the `|` operator to apply to values of this type, where the current type is the left-hand side." + ], + }; + + /// The function to implement for the bitwise xor assign operation. + pub const [BIT_OR_ASSIGN, BIT_OR_ASSIGN_HASH]: Protocol = Protocol { + name: "bit_or_assign", + hash: 0x606d79ff1750a7ecu64, + repr: Some("$value |= b"), + doc: [ + "Allows the `|=` operator to apply to values of this type, where the current type is the left-hand side." + ], + }; + + /// The function to implement for the bitwise shift left operation. + pub const [SHL, SHL_HASH]: Protocol = Protocol { + name: "shl", + hash: 0x6845f7d0cc9e002du64, + repr: Some("let output = $value << b"), + doc: [ + "Allows the `<<` operator to apply to values of this type, where the current type is the left-hand side." + ], + }; + + /// The function to implement for the bitwise shift left assign operation. + pub const [SHL_ASSIGN, SHL_ASSIGN_HASH]: Protocol = Protocol { + name: "shl_assign", + hash: 0xdc4702d0307ba27bu64, + repr: Some("$value <<= b"), + doc: [ + "Allows the `<<=` operator to apply to values of this type, where the current type is the left-hand side." + ], + }; + + /// The function to implement for the bitwise shift right operation. + pub const [SHR, SHR_HASH]: Protocol = Protocol { + name: "shr", + hash: 0x6b485e8e6e58fbc8u64, + repr: Some("let output = $value >> b"), + doc: [ + "Allows the `>>` operator to apply to values of this type, where the current type is the left-hand side." + ], + }; + + /// The function to implement for the bitwise shift right assign operation. + pub const [SHR_ASSIGN, SHR_ASSIGN_HASH]: Protocol = Protocol { + name: "shr_assign", + hash: 0x61ff7c46ff00e74au64, + repr: Some("$value >>= b"), + doc: [ + "Allows the `>>=` operator to apply to values of this type, where the current type is the left-hand side." + ], + }; + + /// Protocol function used by template strings. + pub const [STRING_DISPLAY, STRING_DISPLAY_HASH]: Protocol = Protocol { + name: "string_display", + hash: 0x811b62957ea9d9f9u64, + repr: Some("println(\"{}\", $value)"), + doc: ["Allows the value to be display printed."], + }; + + /// Protocol function used by custom debug impls. + pub const [STRING_DEBUG, STRING_DEBUG_HASH]: Protocol = Protocol { + name: "string_debug", + hash: 0x4064e3867aaa0717u64, + repr: Some("println(\"{:?}\", $value)"), + doc: ["Allows the value to be debug printed."], + }; + + /// Function used to convert an argument into an iterator. + pub const [INTO_ITER, INTO_ITER_HASH]: Protocol = Protocol { + name: "into_iter", + hash: 0x15a85c8d774b4065u64, + repr: Some("for item in $value { }"), + doc: ["Allows the value to be converted into an iterator in a for-loop."], + }; + + /// The function to call to continue iteration. + pub const [NEXT, NEXT_HASH]: Protocol = Protocol { + name: "next", + hash: 0xc3cde069de2ba320u64, + repr: None, + doc: ["Allows iteration to be advanced for the type, this is used for iterators."], + }; + + /// Function used to convert an argument into a future. + /// + /// Signature: `fn(Value) -> Future`. + pub const [INTO_FUTURE, INTO_FUTURE_HASH]: Protocol = Protocol { + name: "into_future", + hash: 0x596e6428deabfda2u64, + repr: Some("value.await"), + doc: ["This protocol allows the type to be converted into a future by awaiting them."], + }; + + /// Coerce a value into a type name. This is stored as a constant. + pub const [INTO_TYPE_NAME, INTO_TYPE_NAME_HASH]: Protocol = Protocol { + name: "into_type_name", + hash: 0xbffd08b816c24682u64, + repr: None, + doc: [ + "This protocol allows the type to be converted into a string which represents the type name.", + ], + }; + + /// Function used to test if a value is a specific variant. + /// + /// Signature: `fn(self, usize) -> bool`. + pub const [IS_VARIANT, IS_VARIANT_HASH]: Protocol = Protocol { + name: "is_variant", + hash: 0xc030d82bbd4dabe8u64, + repr: None, + doc: ["Test if the provided argument is a variant."], + }; + + /// Function used for the question mark operation. + /// + /// Signature: `fn(self) -> Result`. + /// + /// Note that it uses the `Result` like [`std::ops::Try`] uses + /// [`ControlFlow`](std::ops::ControlFlow) i.e., for `Result::` + /// it should return `Result>` + pub const [TRY, TRY_HASH]: Protocol = Protocol { + name: "try", + hash: 0x5da1a80787003354u64, + repr: Some("value?"), + doc: ["Allows the `?` operator to apply to values of this type."], + }; } diff --git a/crates/rune-macros/src/any.rs b/crates/rune-macros/src/any.rs index 0cfd67297..c4006a53d 100644 --- a/crates/rune-macros/src/any.rs +++ b/crates/rune-macros/src/any.rs @@ -1,9 +1,8 @@ use std::collections::BTreeMap; -use std::mem::take; use proc_macro2::TokenStream; use quote::{quote, quote_spanned, ToTokens}; -use rune_core::{ComponentRef, Hash, ItemBuf}; +use rune_core::Hash; use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::Token; @@ -45,7 +44,11 @@ impl InternalCall { let mut item = self.item.clone(); item.segments.push(syn::PathSegment::from(name.clone())); - let type_hash = build_type_hash(&item); + + let type_hash = match crate::hash::build_type_hash(&item) { + Ok(type_hash) => type_hash, + Err(error) => return Err(vec![error]), + }; let name = syn::LitStr::new(&name.to_string(), name.span()); @@ -99,7 +102,11 @@ impl Derive { }; item.segments.push(syn::PathSegment::from(name.clone())); - let type_hash = build_type_hash(&item); + + let type_hash = match crate::hash::build_type_hash(&item) { + Ok(type_hash) => type_hash, + Err(error) => return Err(vec![error]), + }; let name = syn::LitStr::new(&name.to_string(), name.span()); @@ -107,24 +114,6 @@ impl Derive { } } -fn build_type_hash(item: &syn::Path) -> Hash { - // Construct type hash. - let mut buf = ItemBuf::new(); - let mut first = item.leading_colon.is_some(); - - for s in &item.segments { - let ident = s.ident.to_string(); - - if take(&mut first) { - buf.push(ComponentRef::Crate(&ident)); - } else { - buf.push(ComponentRef::Str(&ident)); - } - } - - Hash::type_hash(&buf) -} - /// Expannd the install into impl. pub(crate) fn expand_install_with( cx: &Context, diff --git a/crates/rune-macros/src/hash.rs b/crates/rune-macros/src/hash.rs new file mode 100644 index 000000000..1b9249c21 --- /dev/null +++ b/crates/rune-macros/src/hash.rs @@ -0,0 +1,38 @@ +use core::mem::take; + +use rune_core::{ComponentRef, Hash, ItemBuf}; + +/// Construct a type hash from a Rust path. +pub(crate) fn build_type_hash(path: &syn::Path) -> syn::Result { + // Construct type hash. + let mut buf = ItemBuf::new(); + let mut first = path.leading_colon.is_some(); + + for s in &path.segments { + let ident = s.ident.to_string(); + + if take(&mut first) { + buf.push(ComponentRef::Crate(&ident)); + } else { + buf.push(ComponentRef::Str(&ident)); + } + + match &s.arguments { + syn::PathArguments::None => {} + syn::PathArguments::AngleBracketed(generics) => { + return Err(syn::Error::new_spanned( + generics, + "Generic arguments are not supported", + )); + } + syn::PathArguments::Parenthesized(generics) => { + return Err(syn::Error::new_spanned( + generics, + "Generic arguments are not supported", + )); + } + } + } + + Ok(Hash::type_hash(&buf)) +} diff --git a/crates/rune-macros/src/lib.rs b/crates/rune-macros/src/lib.rs index 9fd4bd194..50a2feb02 100644 --- a/crates/rune-macros/src/lib.rs +++ b/crates/rune-macros/src/lib.rs @@ -31,6 +31,7 @@ mod any; mod context; mod from_value; mod function; +mod hash; mod inst_display; mod instrument; mod internals; @@ -180,6 +181,30 @@ pub fn any(input: proc_macro::TokenStream) -> proc_macro::TokenStream { derive.expand().unwrap_or_else(to_compile_errors).into() } +/// Calculate a type hash. +/// +/// # Examples +/// +/// ``` +/// use rune_core::Hash; +/// +/// let hash: Hash = rune_macros::hash!(::std::ops::Generator); +/// ``` +#[proc_macro] +pub fn hash(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let path = syn::parse_macro_input!(input as syn::Path); + + let stream = match self::hash::build_type_hash(&path) { + Ok(hash) => { + let hash = hash.into_inner(); + ::quote::quote!(Hash::new(#hash)) + } + Err(error) => to_compile_errors([error]), + }; + + stream.into() +} + #[proc_macro] #[doc(hidden)] pub fn __internal_impl_any(input: proc_macro::TokenStream) -> proc_macro::TokenStream { @@ -212,7 +237,10 @@ pub fn inst_display(input: proc_macro::TokenStream) -> proc_macro::TokenStream { derive.expand().unwrap_or_else(to_compile_errors).into() } -fn to_compile_errors(errors: Vec) -> proc_macro2::TokenStream { +fn to_compile_errors(errors: I) -> proc_macro2::TokenStream +where + I: IntoIterator, +{ let compile_errors = errors.into_iter().map(syn::Error::into_compile_error); ::quote::quote!(#(#compile_errors)*) } diff --git a/crates/rune/src/compile/context.rs b/crates/rune/src/compile/context.rs index 1b24ded2c..dc87427ba 100644 --- a/crates/rune/src/compile/context.rs +++ b/crates/rune/src/compile/context.rs @@ -142,7 +142,6 @@ impl Context { this.install(crate::modules::tuple::module()?)?; this.install(crate::modules::fmt::module()?)?; this.install(crate::modules::future::module()?)?; - this.install(crate::modules::generator::module()?)?; this.install(crate::modules::i64::module()?)?; #[cfg(feature = "std")] this.install(crate::modules::io::module(stdio)?)?; diff --git a/crates/rune/src/module/module.rs b/crates/rune/src/module/module.rs index 025aa52e0..09f7adaac 100644 --- a/crates/rune/src/module/module.rs +++ b/crates/rune/src/module/module.rs @@ -351,12 +351,12 @@ impl Module { /// # Examples /// /// This shows how to register the `GeneratorState` as - /// `nonstd::generator::GeneratorState`. + /// `nonstd::ops::GeneratorState`. /// /// ``` /// use rune::Module; /// - /// let mut module = Module::with_crate_item("nonstd", ["generator"]); + /// let mut module = Module::with_crate_item("nonstd", ["ops"]); /// module.generator_state(["GeneratorState"])?; /// # Ok::<_, rune::Error>(()) pub fn generator_state( @@ -1020,6 +1020,8 @@ impl Module { /// } /// /// impl Client { + /// /// Download a thing. + /// #[rune::function(instance, path = Self::download)] /// async fn download(this: Ref) -> Result<(), DownloadError> { /// /* .. */ /// # Ok(()) @@ -1029,8 +1031,7 @@ impl Module { /// let mut module = Module::default(); /// /// module.ty::()?; - /// module.associated_function("download", Client::download)? - /// .docs(["Download a thing."]); + /// module.function_meta(Client::download)?; /// # Ok::<_, rune::Error>(()) /// ``` pub fn associated_function( diff --git a/crates/rune/src/modules/cmp.rs b/crates/rune/src/modules/cmp.rs index d06b5f98e..e9d05238f 100644 --- a/crates/rune/src/modules/cmp.rs +++ b/crates/rune/src/modules/cmp.rs @@ -4,7 +4,7 @@ use core::cmp::Ordering; use core::fmt::{self, Write}; use crate as rune; -use crate::runtime::{Formatter, Protocol, Value, VmResult}; +use crate::runtime::{Formatter, Value, VmResult}; use crate::{ContextError, Module}; /// Construct the `std::cmp` module. @@ -50,10 +50,8 @@ pub fn module() -> Result { .docs(["An ordering where a compared value is greater than another."]); } - m.associated_function(Protocol::PARTIAL_EQ, |lhs: Ordering, rhs: Ordering| { - lhs == rhs - })?; - m.associated_function(Protocol::EQ, |lhs: Ordering, rhs: Ordering| lhs == rhs)?; + m.function_meta(ordering_partial_eq)?; + m.function_meta(ordering_eq)?; m.function_meta(ordering_string_debug)?; m.function_meta(min)?; m.function_meta(max)?; @@ -104,6 +102,37 @@ fn min(v1: Value, v2: Value) -> VmResult { }) } +/// Perform a partial ordering equality test. +/// +/// # Examples +/// +/// ```rune +/// use std::cmp::Ordering; +/// +/// assert!(Ordering::Less == Ordering::Less); +/// assert!(Ordering::Less != Ordering::Equal); +/// ``` +#[rune::function(instance, protocol = PARTIAL_EQ)] +fn ordering_partial_eq(this: Ordering, other: Ordering) -> bool { + this == other +} + +/// Perform a total ordering equality test. +/// +/// # Examples +/// +/// ```rune +/// use std::ops::eq; +/// use std::cmp::Ordering; +/// +/// assert!(eq(Ordering::Less, Ordering::Less)); +/// assert!(!eq(Ordering::Less, Ordering::Equal)); +/// ``` +#[rune::function(instance, protocol = EQ)] +fn ordering_eq(this: Ordering, other: Ordering) -> bool { + this == other +} + /// Debug format [`Ordering`]. /// /// # Examples diff --git a/crates/rune/src/modules/generator.rs b/crates/rune/src/modules/generator.rs index 7b900a07d..c49f3700b 100644 --- a/crates/rune/src/modules/generator.rs +++ b/crates/rune/src/modules/generator.rs @@ -1,16 +1,9 @@ //! The `std::generator` module. -use crate::runtime::{Generator, Protocol, Vm}; use crate::{ContextError, Module}; /// Construct the `std::generator` module. +#[deprecated = "Generators have been moved into std::ops"] pub fn module() -> Result { - let mut module = Module::with_crate_item("std", ["generator"]); - module.ty::>()?; - module.associated_function("next", Generator::::next)?; - module.associated_function("resume", Generator::::resume)?; - module.associated_function("iter", Generator::::into_iterator)?; - module.associated_function(Protocol::INTO_ITER, Generator::::into_iterator)?; - module.generator_state(["GeneratorState"])?; - Ok(module) + Ok(Module::with_crate_item("std", ["generator"])) } diff --git a/crates/rune/src/modules/ops.rs b/crates/rune/src/modules/ops.rs index b8294117b..0646371e3 100644 --- a/crates/rune/src/modules/ops.rs +++ b/crates/rune/src/modules/ops.rs @@ -4,8 +4,8 @@ use core::cmp::Ordering; use crate as rune; use crate::runtime::{ - Function, Protocol, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive, - Value, VmResult, + EnvProtocolCaller, Function, Generator, GeneratorState, Iterator, Protocol, Range, RangeFrom, + RangeFull, RangeInclusive, RangeTo, RangeToInclusive, Value, Vm, VmResult, }; use crate::{ContextError, Module}; @@ -26,10 +26,10 @@ pub fn module() -> Result { "```rune", "let range = 0..;", "", - "assert!(!range.contains::(-10));", - "assert!(range.contains::(5));", - "assert!(range.contains::(10));", - "assert!(range.contains::(20));", + "assert!(!range.contains(-10));", + "assert!(range.contains(5));", + "assert!(range.contains(10));", + "assert!(range.contains(20));", "", "assert!(range is std::ops::RangeFrom);", "```", @@ -54,9 +54,13 @@ pub fn module() -> Result { m.field_function(Protocol::SET, "start", |r: &mut RangeFrom, value: Value| { r.start = value; })?; - m.associated_function(Protocol::INTO_ITER, RangeFrom::iter)?; - m.function_meta(RangeFrom::contains)?; m.function_meta(RangeFrom::iter__meta)?; + m.function_meta(RangeFrom::contains__meta)?; + m.function_meta(RangeFrom::into_iter__meta)?; + m.function_meta(RangeFrom::partial_eq__meta)?; + m.function_meta(RangeFrom::eq__meta)?; + m.function_meta(RangeFrom::partial_cmp__meta)?; + m.function_meta(RangeFrom::cmp__meta)?; } { @@ -69,10 +73,10 @@ pub fn module() -> Result { "```rune", "let range = ..;", "", - "assert!(range.contains::(-10));", - "assert!(range.contains::(5));", - "assert!(range.contains::(10));", - "assert!(range.contains::(20));", + "assert!(range.contains(-10));", + "assert!(range.contains(5));", + "assert!(range.contains(10));", + "assert!(range.contains(20));", "", "assert!(range is std::ops::RangeFull);", "```", @@ -92,10 +96,10 @@ pub fn module() -> Result { "```rune", "let range = 0..=10;", "", - "assert!(!range.contains::(-10));", - "assert!(range.contains::(5));", - "assert!(range.contains::(10));", - "assert!(!range.contains::(20));", + "assert!(!range.contains(-10));", + "assert!(range.contains(5));", + "assert!(range.contains(10));", + "assert!(!range.contains(20));", "", "assert!(range is std::ops::RangeInclusive);", "```", @@ -135,13 +139,13 @@ pub fn module() -> Result { r.end = value; }, )?; - m.associated_function(Protocol::INTO_ITER, RangeInclusive::iter)?; - m.function_meta(RangeInclusive::contains)?; - m.function_meta(RangeInclusive::iter__meta)?.docs([ - "Iterate over the range.", - "", - "This panics if the range is not a well-defined range.", - ]); + m.function_meta(RangeInclusive::iter__meta)?; + m.function_meta(RangeInclusive::contains__meta)?; + m.function_meta(RangeInclusive::into_iter__meta)?; + m.function_meta(RangeInclusive::partial_eq__meta)?; + m.function_meta(RangeInclusive::eq__meta)?; + m.function_meta(RangeInclusive::partial_cmp__meta)?; + m.function_meta(RangeInclusive::cmp__meta)?; } { @@ -155,10 +159,10 @@ pub fn module() -> Result { "", "```rune", "let range = ..=10;", - "assert!(range.contains::(-10));", - "assert!(range.contains::(5));", - "assert!(range.contains::(10));", - "assert!(!range.contains::(20));", + "assert!(range.contains(-10));", + "assert!(range.contains(5));", + "assert!(range.contains(10));", + "assert!(!range.contains(20));", "", "assert!(range is std::ops::RangeToInclusive);", "```", @@ -180,7 +184,11 @@ pub fn module() -> Result { r.end = value; }, )?; - m.function_meta(RangeToInclusive::contains)?; + m.function_meta(RangeToInclusive::contains__meta)?; + m.function_meta(RangeToInclusive::partial_eq__meta)?; + m.function_meta(RangeToInclusive::eq__meta)?; + m.function_meta(RangeToInclusive::partial_cmp__meta)?; + m.function_meta(RangeToInclusive::cmp__meta)?; } { @@ -194,10 +202,10 @@ pub fn module() -> Result { "", "```rune", "let range = ..10;", - "assert!(range.contains::(-10));", - "assert!(range.contains::(5));", - "assert!(!range.contains::(10));", - "assert!(!range.contains::(20));", + "assert!(range.contains(-10));", + "assert!(range.contains(5));", + "assert!(!range.contains(10));", + "assert!(!range.contains(20));", "", "assert!(range is std::ops::RangeTo);", "```", @@ -215,7 +223,11 @@ pub fn module() -> Result { m.field_function(Protocol::SET, "end", |r: &mut RangeTo, value: Value| { r.end = value; })?; - m.function_meta(RangeTo::contains)?; + m.function_meta(RangeTo::contains__meta)?; + m.function_meta(RangeTo::partial_eq__meta)?; + m.function_meta(RangeTo::eq__meta)?; + m.function_meta(RangeTo::partial_cmp__meta)?; + m.function_meta(RangeTo::cmp__meta)?; } { @@ -229,10 +241,10 @@ pub fn module() -> Result { "", "```rune", "let range = 0..10;", - "assert!(!range.contains::(-10));", - "assert!(range.contains::(5));", - "assert!(!range.contains::(10));", - "assert!(!range.contains::(20));", + "assert!(!range.contains(-10));", + "assert!(range.contains(5));", + "assert!(!range.contains(10));", + "assert!(!range.contains(20));", "", "assert!(range is std::ops::Range);", "```", @@ -264,9 +276,13 @@ pub fn module() -> Result { m.field_function(Protocol::SET, "end", |r: &mut Range, value: Value| { r.end = value; })?; - m.associated_function(Protocol::INTO_ITER, Range::iter)?; - m.function_meta(Range::contains)?; m.function_meta(Range::iter__meta)?; + m.function_meta(Range::into_iter__meta)?; + m.function_meta(Range::contains__meta)?; + m.function_meta(Range::partial_eq__meta)?; + m.function_meta(Range::eq__meta)?; + m.function_meta(Range::partial_cmp__meta)?; + m.function_meta(Range::cmp__meta)?; } m.ty::()?.docs([ @@ -296,11 +312,45 @@ pub fn module() -> Result { "```", ]); + { + m.ty::>()?.docs([ + "The return value of a function producing a generator.", + "", + "Functions which contain the `yield` keyword produces generators.", + "", + "# Examples", + "", + "```rune", + "use std::ops::Generator;", + "", + "fn generate() {", + " yield 1;", + " yield 2;", + "}", + "", + "let g = generate();", + "assert!(g is Generator)", + "```", + ]); + + m.function_meta(generator_next)?; + m.function_meta(generator_resume)?; + m.function_meta(generator_iter)?; + m.function_meta(generator_into_iter)?; + } + + { + m.generator_state(["GeneratorState"])? + .docs(["Enum indicating the state of a generator."]); + + m.function_meta(generator_state_partial_eq)?; + m.function_meta(generator_state_eq)?; + } + m.function_meta(partial_eq)?; m.function_meta(eq)?; m.function_meta(partial_cmp)?; m.function_meta(cmp)?; - Ok(m) } @@ -405,3 +455,108 @@ fn partial_cmp(lhs: Value, rhs: Value) -> VmResult> { fn cmp(lhs: Value, rhs: Value) -> VmResult { Value::cmp(&lhs, &rhs) } + +/// Advance a generator producing the next value yielded. +/// +/// Unlike [`Generator::resume`], this can only consume the yielded values. +/// +/// # Examples +/// +/// ```rune +/// use std::ops::{Generator, GeneratorState}; +/// +/// fn generate() { +/// yield 1; +/// yield 2; +/// } +/// +/// let g = generate(); +/// +/// assert_eq!(g.next(), Some(1)); +/// assert_eq!(g.next(), Some(2)); +/// assert_eq!(g.next(), None); +/// `` +#[rune::function(instance, path = next)] +fn generator_next(this: &mut Generator) -> VmResult> { + this.next() +} + +/// Advance a generator producing the next [`GeneratorState`]. +/// +/// # Examples +/// +/// ```rune +/// use std::ops::{Generator, GeneratorState}; +/// +/// fn generate() { +/// let n = yield 1; +/// yield 2 + n; +/// } +/// +/// let g = generate(); +/// +/// assert_eq!(g.resume(()), GeneratorState::Yielded(1)); +/// assert_eq!(g.resume(1), GeneratorState::Yielded(3)); +/// assert_eq!(g.resume(()), GeneratorState::Complete(())); +/// `` +#[rune::function(instance, path = resume)] +fn generator_resume(this: &mut Generator, value: Value) -> VmResult { + this.resume(value) +} + +#[rune::function(instance, path = iter)] +fn generator_iter(this: Generator) -> Iterator { + this.rune_iter() +} + +#[rune::function(instance, protocol = INTO_ITER)] +fn generator_into_iter(this: Generator) -> Iterator { + this.rune_iter() +} + +/// Test for partial equality over a generator state. +/// +/// # Examples +/// +/// ```rune +/// use std::ops::{Generator, GeneratorState}; +/// +/// fn generate() { +/// let n = yield 1; +/// yield 2 + n; +/// } +/// +/// let g = generate(); +/// +/// assert_eq!(g.resume(()), GeneratorState::Yielded(1)); +/// assert_eq!(g.resume(1), GeneratorState::Yielded(3)); +/// assert_eq!(g.resume(()), GeneratorState::Complete(())); +/// `` +#[rune::function(instance, protocol = PARTIAL_EQ)] +fn generator_state_partial_eq(this: &GeneratorState, other: &GeneratorState) -> VmResult { + this.partial_eq_with(other, &mut EnvProtocolCaller) +} + +/// Test for total equality over a generator state. +/// +/// # Examples +/// +/// ```rune +/// use std::ops::{Generator, GeneratorState}; +/// use std::ops::eq; +/// +/// fn generate() { +/// let n = yield 1; +/// yield 2 + n; +/// } +/// +/// let g = generate(); +/// +/// assert!(eq(g.resume(()), GeneratorState::Yielded(1))); +/// assert!(eq(g.resume(1), GeneratorState::Yielded(3))); +/// assert!(eq(g.resume(()), GeneratorState::Complete(()))); +/// `` +#[rune::function(instance, protocol = EQ)] +fn generator_state_eq(this: &GeneratorState, other: &GeneratorState) -> VmResult { + this.eq_with(other, &mut EnvProtocolCaller) +} diff --git a/crates/rune/src/modules/test.rs b/crates/rune/src/modules/test.rs index 37166ed57..17de3f4af 100644 --- a/crates/rune/src/modules/test.rs +++ b/crates/rune/src/modules/test.rs @@ -81,14 +81,14 @@ pub(crate) fn assert( let expanded = message.expand(cx)?; quote!(if !(#expr) { - panic("assertion failed: " + (#expanded)); + ::std::panic("assertion failed: " + (#expanded)); }) } else { let message = format!("assertion failed: {}", cx.stringify(&expr)); let message = cx.lit(&message); quote!(if !(#expr) { - panic(#message); + ::std::panic(#message); }) }; @@ -133,9 +133,9 @@ pub(crate) fn assert_eq( if !(left == right) { let message = #message; - message += format!("\nleft: {:?}", left); - message += format!("\nright: {:?}", right); - panic("assertion failed (left == right): " + message); + message += ::std::fmt::format!("\nleft: {:?}", left); + message += ::std::fmt::format!("\nright: {:?}", right); + ::std::panic("assertion failed (left == right): " + message); } }} } else { @@ -146,10 +146,10 @@ pub(crate) fn assert_eq( let right = #right; if !(left == right) { - let message = String::from(#message); - message += format!("\nleft: {:?}", left); - message += format!("\nright: {:?}", right); - panic(message); + let message = ::std::string::String::from(#message); + message += ::std::fmt::format!("\nleft: {:?}", left); + message += ::std::fmt::format!("\nright: {:?}", right); + ::std::panic(message); } }} }; @@ -195,9 +195,9 @@ pub(crate) fn assert_ne( if !(left != right) { let message = #message; - message += format!("\nleft: {:?}", left); - message += format!("\nright: {:?}", right); - panic("assertion failed (left != right): " + message); + message += ::std::fmt::format!("\nleft: {:?}", left); + message += ::std::fmt::format!("\nright: {:?}", right); + ::std::panic("assertion failed (left != right): " + message); } }} } else { @@ -208,10 +208,10 @@ pub(crate) fn assert_ne( let right = #right; if !(left != right) { - let message = String::from(#message); - message += format!("\nleft: {:?}", left); - message += format!("\nright: {:?}", right); - panic(message); + let message = ::std::string::String::from(#message); + message += ::std::fmt::format!("\nleft: {:?}", left); + message += ::std::fmt::format!("\nright: {:?}", right); + ::std::panic(message); } }} }; diff --git a/crates/rune/src/runtime/generator.rs b/crates/rune/src/runtime/generator.rs index 7820f0f68..415b9532f 100644 --- a/crates/rune/src/runtime/generator.rs +++ b/crates/rune/src/runtime/generator.rs @@ -74,26 +74,26 @@ impl Generator<&mut Vm> { impl Generator { /// Convert into iterator - pub fn into_iterator(self) -> Iterator { - Iterator::from("std::generator::GeneratorIterator", self.into_iter()) + pub fn rune_iter(self) -> Iterator { + Iterator::from("std::ops::generator::Iter", self.into_iter()) } } impl IntoIterator for Generator { type Item = VmResult; - type IntoIter = GeneratorIterator; + type IntoIter = Iter; #[inline] fn into_iter(self) -> Self::IntoIter { - GeneratorIterator { generator: self } + Iter { generator: self } } } -pub struct GeneratorIterator { +pub struct Iter { generator: Generator, } -impl iter::Iterator for GeneratorIterator { +impl iter::Iterator for Iter { type Item = VmResult; #[inline] diff --git a/crates/rune/src/runtime/generator_state.rs b/crates/rune/src/runtime/generator_state.rs index 142e591b9..ca49906b7 100644 --- a/crates/rune/src/runtime/generator_state.rs +++ b/crates/rune/src/runtime/generator_state.rs @@ -1,6 +1,6 @@ use crate::compile::Named; use crate::module::InstallWith; -use crate::runtime::{RawStr, Value, VmResult}; +use crate::runtime::{ProtocolCaller, RawStr, Value, VmResult}; /// The state of a generator. /// @@ -66,6 +66,34 @@ impl GeneratorState { pub fn is_complete(&self) -> bool { matches!(self, Self::Complete(..)) } + + pub(crate) fn partial_eq_with( + &self, + other: &Self, + caller: &mut impl ProtocolCaller, + ) -> VmResult { + match (self, other) { + (GeneratorState::Yielded(a), GeneratorState::Yielded(b)) => { + Value::partial_eq_with(a, b, caller) + } + (GeneratorState::Complete(a), GeneratorState::Complete(b)) => { + Value::partial_eq_with(a, b, caller) + } + _ => VmResult::Ok(false), + } + } + + pub(crate) fn eq_with(&self, other: &Self, caller: &mut impl ProtocolCaller) -> VmResult { + match (self, other) { + (GeneratorState::Yielded(a), GeneratorState::Yielded(b)) => { + Value::eq_with(a, b, caller) + } + (GeneratorState::Complete(a), GeneratorState::Complete(b)) => { + Value::eq_with(a, b, caller) + } + _ => VmResult::Ok(false), + } + } } from_value!(GeneratorState, into_generator_state); diff --git a/crates/rune/src/runtime/range.rs b/crates/rune/src/runtime/range.rs index 7916a48b2..f0e3f8f0f 100644 --- a/crates/rune/src/runtime/range.rs +++ b/crates/rune/src/runtime/range.rs @@ -6,7 +6,8 @@ use crate as rune; use crate::compile::Named; use crate::module::InstallWith; use crate::runtime::{ - FromValue, Iterator, ProtocolCaller, RawStr, ToValue, Value, VmErrorKind, VmResult, + EnvProtocolCaller, FromValue, Iterator, ProtocolCaller, RawStr, ToValue, Value, VmErrorKind, + VmResult, }; /// Struct representing a dynamic anonymous object. @@ -75,71 +76,182 @@ impl Range { } } + /// Iterate over the range. + /// + /// # Panics + /// + /// This panics if the range is not a well-defined range. + /// + /// # Examples + /// + /// ```rune + /// let vec = []; + /// + /// for value in 'a'..'e' { + /// vec.push(value); + /// } + /// + /// assert_eq!(vec, ['a', 'b', 'c', 'd']); + /// ``` + /// + /// Cannot construct an iterator over floats: + /// + /// ```rune,should_panic + /// for value in 1.0..2.0 { + /// } + /// ``` + #[rune::function(keep, protocol = INTO_ITER)] + pub fn into_iter(&self) -> VmResult { + self.iter() + } + + /// Test the range for partial equality. + /// + /// # Examples + /// + /// ```rune + /// let range = 'a'..'e'; + /// assert!(range == ('a'..'e')); + /// assert!(range != ('b'..'e')); + /// + /// let range = 1.0..2.0; + /// assert!(range == (1.0..2.0)); + /// assert!(range != (f64::NAN..2.0)); + /// assert!((f64::NAN..2.0) != (f64::NAN..2.0)); + /// ``` + #[rune::function(keep, protocol = PARTIAL_EQ)] + pub fn partial_eq(&self, other: &Self) -> VmResult { + self.partial_eq_with(other, &mut EnvProtocolCaller) + } + pub(crate) fn partial_eq_with( - a: &Self, + &self, b: &Self, caller: &mut impl ProtocolCaller, ) -> VmResult { - if !vm_try!(Value::partial_eq_with(&a.start, &b.start, caller)) { + if !vm_try!(Value::partial_eq_with(&self.start, &b.start, caller)) { return VmResult::Ok(false); } - Value::partial_eq_with(&a.end, &b.end, caller) + Value::partial_eq_with(&self.end, &b.end, caller) } - pub(crate) fn eq_with(a: &Self, b: &Self, caller: &mut impl ProtocolCaller) -> VmResult { - if !vm_try!(Value::eq_with(&a.start, &b.start, caller)) { + /// Test the range for total equality. + /// + /// # Examples + /// + /// ```rune + /// use std::ops::eq; + /// + /// let range = 'a'..'e'; + /// assert!(eq(range, 'a'..'e')); + /// assert!(!eq(range, 'b'..'e')); + /// ``` + #[rune::function(keep, protocol = EQ)] + pub fn eq(&self, other: &Self) -> VmResult { + self.eq_with(other, &mut EnvProtocolCaller) + } + + pub(crate) fn eq_with(&self, b: &Self, caller: &mut impl ProtocolCaller) -> VmResult { + if !vm_try!(Value::eq_with(&self.start, &b.start, caller)) { return VmResult::Ok(false); } - Value::eq_with(&a.end, &b.end, caller) + Value::eq_with(&self.end, &b.end, caller) + } + + /// Test the range for partial ordering. + /// + /// # Examples + /// + /// ```rune + /// assert!(('a'..'e') < ('b'..'e')); + /// assert!(('c'..'e') > ('b'..'e')); + /// assert!(!((f64::NAN..2.0) > (f64::INFINITY..2.0))); + /// assert!(!((f64::NAN..2.0) < (f64::INFINITY..2.0))); + /// ``` + #[rune::function(keep, protocol = PARTIAL_CMP)] + pub fn partial_cmp(&self, other: &Self) -> VmResult> { + self.partial_cmp_with(other, &mut EnvProtocolCaller) } pub(crate) fn partial_cmp_with( - a: &Self, + &self, b: &Self, caller: &mut impl ProtocolCaller, ) -> VmResult> { - match vm_try!(Value::partial_cmp_with(&a.start, &b.start, caller)) { + match vm_try!(Value::partial_cmp_with(&self.start, &b.start, caller)) { Some(Ordering::Equal) => (), other => return VmResult::Ok(other), } - Value::partial_cmp_with(&a.end, &b.end, caller) + Value::partial_cmp_with(&self.end, &b.end, caller) + } + + /// Test the range for total ordering. + /// + /// # Examples + /// + /// ```rune + /// use std::ops::cmp; + /// use std::cmp::Ordering; + /// + /// assert_eq!(cmp('a'..'e', 'b'..'e'), Ordering::Less); + /// assert_eq!(cmp('c'..'e', 'b'..'e'), Ordering::Greater); + /// ``` + #[rune::function(keep, protocol = CMP)] + pub fn cmp(&self, other: &Self) -> VmResult { + self.cmp_with(other, &mut EnvProtocolCaller) } pub(crate) fn cmp_with( - a: &Self, + &self, b: &Self, caller: &mut impl ProtocolCaller, ) -> VmResult { - match vm_try!(Value::cmp_with(&a.start, &b.start, caller)) { + match vm_try!(Value::cmp_with(&self.start, &b.start, caller)) { Ordering::Equal => (), other => return VmResult::Ok(other), } - Value::cmp_with(&a.end, &b.end, caller) + Value::cmp_with(&self.end, &b.end, caller) } - /// Test if the range contains the given integer. + /// Test if the range contains the given value. + /// + /// The check is performed using the [`PARTIAL_CMP`] protocol. /// /// # Examples /// /// ```rune /// let range = 0..10; /// - /// assert!(!range.contains::(-10)); - /// assert!(range.contains::(5)); - /// assert!(!range.contains::(10)); - /// assert!(!range.contains::(20)); + /// assert!(!range.contains(-10)); + /// assert!(range.contains(5)); + /// assert!(!range.contains(10)); + /// assert!(!range.contains(20)); /// /// assert!(range is std::ops::Range); /// ``` - #[rune::function(path = contains::)] - pub(crate) fn contains(&self, n: i64) -> VmResult { - let start: i64 = vm_try!(FromValue::from_value(self.start.clone())); - let end: i64 = vm_try!(FromValue::from_value(self.end.clone())); - VmResult::Ok((start..end).contains(&n)) + #[rune::function(keep)] + pub(crate) fn contains(&self, value: Value) -> VmResult { + self.contains_with(value, &mut EnvProtocolCaller) + } + + pub(crate) fn contains_with( + &self, + value: Value, + caller: &mut impl ProtocolCaller, + ) -> VmResult { + match vm_try!(Value::partial_cmp_with(&self.start, &value, caller)) { + Some(Ordering::Less | Ordering::Equal) => {} + _ => return VmResult::Ok(false), + } + + VmResult::Ok(matches!( + vm_try!(Value::partial_cmp_with(&self.end, &value, caller)), + Some(Ordering::Greater) + )) } } diff --git a/crates/rune/src/runtime/range_from.rs b/crates/rune/src/runtime/range_from.rs index eb35951c2..a70883be0 100644 --- a/crates/rune/src/runtime/range_from.rs +++ b/crates/rune/src/runtime/range_from.rs @@ -6,7 +6,8 @@ use crate as rune; use crate::compile::Named; use crate::module::InstallWith; use crate::runtime::{ - FromValue, Iterator, ProtocolCaller, RawStr, ToValue, Value, VmErrorKind, VmResult, + EnvProtocolCaller, FromValue, Iterator, ProtocolCaller, RawStr, ToValue, Value, VmErrorKind, + VmResult, }; /// Struct representing an open range `start..`. @@ -65,52 +66,165 @@ impl RangeFrom { } } + /// Build an iterator over the range. + /// + /// # Panics + /// + /// This panics if the range is not a well-defined range. + /// + /// # Examples + /// + /// ```rune + /// let vec = []; + /// + /// for value in 'a'.. { + /// vec.push(value); + /// + /// if value == 'e' { + /// break; + /// } + /// } + /// + /// assert_eq!(vec, ['a', 'b', 'c', 'd', 'e']); + /// ``` + /// + /// Cannot construct an iterator over floats: + /// + /// ```rune,should_panic + /// let range = 1.0..; + /// + /// for value in 1.0 .. { + /// } + /// ``` + #[rune::function(keep, protocol = INTO_ITER)] + pub fn into_iter(&self) -> VmResult { + self.iter() + } + + /// Test the range for partial equality. + /// + /// # Examples + /// + /// ```rune + /// let range = 'a'..; + /// assert!(range == ('a'..)); + /// assert!(range != ('b'..)); + /// + /// let range = 1.0..; + /// assert!(range == (1.0..)); + /// assert!(range != (f64::NAN..)); + /// assert!((f64::NAN..) != (f64::NAN..)); + /// ``` + #[rune::function(keep, protocol = PARTIAL_EQ)] + pub fn partial_eq(&self, other: &Self) -> VmResult { + self.partial_eq_with(other, &mut EnvProtocolCaller) + } + pub(crate) fn partial_eq_with( - a: &Self, + &self, b: &Self, caller: &mut impl ProtocolCaller, ) -> VmResult { - Value::partial_eq_with(&a.start, &b.start, caller) + Value::partial_eq_with(&self.start, &b.start, caller) } - pub(crate) fn eq_with(a: &Self, b: &Self, caller: &mut impl ProtocolCaller) -> VmResult { - Value::eq_with(&a.start, &b.start, caller) + /// Test the range for total equality. + /// + /// # Examples + /// + /// ```rune + /// use std::ops::eq; + /// + /// let range = 'a'..; + /// assert!(eq(range, 'a'..)); + /// assert!(!eq(range, 'b'..)); + /// ``` + #[rune::function(keep, protocol = EQ)] + pub fn eq(&self, other: &Self) -> VmResult { + self.eq_with(other, &mut EnvProtocolCaller) + } + + pub(crate) fn eq_with(&self, b: &Self, caller: &mut impl ProtocolCaller) -> VmResult { + Value::eq_with(&self.start, &b.start, caller) + } + + /// Test the range for partial ordering. + /// + /// # Examples + /// + /// ```rune + /// assert!(('a'..) < ('b'..)); + /// assert!(('c'..) > ('b'..)); + /// assert!(!((f64::NAN..) > (f64::INFINITY..))); + /// assert!(!((f64::NAN..) < (f64::INFINITY..))); + /// ``` + #[rune::function(keep, protocol = PARTIAL_CMP)] + pub fn partial_cmp(&self, other: &Self) -> VmResult> { + self.partial_cmp_with(other, &mut EnvProtocolCaller) } pub(crate) fn partial_cmp_with( - a: &Self, + &self, b: &Self, caller: &mut impl ProtocolCaller, ) -> VmResult> { - Value::partial_cmp_with(&a.start, &b.start, caller) + Value::partial_cmp_with(&self.start, &b.start, caller) + } + + /// Test the range for total ordering. + /// + /// # Examples + /// + /// ```rune + /// use std::ops::cmp; + /// use std::cmp::Ordering; + /// + /// assert_eq!(cmp('a'.., 'b'..), Ordering::Less); + /// assert_eq!(cmp('c'.., 'b'..), Ordering::Greater); + /// ``` + #[rune::function(keep, protocol = CMP)] + pub fn cmp(&self, other: &Self) -> VmResult { + self.cmp_with(other, &mut EnvProtocolCaller) } pub(crate) fn cmp_with( - a: &Self, + &self, b: &Self, caller: &mut impl ProtocolCaller, ) -> VmResult { - Value::cmp_with(&a.start, &b.start, caller) + Value::cmp_with(&self.start, &b.start, caller) } - /// Test if the range contains the given integer. + /// Test if the range contains the given value. + /// + /// The check is performed using the [`PARTIAL_CMP`] protocol. /// /// # Examples /// /// ```rune /// let range = 0..; /// - /// assert!(!range.contains::(-10)); - /// assert!(range.contains::(5)); - /// assert!(range.contains::(10)); - /// assert!(range.contains::(20)); + /// assert!(!range.contains(-10)); + /// assert!(range.contains(5)); + /// assert!(range.contains(10)); + /// assert!(range.contains(20)); /// /// assert!(range is std::ops::RangeFrom); /// ``` - #[rune::function(path = contains::)] - pub(crate) fn contains(&self, n: i64) -> VmResult { - let start: i64 = vm_try!(FromValue::from_value(self.start.clone())); - VmResult::Ok((start..).contains(&n)) + #[rune::function(keep)] + pub(crate) fn contains(&self, value: Value) -> VmResult { + self.contains_with(value, &mut EnvProtocolCaller) + } + + pub(crate) fn contains_with( + &self, + value: Value, + caller: &mut impl ProtocolCaller, + ) -> VmResult { + VmResult::Ok(matches!( + vm_try!(Value::partial_cmp_with(&self.start, &value, caller)), + Some(Ordering::Less | Ordering::Equal) + )) } } diff --git a/crates/rune/src/runtime/range_full.rs b/crates/rune/src/runtime/range_full.rs index 2e6da1bac..9f6c14267 100644 --- a/crates/rune/src/runtime/range_full.rs +++ b/crates/rune/src/runtime/range_full.rs @@ -50,22 +50,24 @@ impl RangeFull { VmResult::Ok(Ordering::Equal) } - /// Test if the range contains the given integer. + /// Test if the range contains the given value. + /// + /// The check is performed using the [`PARTIAL_CMP`] protocol. /// /// # Examples /// /// ```rune /// let range = ..; /// - /// assert!(range.contains::(-10)); - /// assert!(range.contains::(5)); - /// assert!(range.contains::(10)); - /// assert!(range.contains::(20)); + /// assert!(range.contains(-10)); + /// assert!(range.contains(5)); + /// assert!(range.contains(10)); + /// assert!(range.contains(20)); /// /// assert!(range is std::ops::RangeFull); /// ``` - #[rune::function(path = contains::)] - pub(crate) fn contains(&self, _: i64) -> VmResult { + #[rune::function] + pub(crate) fn contains(&self, _: Value) -> VmResult { VmResult::Ok(true) } } diff --git a/crates/rune/src/runtime/range_inclusive.rs b/crates/rune/src/runtime/range_inclusive.rs index f191d9aac..3fa418b59 100644 --- a/crates/rune/src/runtime/range_inclusive.rs +++ b/crates/rune/src/runtime/range_inclusive.rs @@ -6,7 +6,8 @@ use crate as rune; use crate::compile::Named; use crate::module::InstallWith; use crate::runtime::{ - FromValue, Iterator, ProtocolCaller, RawStr, ToValue, Value, VmErrorKind, VmResult, + EnvProtocolCaller, FromValue, Iterator, ProtocolCaller, RawStr, ToValue, Value, VmErrorKind, + VmResult, }; /// Struct representing a range `start..=end`. @@ -75,71 +76,182 @@ impl RangeInclusive { } } + /// Iterate over the range. + /// + /// # Panics + /// + /// This panics if the range is not a well-defined range. + /// + /// # Examples + /// + /// ```rune + /// let vec = []; + /// + /// for value in 'a'..='e' { + /// vec.push(value); + /// } + /// + /// assert_eq!(vec, ['a', 'b', 'c', 'd', 'e']); + /// ``` + /// + /// Cannot construct an iterator over floats: + /// + /// ```rune,should_panic + /// for value in 1.0..=2.0 { + /// } + /// ``` + #[rune::function(keep, protocol = INTO_ITER)] + pub fn into_iter(&self) -> VmResult { + self.iter() + } + + /// Test the range for partial equality. + /// + /// # Examples + /// + /// ```rune + /// let range = 'a'..='e'; + /// assert!(range == ('a'..='e')); + /// assert!(range != ('b'..='e')); + /// + /// let range = 1.0..=2.0; + /// assert!(range == (1.0..=2.0)); + /// assert!(range != (f64::NAN..=2.0)); + /// assert!((f64::NAN..=2.0) != (f64::NAN..=2.0)); + /// ``` + #[rune::function(keep, protocol = PARTIAL_EQ)] + pub fn partial_eq(&self, other: &Self) -> VmResult { + self.partial_eq_with(other, &mut EnvProtocolCaller) + } + pub(crate) fn partial_eq_with( - a: &Self, + &self, b: &Self, caller: &mut impl ProtocolCaller, ) -> VmResult { - if !vm_try!(Value::partial_eq_with(&a.start, &b.start, caller)) { + if !vm_try!(Value::partial_eq_with(&self.start, &b.start, caller)) { return VmResult::Ok(false); } - Value::partial_eq_with(&a.end, &b.end, caller) + Value::partial_eq_with(&self.end, &b.end, caller) } - pub(crate) fn eq_with(a: &Self, b: &Self, caller: &mut impl ProtocolCaller) -> VmResult { - if !vm_try!(Value::eq_with(&a.start, &b.start, caller)) { + /// Test the range for total equality. + /// + /// # Examples + /// + /// ```rune + /// use std::ops::eq; + /// + /// let range = 'a'..='e'; + /// assert!(eq(range, 'a'..='e')); + /// assert!(!eq(range, 'b'..='e')); + /// ``` + #[rune::function(keep, protocol = EQ)] + pub fn eq(&self, other: &Self) -> VmResult { + self.eq_with(other, &mut EnvProtocolCaller) + } + + pub(crate) fn eq_with(&self, b: &Self, caller: &mut impl ProtocolCaller) -> VmResult { + if !vm_try!(Value::eq_with(&self.start, &b.start, caller)) { return VmResult::Ok(false); } - Value::eq_with(&a.end, &b.end, caller) + Value::eq_with(&self.end, &b.end, caller) + } + + /// Test the range for partial ordering. + /// + /// # Examples + /// + /// ```rune + /// assert!(('a'..='e') < ('b'..='e')); + /// assert!(('c'..='e') > ('b'..='e')); + /// assert!(!((f64::NAN..=2.0) > (f64::INFINITY..=2.0))); + /// assert!(!((f64::NAN..=2.0) < (f64::INFINITY..=2.0))); + /// ``` + #[rune::function(keep, protocol = PARTIAL_CMP)] + pub fn partial_cmp(&self, other: &Self) -> VmResult> { + self.partial_cmp_with(other, &mut EnvProtocolCaller) } pub(crate) fn partial_cmp_with( - a: &Self, + &self, b: &Self, caller: &mut impl ProtocolCaller, ) -> VmResult> { - match vm_try!(Value::partial_cmp_with(&a.start, &b.start, caller)) { + match vm_try!(Value::partial_cmp_with(&self.start, &b.start, caller)) { Some(Ordering::Equal) => (), other => return VmResult::Ok(other), } - Value::partial_cmp_with(&a.end, &b.end, caller) + Value::partial_cmp_with(&self.end, &b.end, caller) + } + + /// Test the range for total ordering. + /// + /// # Examples + /// + /// ```rune + /// use std::ops::cmp; + /// use std::cmp::Ordering; + /// + /// assert_eq!(cmp('a'..='e', 'b'..='e'), Ordering::Less); + /// assert_eq!(cmp('c'..='e', 'b'..='e'), Ordering::Greater); + /// ``` + #[rune::function(keep, protocol = CMP)] + pub fn cmp(&self, other: &Self) -> VmResult { + self.cmp_with(other, &mut EnvProtocolCaller) } pub(crate) fn cmp_with( - a: &Self, + &self, b: &Self, caller: &mut impl ProtocolCaller, ) -> VmResult { - match vm_try!(Value::cmp_with(&a.start, &b.start, caller)) { + match vm_try!(Value::cmp_with(&self.start, &b.start, caller)) { Ordering::Equal => (), other => return VmResult::Ok(other), } - Value::cmp_with(&a.end, &b.end, caller) + Value::cmp_with(&self.end, &b.end, caller) } - /// Test if the range contains the given integer. + /// Test if the range contains the given value. + /// + /// The check is performed using the [`PARTIAL_CMP`] protocol. /// /// # Examples /// /// ```rune /// let range = 0..=10; /// - /// assert!(!range.contains::(-10)); - /// assert!(range.contains::(5)); - /// assert!(range.contains::(10)); - /// assert!(!range.contains::(20)); + /// assert!(!range.contains(-10)); + /// assert!(range.contains(5)); + /// assert!(range.contains(10)); + /// assert!(!range.contains(20)); /// /// assert!(range is std::ops::RangeInclusive); /// ``` - #[rune::function(path = contains::)] - pub(crate) fn contains(&self, n: i64) -> VmResult { - let start: i64 = vm_try!(FromValue::from_value(self.start.clone())); - let end: i64 = vm_try!(FromValue::from_value(self.end.clone())); - VmResult::Ok((start..=end).contains(&n)) + #[rune::function(keep)] + pub(crate) fn contains(&self, value: Value) -> VmResult { + self.contains_with(value, &mut EnvProtocolCaller) + } + + pub(crate) fn contains_with( + &self, + value: Value, + caller: &mut impl ProtocolCaller, + ) -> VmResult { + match vm_try!(Value::partial_cmp_with(&self.start, &value, caller)) { + Some(Ordering::Less | Ordering::Equal) => {} + _ => return VmResult::Ok(false), + } + + VmResult::Ok(matches!( + vm_try!(Value::partial_cmp_with(&self.end, &value, caller)), + Some(Ordering::Greater | Ordering::Equal) + )) } } diff --git a/crates/rune/src/runtime/range_to.rs b/crates/rune/src/runtime/range_to.rs index bf232b7f4..495344af5 100644 --- a/crates/rune/src/runtime/range_to.rs +++ b/crates/rune/src/runtime/range_to.rs @@ -5,7 +5,9 @@ use core::ops; use crate as rune; use crate::compile::Named; use crate::module::InstallWith; -use crate::runtime::{FromValue, ProtocolCaller, RawStr, ToValue, Value, VmResult}; +use crate::runtime::{ + EnvProtocolCaller, FromValue, ProtocolCaller, RawStr, ToValue, Value, VmResult, +}; /// Struct representing an open range `..end`. /// @@ -30,52 +32,130 @@ impl RangeTo { Self { end } } + /// Test the range for partial equality. + /// + /// # Examples + /// + /// ```rune + /// let range = ..'e'; + /// assert!(range == (..'e')); + /// assert!(range != (..'f')); + /// + /// let range = ..2.0; + /// assert!(range == (..2.0)); + /// assert!(range != (..f64::NAN)); + /// assert!((..f64::NAN) != (..f64::NAN)); + /// ``` + #[rune::function(keep, protocol = PARTIAL_EQ)] + pub fn partial_eq(&self, other: &Self) -> VmResult { + self.partial_eq_with(other, &mut EnvProtocolCaller) + } + pub(crate) fn partial_eq_with( - a: &Self, + &self, b: &Self, caller: &mut impl ProtocolCaller, ) -> VmResult { - Value::partial_eq_with(&a.end, &b.end, caller) + Value::partial_eq_with(&self.end, &b.end, caller) + } + + /// Test the range for total equality. + /// + /// # Examples + /// + /// ```rune + /// use std::ops::eq; + /// + /// let range = ..'e'; + /// assert!(eq(range, ..'e')); + /// assert!(!eq(range, ..'f')); + /// ``` + #[rune::function(keep, protocol = EQ)] + pub fn eq(&self, other: &Self) -> VmResult { + self.eq_with(other, &mut EnvProtocolCaller) + } + + pub(crate) fn eq_with(&self, b: &Self, caller: &mut impl ProtocolCaller) -> VmResult { + Value::eq_with(&self.end, &b.end, caller) } - pub(crate) fn eq_with(a: &Self, b: &Self, caller: &mut impl ProtocolCaller) -> VmResult { - Value::eq_with(&a.end, &b.end, caller) + /// Test the range for partial ordering. + /// + /// # Examples + /// + /// ```rune + /// assert!((..'a') < (..'b')); + /// assert!((..'d') > (..'b')); + /// assert!(!((..f64::NAN) > (..f64::INFINITY))); + /// assert!(!((..f64::NAN) < (..f64::INFINITY))); + /// ``` + #[rune::function(keep, protocol = PARTIAL_CMP)] + pub fn partial_cmp(&self, other: &Self) -> VmResult> { + self.partial_cmp_with(other, &mut EnvProtocolCaller) } pub(crate) fn partial_cmp_with( - a: &Self, + &self, b: &Self, caller: &mut impl ProtocolCaller, ) -> VmResult> { - Value::partial_cmp_with(&a.end, &b.end, caller) + Value::partial_cmp_with(&self.end, &b.end, caller) + } + + /// Test the range for total ordering. + /// + /// # Examples + /// + /// ```rune + /// use std::ops::cmp; + /// use std::cmp::Ordering; + /// + /// assert_eq!(cmp(..'a', ..'b'), Ordering::Less); + /// assert_eq!(cmp(..'c', ..'b'), Ordering::Greater); + /// ``` + #[rune::function(keep, protocol = CMP)] + pub fn cmp(&self, other: &Self) -> VmResult { + self.cmp_with(other, &mut EnvProtocolCaller) } pub(crate) fn cmp_with( - a: &Self, + &self, b: &Self, caller: &mut impl ProtocolCaller, ) -> VmResult { - Value::cmp_with(&a.end, &b.end, caller) + Value::cmp_with(&self.end, &b.end, caller) } - /// Test if the range contains the given integer. + /// Test if the range contains the given value. + /// + /// The check is performed using the [`PARTIAL_CMP`] protocol. /// /// # Examples /// /// ```rune /// let range = ..10; /// - /// assert!(range.contains::(-10)); - /// assert!(range.contains::(5)); - /// assert!(!range.contains::(10)); - /// assert!(!range.contains::(20)); + /// assert!(range.contains(-10)); + /// assert!(range.contains(5)); + /// assert!(!range.contains(10)); + /// assert!(!range.contains(20)); /// /// assert!(range is std::ops::RangeTo); /// ``` - #[rune::function(path = contains::)] - pub(crate) fn contains(&self, n: i64) -> VmResult { - let end: i64 = vm_try!(FromValue::from_value(self.end.clone())); - VmResult::Ok((..end).contains(&n)) + #[rune::function(keep)] + pub(crate) fn contains(&self, value: Value) -> VmResult { + self.contains_with(value, &mut EnvProtocolCaller) + } + + pub(crate) fn contains_with( + &self, + value: Value, + caller: &mut impl ProtocolCaller, + ) -> VmResult { + VmResult::Ok(matches!( + vm_try!(Value::partial_cmp_with(&self.end, &value, caller)), + Some(Ordering::Greater) + )) } } diff --git a/crates/rune/src/runtime/range_to_inclusive.rs b/crates/rune/src/runtime/range_to_inclusive.rs index a0fd41b46..25c2c2e1d 100644 --- a/crates/rune/src/runtime/range_to_inclusive.rs +++ b/crates/rune/src/runtime/range_to_inclusive.rs @@ -5,7 +5,9 @@ use core::ops; use crate as rune; use crate::compile::Named; use crate::module::InstallWith; -use crate::runtime::{FromValue, ProtocolCaller, RawStr, ToValue, Value, VmResult}; +use crate::runtime::{ + EnvProtocolCaller, FromValue, ProtocolCaller, RawStr, ToValue, Value, VmResult, +}; /// Struct representing an open range `..=end`. /// @@ -30,52 +32,130 @@ impl RangeToInclusive { Self { end } } + /// Test the range for partial equality. + /// + /// # Examples + /// + /// ```rune + /// let range = ..='e'; + /// assert!(range == (..='e')); + /// assert!(range != (..='f')); + /// + /// let range = ..=2.0; + /// assert!(range == (..=2.0)); + /// assert!(range != (..=f64::NAN)); + /// assert!((..=f64::NAN) != (..=f64::NAN)); + /// ``` + #[rune::function(keep, protocol = PARTIAL_EQ)] + pub fn partial_eq(&self, other: &Self) -> VmResult { + self.partial_eq_with(other, &mut EnvProtocolCaller) + } + pub(crate) fn partial_eq_with( - a: &Self, + &self, b: &Self, caller: &mut impl ProtocolCaller, ) -> VmResult { - Value::partial_eq_with(&a.end, &b.end, caller) + Value::partial_eq_with(&self.end, &b.end, caller) + } + + /// Test the range for total equality. + /// + /// # Examples + /// + /// ```rune + /// use std::ops::eq; + /// + /// let range = ..='e'; + /// assert!(eq(range, ..='e')); + /// assert!(!eq(range, ..='f')); + /// ``` + #[rune::function(keep, protocol = EQ)] + pub fn eq(&self, other: &Self) -> VmResult { + self.eq_with(other, &mut EnvProtocolCaller) + } + + pub(crate) fn eq_with(&self, b: &Self, caller: &mut impl ProtocolCaller) -> VmResult { + Value::eq_with(&self.end, &b.end, caller) } - pub(crate) fn eq_with(a: &Self, b: &Self, caller: &mut impl ProtocolCaller) -> VmResult { - Value::eq_with(&a.end, &b.end, caller) + /// Test the range for partial ordering. + /// + /// # Examples + /// + /// ```rune + /// assert!((..='a') < (..='b')); + /// assert!((..='d') > (..='b')); + /// assert!(!((..=f64::NAN) > (..=f64::INFINITY))); + /// assert!(!((..=f64::NAN) < (..=f64::INFINITY))); + /// ``` + #[rune::function(keep, protocol = PARTIAL_CMP)] + pub fn partial_cmp(&self, other: &Self) -> VmResult> { + self.partial_cmp_with(other, &mut EnvProtocolCaller) } pub(crate) fn partial_cmp_with( - a: &Self, + &self, b: &Self, caller: &mut impl ProtocolCaller, ) -> VmResult> { - Value::partial_cmp_with(&a.end, &b.end, caller) + Value::partial_cmp_with(&self.end, &b.end, caller) + } + + /// Test the range for total ordering. + /// + /// # Examples + /// + /// ```rune + /// use std::ops::cmp; + /// use std::cmp::Ordering; + /// + /// assert_eq!(cmp(..='a', ..='b'), Ordering::Less); + /// assert_eq!(cmp(..='c', ..='b'), Ordering::Greater); + /// ``` + #[rune::function(keep, protocol = CMP)] + pub fn cmp(&self, other: &Self) -> VmResult { + self.cmp_with(other, &mut EnvProtocolCaller) } pub(crate) fn cmp_with( - a: &Self, + &self, b: &Self, caller: &mut impl ProtocolCaller, ) -> VmResult { - Value::cmp_with(&a.end, &b.end, caller) + Value::cmp_with(&self.end, &b.end, caller) } - /// Test if the range contains the given integer. + /// Test if the range contains the given value. + /// + /// The check is performed using the [`PARTIAL_CMP`] protocol. /// /// # Examples /// /// ```rune /// let range = ..=10; /// - /// assert!(range.contains::(-10)); - /// assert!(range.contains::(5)); - /// assert!(range.contains::(10)); - /// assert!(!range.contains::(20)); + /// assert!(range.contains(-10)); + /// assert!(range.contains(5)); + /// assert!(range.contains(10)); + /// assert!(!range.contains(20)); /// /// assert!(range is std::ops::RangeToInclusive); /// ``` - #[rune::function(path = contains::)] - pub(crate) fn contains(&self, n: i64) -> VmResult { - let end: i64 = vm_try!(FromValue::from_value(self.end.clone())); - VmResult::Ok((..=end).contains(&n)) + #[rune::function(keep)] + pub(crate) fn contains(&self, value: Value) -> VmResult { + self.contains_with(value, &mut EnvProtocolCaller) + } + + pub(crate) fn contains_with( + &self, + value: Value, + caller: &mut impl ProtocolCaller, + ) -> VmResult { + VmResult::Ok(matches!( + vm_try!(Value::partial_cmp_with(&self.end, &value, caller)), + Some(Ordering::Greater | Ordering::Equal) + )) } } diff --git a/crates/rune/src/runtime/static_type.rs b/crates/rune/src/runtime/static_type.rs index ed8ec71bc..12e191c1b 100644 --- a/crates/rune/src/runtime/static_type.rs +++ b/crates/rune/src/runtime/static_type.rs @@ -41,7 +41,7 @@ impl hash::Hash for &'static StaticType { } /// Hash for `::std::u8`. -pub(crate) const BYTE_TYPE_HASH: Hash = Hash::new(0xe6cb97d2df702ff4); +pub(crate) const BYTE_TYPE_HASH: Hash = ::rune_macros::hash!(::std::u8); /// The specialized type information for a byte type. pub(crate) static BYTE_TYPE: &StaticType = &StaticType { @@ -54,8 +54,7 @@ impl_static_type!(u8 => BYTE_TYPE); /// The specialized type information for a bool type. pub(crate) static BOOL_TYPE: &StaticType = &StaticType { name: RawStr::from_str("bool"), - // hash for ::std::bool - hash: Hash::new(0x981333df5abb043f), + hash: ::rune_macros::hash!(::std::bool), }; impl_static_type!(bool => BOOL_TYPE); @@ -63,14 +62,13 @@ impl_static_type!(bool => BOOL_TYPE); /// The specialized type information for a char type. pub(crate) static CHAR_TYPE: &StaticType = &StaticType { name: RawStr::from_str("char"), - // hash for ::std::char - hash: Hash::new(0x214e0b95f9831430), + hash: ::rune_macros::hash!(::std::char), }; impl_static_type!(char => CHAR_TYPE); /// Hash for `::std::i64`. -pub(crate) const INTEGER_TYPE_HASH: Hash = Hash::new(0x1cad9186c9641c4f); +pub(crate) const INTEGER_TYPE_HASH: Hash = ::rune_macros::hash!(::std::i64); /// The specialized type information for a integer type. pub(crate) static INTEGER_TYPE: &StaticType = &StaticType { @@ -92,7 +90,7 @@ impl_static_type!(usize => INTEGER_TYPE); impl_static_type!(isize => INTEGER_TYPE); /// Hash for `::std::f64`. -pub(crate) const FLOAT_TYPE_HASH: Hash = Hash::new(0xb9b22c3893248f31); +pub(crate) const FLOAT_TYPE_HASH: Hash = ::rune_macros::hash!(::std::f64); pub(crate) static FLOAT_TYPE: &StaticType = &StaticType { name: RawStr::from_str("f64"), @@ -104,8 +102,7 @@ impl_static_type!(f64 => FLOAT_TYPE); pub(crate) static STRING_TYPE: &StaticType = &StaticType { name: RawStr::from_str("String"), - // hash for ::std::string::String - hash: Hash::new(0x2f4720d36d2b70c), + hash: ::rune_macros::hash!(::std::string::String), }; impl_static_type!(String => STRING_TYPE); @@ -113,8 +110,7 @@ impl_static_type!(str => STRING_TYPE); pub(crate) static BYTES_TYPE: &StaticType = &StaticType { name: RawStr::from_str("Bytes"), - // hash for ::std::bytes::Bytes - hash: Hash::new(0x3470d9498e601529), + hash: ::rune_macros::hash!(::std::bytes::Bytes), }; impl_static_type!(rt::Bytes => BYTES_TYPE); @@ -122,8 +118,7 @@ impl_static_type!([u8] => BYTES_TYPE); pub(crate) static VEC_TYPE: &StaticType = &StaticType { name: RawStr::from_str("Vec"), - // hash for ::std::vec::Vec - hash: Hash::new(0xc11b1e769aea94f2), + hash: ::rune_macros::hash!(::std::vec::Vec), }; impl_static_type!(rt::Vec => VEC_TYPE); @@ -133,8 +128,7 @@ impl_static_type!(impl rt::VecTuple => VEC_TYPE); pub(crate) static TUPLE_TYPE: &StaticType = &StaticType { name: RawStr::from_str("Tuple"), - // hash for ::std::tuple::Tuple - hash: Hash::new(0xfeb65b3f4755aef0), + hash: ::rune_macros::hash!(::std::tuple::Tuple), }; impl_static_type!(rt::OwnedTuple => TUPLE_TYPE); @@ -142,8 +136,7 @@ impl_static_type!(rt::Tuple => TUPLE_TYPE); pub(crate) static OBJECT_TYPE: &StaticType = &StaticType { name: RawStr::from_str("Object"), - // hash for ::std::object::Object - hash: Hash::new(0x2f5c2c7799148025), + hash: ::rune_macros::hash!(::std::object::Object), }; impl_static_type!(rt::Object => OBJECT_TYPE); @@ -151,104 +144,91 @@ impl_static_type!(rt::Struct => OBJECT_TYPE); pub(crate) static RANGE_FROM_TYPE: &StaticType = &StaticType { name: RawStr::from_str("RangeFrom"), - // hash for ::std::ops::RangeFrom - hash: Hash::new(0x5ef6dd950a769b9e), + hash: ::rune_macros::hash!(::std::ops::RangeFrom), }; impl_static_type!(rt::RangeFrom => RANGE_FROM_TYPE); pub(crate) static RANGE_FULL_TYPE: &StaticType = &StaticType { name: RawStr::from_str("RangeFull"), - // hash for ::std::ops::RangeFull - hash: Hash::new(0xc16d3e8d86723c1c), + hash: ::rune_macros::hash!(::std::ops::RangeFull), }; impl_static_type!(rt::RangeFull => RANGE_FULL_TYPE); pub(crate) static RANGE_INCLUSIVE_TYPE: &StaticType = &StaticType { name: RawStr::from_str("RangeInclusive"), - // hash for ::std::ops::RangeInclusive - hash: Hash::new(0x58ec7f802ac7728), + hash: ::rune_macros::hash!(::std::ops::RangeInclusive), }; impl_static_type!(rt::RangeInclusive => RANGE_INCLUSIVE_TYPE); pub(crate) static RANGE_TO_INCLUSIVE_TYPE: &StaticType = &StaticType { name: RawStr::from_str("RangeToInclusive"), - // hash for ::std::ops::RangeToInclusive - hash: Hash::new(0xd46e14ce9cc3e9ae), + hash: ::rune_macros::hash!(::std::ops::RangeToInclusive), }; impl_static_type!(rt::RangeToInclusive => RANGE_TO_INCLUSIVE_TYPE); pub(crate) static RANGE_TO_TYPE: &StaticType = &StaticType { name: RawStr::from_str("RangeTo"), - // hash for ::std::ops::RangeTo - hash: Hash::new(0xf48d2d43d015d3b7), + hash: ::rune_macros::hash!(::std::ops::RangeTo), }; impl_static_type!(rt::RangeTo => RANGE_TO_TYPE); pub(crate) static RANGE_TYPE: &StaticType = &StaticType { name: RawStr::from_str("Range"), - // hash for ::std::ops::Range - hash: Hash::new(0x700a34bcd6630cba), + hash: ::rune_macros::hash!(::std::ops::Range), }; impl_static_type!(rt::Range => RANGE_TYPE); pub(crate) static FUTURE_TYPE: &StaticType = &StaticType { name: RawStr::from_str("Future"), - // hash for ::std::future::Future - hash: Hash::new(0x157cfe667ac47042), + hash: ::rune_macros::hash!(::std::future::Future), }; impl_static_type!(rt::Future => FUTURE_TYPE); pub(crate) static GENERATOR_TYPE: &StaticType = &StaticType { name: RawStr::from_str("Generator"), - // hash for ::std::generator::Generator - hash: Hash::new(0x9041ff127bcec639), + hash: ::rune_macros::hash!(::std::ops::Generator), }; impl_static_type!(rt::Generator => GENERATOR_TYPE); pub(crate) static GENERATOR_STATE_TYPE: &StaticType = &StaticType { name: RawStr::from_str("GeneratorState"), - // hash for ::std::generator::GeneratorState - hash: Hash::new(0xae44122e30ae33ae), + hash: ::rune_macros::hash!(::std::ops::GeneratorState), }; impl_static_type!(rt::GeneratorState => GENERATOR_STATE_TYPE); pub(crate) static STREAM_TYPE: &StaticType = &StaticType { name: RawStr::from_str("Stream"), - // hash for ::std::stream::Stream - hash: Hash::new(0xd849ef81ff581a21), + hash: ::rune_macros::hash!(::std::stream::Stream), }; impl_static_type!(rt::Stream => STREAM_TYPE); pub(crate) static RESULT_TYPE: &StaticType = &StaticType { name: RawStr::from_str("Result"), - // hash for ::std::result::Result - hash: Hash::new(0x1978eae6b50a98ef), + hash: ::rune_macros::hash!(::std::result::Result), }; impl_static_type!(impl Result => RESULT_TYPE); pub(crate) static OPTION_TYPE: &StaticType = &StaticType { name: RawStr::from_str("Option"), - // hash for ::std::option::Option - hash: Hash::new(0xc0958f246e193e78), + hash: ::rune_macros::hash!(::std::option::Option), }; impl_static_type!(impl Option => OPTION_TYPE); pub(crate) static FUNCTION_TYPE: &StaticType = &StaticType { name: RawStr::from_str("Function"), - // hash for ::std::ops::Function - hash: Hash::new(0x20b8050151a2855), + hash: ::rune_macros::hash!(::std::ops::Function), }; impl_static_type!(rt::Function => FUNCTION_TYPE); @@ -256,32 +236,28 @@ impl_static_type!(impl HashMap => OBJECT_TYPE); pub(crate) static FORMAT_TYPE: &StaticType = &StaticType { name: RawStr::from_str("Format"), - // hash for ::std::fmt::Format - hash: Hash::new(0xc331a83f0ad5a659), + hash: ::rune_macros::hash!(::std::fmt::Format), }; impl_static_type!(rt::Format => FORMAT_TYPE); pub(crate) static ITERATOR_TYPE: &StaticType = &StaticType { name: RawStr::from_str("Iterator"), - // hash for ::std::iter::Iterator - hash: Hash::new(0xff918cc536a3fa32), + hash: ::rune_macros::hash!(::std::iter::Iterator), }; impl_static_type!(rt::Iterator => ITERATOR_TYPE); pub(crate) static TYPE: &StaticType = &StaticType { name: RawStr::from_str("Type"), - // hash for ::std::any::Type - hash: Hash::new(0xbd7ccf79d00ee140), + hash: ::rune_macros::hash!(::std::any::Type), }; impl_static_type!(rt::Type => TYPE); pub(crate) static ORDERING: &StaticType = &StaticType { name: RawStr::from_str("Ordering"), - // hash for ::std::cmp::Ordering - hash: Hash::new(0x30d24cc3fa13e4b7), + hash: ::rune_macros::hash!(::std::cmp::Ordering), }; impl_static_type!(Ordering => ORDERING); diff --git a/crates/rune/src/tests/bug_344.rs b/crates/rune/src/tests/bug_344.rs index 184070126..4cdfa2c55 100644 --- a/crates/rune/src/tests/bug_344.rs +++ b/crates/rune/src/tests/bug_344.rs @@ -177,7 +177,7 @@ impl GuardCheck { impl Any for GuardCheck { fn type_hash() -> Hash { - Hash::new(0x6b8fb6d544712e99) + rune_macros::hash!(GuardCheck) } } diff --git a/crates/rune/src/tests/vm_generators.rs b/crates/rune/src/tests/vm_generators.rs index eeda06d36..08355f291 100644 --- a/crates/rune/src/tests/vm_generators.rs +++ b/crates/rune/src/tests/vm_generators.rs @@ -22,7 +22,7 @@ fn test_simple_generator() { #[test] fn test_resume() { let out: i64 = rune! { - use std::generator::GeneratorState; + use std::ops::GeneratorState; fn foo() { let a = yield 1; let b = yield a; b } diff --git a/crates/rune/src/tests/vm_streams.rs b/crates/rune/src/tests/vm_streams.rs index 6c8358dea..f45517257 100644 --- a/crates/rune/src/tests/vm_streams.rs +++ b/crates/rune/src/tests/vm_streams.rs @@ -32,7 +32,7 @@ fn test_simple_stream() { #[test] fn test_resume() { let out: i64 = rune! { - use std::generator::GeneratorState; + use std::ops::GeneratorState; async fn foo() { let a = yield 1; let b = yield a; b } diff --git a/scripts/generator_resume.rn b/scripts/generator_resume.rn index 7cd7a5bea..cd82a313d 100644 --- a/scripts/generator_resume.rn +++ b/scripts/generator_resume.rn @@ -1,4 +1,4 @@ -use std::generator::GeneratorState; +use std::ops::GeneratorState; fn foo() { let a = yield 1;