Fix BLOB and string operations.
This commit is contained in:
parent
8215c75a17
commit
753e527cbb
@ -10,6 +10,7 @@ Bug fixes
|
|||||||
---------
|
---------
|
||||||
|
|
||||||
* `switch` cases with conditions that evaluate to constant `()` no longer optimize to `false` (should raise a type error during runtime).
|
* `switch` cases with conditions that evaluate to constant `()` no longer optimize to `false` (should raise a type error during runtime).
|
||||||
|
* Fixes concatenation of BLOB's and strings, where the BLOB's should be interpreted as UTF-8 encoded strings.
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
@ -30,6 +31,7 @@ Enhancements
|
|||||||
|
|
||||||
* `EvalContext::eval_expression_tree_raw` and `Expression::eval_with_context_raw` are added to allow for not rewinding the `Scope` at the end of a statements block.
|
* `EvalContext::eval_expression_tree_raw` and `Expression::eval_with_context_raw` are added to allow for not rewinding the `Scope` at the end of a statements block.
|
||||||
* A new `range` function variant that takes an exclusive range with a step.
|
* A new `range` function variant that takes an exclusive range with a step.
|
||||||
|
* `as_string` is added to BLOB's to convert it into a string by interpreting it as a UTF-8 byte stream.
|
||||||
|
|
||||||
|
|
||||||
Version 1.8.0
|
Version 1.8.0
|
||||||
|
170
scripts/static.d.rhai
Normal file
170
scripts/static.d.rhai
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
/// This definition file extends the scope of all scripts.
|
||||||
|
///
|
||||||
|
/// The items defined here simply exist and are available.
|
||||||
|
/// everywhere.
|
||||||
|
///
|
||||||
|
/// These definitions should be used for built-in functions and
|
||||||
|
/// local domain-specific environment-provided values.
|
||||||
|
module static;
|
||||||
|
|
||||||
|
/// Display any data to the standard output.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let answer = 42;
|
||||||
|
///
|
||||||
|
/// print(`The Answer is ${answer}`);
|
||||||
|
/// ```
|
||||||
|
fn print(data: ?);
|
||||||
|
|
||||||
|
/// Display any data to the standard output in debug format.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let answer = 42;
|
||||||
|
///
|
||||||
|
/// debug(answer);
|
||||||
|
/// ```
|
||||||
|
fn debug(data: ?);
|
||||||
|
|
||||||
|
/// Get the type of a value.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let x = "hello, world!";
|
||||||
|
///
|
||||||
|
/// print(x.type_of()); // prints "string"
|
||||||
|
/// ```
|
||||||
|
fn type_of(data: ?) -> String;
|
||||||
|
|
||||||
|
/// Create a function pointer to a named function.
|
||||||
|
///
|
||||||
|
/// If the specified name is not a valid function name, an error is raised.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let f = Fn("foo"); // function pointer to 'foo'
|
||||||
|
///
|
||||||
|
/// f.call(42); // call: foo(42)
|
||||||
|
/// ```
|
||||||
|
fn Fn(fn_name: String) -> FnPtr;
|
||||||
|
|
||||||
|
/// Call a function pointed to by a function pointer,
|
||||||
|
/// passing following arguments to the function call.
|
||||||
|
///
|
||||||
|
/// If an appropriate function is not found, an error is raised.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let f = Fn("foo"); // function pointer to 'foo'
|
||||||
|
///
|
||||||
|
/// f.call(1, 2, 3); // call: foo(1, 2, 3)
|
||||||
|
/// ```
|
||||||
|
fn call(fn_ptr: FnPtr, ...args: ?) -> ?;
|
||||||
|
|
||||||
|
/// Call a function pointed to by a function pointer, binding the `this` pointer
|
||||||
|
/// to the object of the method call, and passing on following arguments to the function call.
|
||||||
|
///
|
||||||
|
/// If an appropriate function is not found, an error is raised.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// fn add(x) {
|
||||||
|
/// this + x
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let f = Fn("add"); // function pointer to 'add'
|
||||||
|
///
|
||||||
|
/// let x = 41;
|
||||||
|
///
|
||||||
|
/// let r = x.call(f, 1); // call: add(1) with 'this' = 'x'
|
||||||
|
///
|
||||||
|
/// print(r); // prints 42
|
||||||
|
/// ```
|
||||||
|
fn call(obj: ?, fn_ptr: FnPtr, ...args: ?) -> ?;
|
||||||
|
|
||||||
|
/// Curry a number of arguments into a function pointer and return it as a new function pointer.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// fn foo(x, y, z) {
|
||||||
|
/// x + y + z
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let f = Fn("foo");
|
||||||
|
///
|
||||||
|
/// let g = f.curry(1, 2); // curried arguments: 1, 2
|
||||||
|
///
|
||||||
|
/// g.call(3); // call: foo(1, 2, 3)
|
||||||
|
/// ```
|
||||||
|
fn curry(fn_ptr: FnPtr, ...args: ?) -> FnPtr;
|
||||||
|
|
||||||
|
/// Return `true` if a script-defined function exists with a specified name and
|
||||||
|
/// number of parameters.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// fn foo(x) { }
|
||||||
|
///
|
||||||
|
/// print(is_def_fn("foo", 1)); // prints true
|
||||||
|
/// print(is_def_fn("foo", 2)); // prints false
|
||||||
|
/// print(is_def_fn("foo", 0)); // prints false
|
||||||
|
/// print(is_def_fn("bar", 1)); // prints false
|
||||||
|
/// ```
|
||||||
|
fn is_def_fn(fn_name: String, num_params: i64) -> bool;
|
||||||
|
|
||||||
|
/// Return `true` if a variable matching a specified name is defined.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let x = 42;
|
||||||
|
///
|
||||||
|
/// print(is_def_var("x")); // prints true
|
||||||
|
/// print(is_def_var("foo")); // prints false
|
||||||
|
///
|
||||||
|
/// {
|
||||||
|
/// let y = 1;
|
||||||
|
/// print(is_def_var("y")); // prints true
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// print(is_def_var("y")); // prints false
|
||||||
|
/// ```
|
||||||
|
fn is_def_var(var_name: String) -> bool;
|
||||||
|
|
||||||
|
/// Return `true` if the variable is shared.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let x = 42;
|
||||||
|
///
|
||||||
|
/// print(is_shared(x)); // prints false
|
||||||
|
///
|
||||||
|
/// let f = || x; // capture 'x', making it shared
|
||||||
|
///
|
||||||
|
/// print(is_shared(x)); // prints true
|
||||||
|
/// ```
|
||||||
|
fn is_shared(variable: ?) -> bool;
|
||||||
|
|
||||||
|
/// Evaluate a text script within the current scope.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let x = 42;
|
||||||
|
///
|
||||||
|
/// eval("let y = x; x = 123;");
|
||||||
|
///
|
||||||
|
/// print(x); // prints 123
|
||||||
|
/// print(y); // prints 42
|
||||||
|
/// ```
|
||||||
|
fn eval(script: String) -> ?;
|
@ -293,10 +293,15 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
|
|||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if type1 == type2 {
|
if type2 == TypeId::of::<char>() {
|
||||||
return match op {
|
return match op {
|
||||||
"==" => Some(impl_op!(Blob == Blob)),
|
"+" => Some(|_, args| {
|
||||||
"!=" => Some(impl_op!(Blob != Blob)),
|
let mut buf = [0_u8; 4];
|
||||||
|
let mut blob = args[0].read_lock::<Blob>().expect(BUILTIN).clone();
|
||||||
|
let x = args[1].as_char().expect("`char`").encode_utf8(&mut buf);
|
||||||
|
blob.extend(x.as_bytes());
|
||||||
|
Ok(Dynamic::from_blob(blob))
|
||||||
|
}),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -503,6 +508,33 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option<Fn
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
if type1 == TypeId::of::<crate::Blob>() {
|
||||||
|
use crate::Blob;
|
||||||
|
|
||||||
|
return match op {
|
||||||
|
"+" => Some(|_, args| {
|
||||||
|
let blob1 = &*args[0].read_lock::<Blob>().expect(BUILTIN);
|
||||||
|
let blob2 = &*args[1].read_lock::<Blob>().expect(BUILTIN);
|
||||||
|
|
||||||
|
Ok(Dynamic::from_blob(if !blob2.is_empty() {
|
||||||
|
if blob1.is_empty() {
|
||||||
|
blob2.clone()
|
||||||
|
} else {
|
||||||
|
let mut blob = blob1.clone();
|
||||||
|
blob.extend(blob2);
|
||||||
|
blob
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
blob1.clone()
|
||||||
|
}))
|
||||||
|
}),
|
||||||
|
"==" => Some(impl_op!(Blob == Blob)),
|
||||||
|
"!=" => Some(impl_op!(Blob != Blob)),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if type1 == TypeId::of::<()>() {
|
if type1 == TypeId::of::<()>() {
|
||||||
return match op {
|
return match op {
|
||||||
"==" => Some(|_, _| Ok(Dynamic::TRUE)),
|
"==" => Some(|_, _| Ok(Dynamic::TRUE)),
|
||||||
@ -684,71 +716,39 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
{
|
{
|
||||||
// string op= blob
|
use crate::Blob;
|
||||||
if types_pair == (TypeId::of::<ImmutableString>(), TypeId::of::<crate::Blob>()) {
|
|
||||||
return match op {
|
|
||||||
"+=" => Some(|_, args| {
|
|
||||||
let buf = {
|
|
||||||
let x = args[1].read_lock::<crate::Blob>().expect(BUILTIN);
|
|
||||||
if x.is_empty() {
|
|
||||||
return Ok(Dynamic::UNIT);
|
|
||||||
}
|
|
||||||
let s = args[0].read_lock::<ImmutableString>().expect(BUILTIN);
|
|
||||||
let mut buf = crate::SmartString::from(s.as_str());
|
|
||||||
buf.push_str(&String::from_utf8_lossy(&x));
|
|
||||||
buf
|
|
||||||
};
|
|
||||||
let mut s = args[0].write_lock::<ImmutableString>().expect(BUILTIN);
|
|
||||||
*s = buf.into();
|
|
||||||
Ok(Dynamic::UNIT)
|
|
||||||
}),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
// blob op= int
|
|
||||||
if types_pair == (TypeId::of::<crate::Blob>(), TypeId::of::<INT>()) {
|
|
||||||
use crate::Blob;
|
|
||||||
|
|
||||||
|
// blob op= int
|
||||||
|
if types_pair == (TypeId::of::<Blob>(), TypeId::of::<INT>()) {
|
||||||
return match op {
|
return match op {
|
||||||
"+=" => Some(|_, args| {
|
"+=" => Some(|_, args| {
|
||||||
let x = (args[1].as_int().expect("`INT`") & 0x000000ff) as u8;
|
let x = args[1].as_int().expect("`INT`");
|
||||||
let mut blob = args[0].write_lock::<Blob>().expect(BUILTIN);
|
let blob = &mut *args[0].write_lock::<Blob>().expect(BUILTIN);
|
||||||
Ok(blob.push(x).into())
|
Ok(crate::packages::blob_basic::blob_functions::push(blob, x).into())
|
||||||
}),
|
}),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// blob op= char
|
// blob op= char
|
||||||
if types_pair == (TypeId::of::<crate::Blob>(), TypeId::of::<char>()) {
|
if types_pair == (TypeId::of::<Blob>(), TypeId::of::<char>()) {
|
||||||
use crate::Blob;
|
|
||||||
|
|
||||||
return match op {
|
return match op {
|
||||||
"+=" => Some(|_, args| {
|
"+=" => Some(|_, args| {
|
||||||
let mut buf = [0_u8; 4];
|
let x = args[1].as_char().expect("`char`");
|
||||||
let x = args[1].as_char().expect("`char`").encode_utf8(&mut buf);
|
let blob = &mut *args[0].write_lock::<Blob>().expect(BUILTIN);
|
||||||
let mut blob = args[0].write_lock::<Blob>().expect(BUILTIN);
|
Ok(crate::packages::blob_basic::blob_functions::append_char(blob, x).into())
|
||||||
Ok(blob.extend(x.as_bytes()).into())
|
|
||||||
}),
|
}),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// blob op= string
|
// blob op= string
|
||||||
if types_pair == (TypeId::of::<crate::Blob>(), TypeId::of::<ImmutableString>()) {
|
if types_pair == (TypeId::of::<Blob>(), TypeId::of::<ImmutableString>()) {
|
||||||
use crate::Blob;
|
|
||||||
|
|
||||||
return match op {
|
return match op {
|
||||||
"+=" => Some(|_, args| {
|
"+=" => Some(|_, args| {
|
||||||
let s: crate::Blob = {
|
let s = std::mem::take(args[1]).cast::<ImmutableString>();
|
||||||
let s = args[1].read_lock::<ImmutableString>().expect(BUILTIN);
|
let blob = &mut *args[0].write_lock::<Blob>().expect(BUILTIN);
|
||||||
if s.is_empty() {
|
Ok(crate::packages::blob_basic::blob_functions::append_str(blob, &s).into())
|
||||||
return Ok(Dynamic::UNIT);
|
|
||||||
}
|
|
||||||
s.as_bytes().into()
|
|
||||||
};
|
|
||||||
let mut blob = args[0].write_lock::<Blob>().expect(BUILTIN);
|
|
||||||
Ok(blob.extend(s).into())
|
|
||||||
}),
|
}),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
@ -838,14 +838,13 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
if type1 == TypeId::of::<crate::Blob>() {
|
if type1 == TypeId::of::<crate::Blob>() {
|
||||||
use crate::packages::blob_basic::blob_functions::*;
|
|
||||||
use crate::Blob;
|
use crate::Blob;
|
||||||
|
|
||||||
return match op {
|
return match op {
|
||||||
"+=" => Some(|_, args| {
|
"+=" => Some(|_, args| {
|
||||||
let blob2 = std::mem::take(args[1]).cast::<Blob>();
|
let blob2 = std::mem::take(args[1]).cast::<Blob>();
|
||||||
let blob1 = &mut *args[0].write_lock::<Blob>().expect(BUILTIN);
|
let blob1 = &mut *args[0].write_lock::<Blob>().expect(BUILTIN);
|
||||||
Ok(append(blob1, blob2).into())
|
Ok(crate::packages::blob_basic::blob_functions::append(blob1, blob2).into())
|
||||||
}),
|
}),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
@ -213,9 +213,9 @@ fn optimize_stmt_block(
|
|||||||
|
|
||||||
// Flatten blocks
|
// Flatten blocks
|
||||||
loop {
|
loop {
|
||||||
if let Some(n) = statements.iter().enumerate().find_map(|(i, s)| match s {
|
if let Some(n) = statements.iter().position(|s| match s {
|
||||||
Stmt::Block(block, ..) if !block.iter().any(Stmt::is_block_dependent) => Some(i),
|
Stmt::Block(block, ..) if !block.iter().any(Stmt::is_block_dependent) => true,
|
||||||
_ => None,
|
_ => false,
|
||||||
}) {
|
}) {
|
||||||
let (first, second) = statements.split_at_mut(n);
|
let (first, second) = statements.split_at_mut(n);
|
||||||
let stmt = mem::take(&mut second[0]);
|
let stmt = mem::take(&mut second[0]);
|
||||||
|
@ -8,7 +8,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
use std::{any::TypeId, mem};
|
use std::{any::TypeId, borrow::Cow, mem};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
use crate::{FLOAT, FLOAT_BYTES};
|
use crate::{FLOAT, FLOAT_BYTES};
|
||||||
@ -104,6 +104,27 @@ pub mod blob_functions {
|
|||||||
pub fn to_array(blob: &mut Blob) -> Array {
|
pub fn to_array(blob: &mut Blob) -> Array {
|
||||||
blob.iter().map(|&ch| (ch as INT).into()).collect()
|
blob.iter().map(|&ch| (ch as INT).into()).collect()
|
||||||
}
|
}
|
||||||
|
/// Convert the BLOB into a string.
|
||||||
|
///
|
||||||
|
/// The byte stream must be valid UTF-8, otherwise an error is raised.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// let b = blob(5, 0x42);
|
||||||
|
///
|
||||||
|
/// let x = b.as_string();
|
||||||
|
///
|
||||||
|
/// print(x); // prints "FFFFF"
|
||||||
|
/// ```
|
||||||
|
pub fn as_string(blob: Blob) -> String {
|
||||||
|
let s = String::from_utf8_lossy(&blob);
|
||||||
|
|
||||||
|
match s {
|
||||||
|
Cow::Borrowed(_) => String::from_utf8(blob).unwrap(),
|
||||||
|
Cow::Owned(_) => s.into_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
/// Return the length of the BLOB.
|
/// Return the length of the BLOB.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
@ -200,6 +221,7 @@ pub mod blob_functions {
|
|||||||
///
|
///
|
||||||
/// print(b); // prints "[42]"
|
/// print(b); // prints "[42]"
|
||||||
/// ```
|
/// ```
|
||||||
|
#[rhai_fn(name = "push", name = "append")]
|
||||||
pub fn push(blob: &mut Blob, value: INT) {
|
pub fn push(blob: &mut Blob, value: INT) {
|
||||||
blob.push((value & 0x000000ff) as u8);
|
blob.push((value & 0x000000ff) as u8);
|
||||||
}
|
}
|
||||||
@ -235,13 +257,13 @@ pub mod blob_functions {
|
|||||||
///
|
///
|
||||||
/// print(b); // prints "[424242424268656c 6c6f]"
|
/// print(b); // prints "[424242424268656c 6c6f]"
|
||||||
/// ```
|
/// ```
|
||||||
#[rhai_fn(name = "+=", name = "append")]
|
#[rhai_fn(name = "append")]
|
||||||
pub fn append_str(blob: &mut Blob, string: &str) {
|
pub fn append_str(blob: &mut Blob, string: &str) {
|
||||||
if !string.is_empty() {
|
if !string.is_empty() {
|
||||||
blob.extend(string.as_bytes());
|
blob.extend(string.as_bytes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Add a string (as UTF-8 encoded byte-stream) to the end of the BLOB
|
/// Add a character (as UTF-8 encoded byte-stream) to the end of the BLOB
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -252,38 +274,12 @@ pub mod blob_functions {
|
|||||||
///
|
///
|
||||||
/// print(b); // prints "[424242424221]"
|
/// print(b); // prints "[424242424221]"
|
||||||
/// ```
|
/// ```
|
||||||
#[rhai_fn(name = "+=", name = "append")]
|
#[rhai_fn(name = "append")]
|
||||||
pub fn append_char(blob: &mut Blob, character: char) {
|
pub fn append_char(blob: &mut Blob, character: char) {
|
||||||
let mut buf = [0_u8; 4];
|
let mut buf = [0_u8; 4];
|
||||||
let x = character.encode_utf8(&mut buf);
|
let x = character.encode_utf8(&mut buf);
|
||||||
blob.extend(x.as_bytes());
|
blob.extend(x.as_bytes());
|
||||||
}
|
}
|
||||||
/// Add another BLOB to the end of the BLOB, returning it as a new BLOB.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
///
|
|
||||||
/// ```rhai
|
|
||||||
/// let b1 = blob(5, 0x42);
|
|
||||||
/// let b2 = blob(3, 0x11);
|
|
||||||
///
|
|
||||||
/// print(b1 + b2); // prints "[4242424242111111]"
|
|
||||||
///
|
|
||||||
/// print(b1); // prints "[4242424242]"
|
|
||||||
/// ```
|
|
||||||
#[rhai_fn(name = "+")]
|
|
||||||
pub fn concat(blob1: Blob, blob2: Blob) -> Blob {
|
|
||||||
if !blob2.is_empty() {
|
|
||||||
if blob1.is_empty() {
|
|
||||||
blob2
|
|
||||||
} else {
|
|
||||||
let mut blob = blob1;
|
|
||||||
blob.extend(blob2);
|
|
||||||
blob
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
blob1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Add a byte `value` to the BLOB at a particular `index` position.
|
/// Add a byte `value` to the BLOB at a particular `index` position.
|
||||||
///
|
///
|
||||||
/// * If `index` < 0, position counts from the end of the BLOB (`-1` is the last byte).
|
/// * If `index` < 0, position counts from the end of the BLOB (`-1` is the last byte).
|
||||||
|
@ -4,20 +4,20 @@ use crate::{Module, Shared};
|
|||||||
|
|
||||||
pub(crate) mod arithmetic;
|
pub(crate) mod arithmetic;
|
||||||
pub(crate) mod array_basic;
|
pub(crate) mod array_basic;
|
||||||
mod bit_field;
|
pub(crate) mod bit_field;
|
||||||
pub(crate) mod blob_basic;
|
pub(crate) mod blob_basic;
|
||||||
mod debugging;
|
pub(crate) mod debugging;
|
||||||
mod fn_basic;
|
pub(crate) mod fn_basic;
|
||||||
pub(crate) mod iter_basic;
|
pub(crate) mod iter_basic;
|
||||||
mod lang_core;
|
pub(crate) mod lang_core;
|
||||||
mod logic;
|
pub(crate) mod logic;
|
||||||
mod map_basic;
|
pub(crate) mod map_basic;
|
||||||
mod math_basic;
|
pub(crate) mod math_basic;
|
||||||
mod pkg_core;
|
pub(crate) mod pkg_core;
|
||||||
mod pkg_std;
|
pub(crate) mod pkg_std;
|
||||||
mod string_basic;
|
pub(crate) mod string_basic;
|
||||||
mod string_more;
|
pub(crate) mod string_more;
|
||||||
mod time_basic;
|
pub(crate) mod time_basic;
|
||||||
|
|
||||||
pub use arithmetic::ArithmeticPackage;
|
pub use arithmetic::ArithmeticPackage;
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
@ -87,25 +87,42 @@ mod string_functions {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
pub mod blob_functions {
|
pub mod blob_functions {
|
||||||
#[rhai_fn(name = "+", pure)]
|
#[rhai_fn(name = "+", pure)]
|
||||||
pub fn add_append_blob(string: &mut ImmutableString, utf8: Blob) -> ImmutableString {
|
pub fn add_append(string: &mut ImmutableString, utf8: Blob) -> ImmutableString {
|
||||||
if utf8.is_empty() {
|
if utf8.is_empty() {
|
||||||
string.clone()
|
return string.clone();
|
||||||
} else if string.is_empty() {
|
}
|
||||||
String::from_utf8_lossy(&utf8).into_owned().into()
|
|
||||||
|
let s = String::from_utf8_lossy(&utf8);
|
||||||
|
|
||||||
|
if string.is_empty() {
|
||||||
|
match s {
|
||||||
|
std::borrow::Cow::Borrowed(_) => String::from_utf8(utf8).unwrap(),
|
||||||
|
std::borrow::Cow::Owned(_) => s.into_owned(),
|
||||||
|
}
|
||||||
|
.into()
|
||||||
} else {
|
} else {
|
||||||
let mut s = crate::SmartString::from(string.as_str());
|
let mut x = SmartString::from(string.as_str());
|
||||||
s.push_str(&String::from_utf8_lossy(&utf8));
|
x.push_str(s.as_ref());
|
||||||
s.into()
|
x.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[rhai_fn(name = "append")]
|
#[rhai_fn(name = "+=", name = "append")]
|
||||||
pub fn add_blob(string: &mut ImmutableString, utf8: Blob) {
|
pub fn add(string: &mut ImmutableString, utf8: Blob) {
|
||||||
let mut s = crate::SmartString::from(string.as_str());
|
let mut s = crate::SmartString::from(string.as_str());
|
||||||
if !utf8.is_empty() {
|
if !utf8.is_empty() {
|
||||||
s.push_str(&String::from_utf8_lossy(&utf8));
|
s.push_str(&String::from_utf8_lossy(&utf8));
|
||||||
*string = s.into();
|
*string = s.into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[rhai_fn(name = "+")]
|
||||||
|
pub fn add_prepend(utf8: Blob, string: ImmutableString) -> Blob {
|
||||||
|
let mut blob = utf8;
|
||||||
|
|
||||||
|
if !string.is_empty() {
|
||||||
|
blob.extend(string.as_bytes());
|
||||||
|
}
|
||||||
|
blob
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the length of the string, in number of characters.
|
/// Return the length of the string, in number of characters.
|
||||||
|
Loading…
Reference in New Issue
Block a user