Reimplement pad for string.

This commit is contained in:
Stephen Chung 2020-10-18 22:36:58 +08:00
parent ea814779bf
commit 25f820f5bf
7 changed files with 150 additions and 97 deletions

View File

@ -30,36 +30,36 @@ Built-in Functions
The following methods (mostly defined in the [`BasicArrayPackage`][packages] but excluded if using a [raw `Engine`]) operate on arrays: The following methods (mostly defined in the [`BasicArrayPackage`][packages] but excluded if using a [raw `Engine`]) operate on arrays:
| Function | Parameter(s) | Description | | Function | Parameter(s) | Description |
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `push` | element to insert | inserts an element at the end | | `push` | element to insert | inserts an element at the end |
| `append` | array to append | concatenates the second array to the end of the first | | `append` | array to append | concatenates the second array to the end of the first |
| `+=` operator | 1) array<br/>2) element to insert (not another array) | inserts an element at the end | | `+=` operator | 1) array<br/>2) element to insert (not another array) | inserts an element at the end |
| `+=` operator | 1) array<br/>2) array to append | concatenates the second array to the end of the first | | `+=` operator | 1) array<br/>2) array to append | concatenates the second array to the end of the first |
| `+` operator | 1) first array<br/>2) second array | concatenates the first array with the second | | `+` operator | 1) first array<br/>2) second array | concatenates the first array with the second |
| `insert` | 1) element to insert<br/>2) position, beginning if < 0, end if > length | inserts an element at a certain index | | `insert` | 1) element to insert<br/>2) position, beginning if < 0, end if > length | inserts an element at a certain index |
| `pop` | _none_ | removes the last element and returns it ([`()`] if empty) | | `pop` | _none_ | removes the last element and returns it ([`()`] if empty) |
| `shift` | _none_ | removes the first element and returns it ([`()`] if empty) | | `shift` | _none_ | removes the first element and returns it ([`()`] if empty) |
| `extract` | 1) start position, beginning if < 0, end if > length,<br/>2) _(optional)_ number of items to extract, none if < 0 | extracts a portion of the array into a new array | | `extract` | 1) start position, beginning if < 0, end if > length<br/>2) _(optional)_ number of items to extract, none if < 0 | extracts a portion of the array into a new array |
| `remove` | index | removes an element at a particular index and returns it ([`()`] if the index is not valid) | | `remove` | index | removes an element at a particular index and returns it ([`()`] if the index is not valid) |
| `reverse` | _none_ | reverses the array | | `reverse` | _none_ | reverses the array |
| `len` method and property | _none_ | returns the number of elements | | `len` method and property | _none_ | returns the number of elements |
| `pad` | 1) target length<br/>2) element to pad | pads the array with an element to at least a specified length | | `pad` | 1) target length<br/>2) element to pad | pads the array with an element to at least a specified length |
| `clear` | _none_ | empties the array | | `clear` | _none_ | empties the array |
| `truncate` | target length | cuts off the array at exactly a specified length (discarding all subsequent elements) | | `truncate` | target length | cuts off the array at exactly a specified length (discarding all subsequent elements) |
| `chop` | target length | cuts off the head of the array, leaving the tail at exactly a specified length | | `chop` | target length | cuts off the head of the array, leaving the tail at exactly a specified length |
| `drain` | 1) [function pointer] to predicate (usually a [closure]),<br/>2) _(optional)_ [function pointer] to function (usually a [closure]) that provides the initial value | removes all items (returning them) that return `true` when called with the predicate function:<br/>1st parameter: array item,<br/>2nd parameter: _(optional)_ offset index | | `drain` | 1) [function pointer] to predicate (usually a [closure])<br/>2) _(optional)_ [function pointer] to function (usually a [closure]) that provides the initial value | removes all items (returning them) that return `true` when called with the predicate function:<br/>1st parameter: array item<br/>2nd parameter: _(optional)_ offset index |
| `drain` | 1) start position, beginning if < 0, end if > length,<br/>2) number of items to remove, none if < 0 | removes a portion of the array, returning the removed items (not in original order) | | `drain` | 1) start position, beginning if < 0, end if > length<br/>2) number of items to remove, none if < 0 | removes a portion of the array, returning the removed items (not in original order) |
| `retain` | 1) [function pointer] to predicate (usually a [closure]),<br/>2) _(optional)_ [function pointer] to function (usually a [closure]) that provides the initial value | removes all items (returning them) that do not return `true` when called with the predicate function:<br/>1st parameter: array item,<br/>2nd parameter: _(optional)_ offset index | | `retain` | 1) [function pointer] to predicate (usually a [closure])<br/>2) _(optional)_ [function pointer] to function (usually a [closure]) that provides the initial value | removes all items (returning them) that do not return `true` when called with the predicate function:<br/>1st parameter: array item<br/>2nd parameter: _(optional)_ offset index |
| `retain` | 1) start position, beginning if < 0, end if > length,<br/>2) number of items to retain, none if < 0 | retains a portion of the array, removes all other items and returning them (not in original order) | | `retain` | 1) start position, beginning if < 0, end if > length<br/>2) number of items to retain, none if < 0 | retains a portion of the array, removes all other items and returning them (not in original order) |
| `splice` | 1) start position, beginning if < 0, end if > length,<br/>2) number of items to remove, none if < 0,<br/>3) array to insert | replaces a portion of the array with another (not necessarily of the same length as the replaced portion) | | `splice` | 1) start position, beginning if < 0, end if > length<br/>2) number of items to remove, none if < 0<br/>3) array to insert | replaces a portion of the array with another (not necessarily of the same length as the replaced portion) |
| `filter` | [function pointer] to predicate (usually a [closure]) | constructs a new array with all items that return `true` when called with the predicate function:<br/>1st parameter: array item,<br/>2nd parameter: _(optional)_ offset index | | `filter` | [function pointer] to predicate (usually a [closure]) | constructs a new array with all items that return `true` when called with the predicate function:<br/>1st parameter: array item<br/>2nd parameter: _(optional)_ offset index |
| `map` | [function pointer] to conversion function (usually a [closure]) | constructs a new array with all items mapped to the result of applying the conversion function:<br/>1st parameter: array item,<br/>2nd parameter: _(optional)_ offset index | | `map` | [function pointer] to conversion function (usually a [closure]) | constructs a new array with all items mapped to the result of applying the conversion function:<br/>1st parameter: array item<br/>2nd parameter: _(optional)_ offset index |
| `reduce` | 1) [function pointer] to accumulator function (usually a [closure]),<br/>2) _(optional)_ [function pointer] to function (usually a [closure]) that provides the initial value | reduces the array into a single value via the accumulator function:<br/>1st parameter: accumulated value ([`()`] initially),<br/>2nd parameter: array item,<br/>3rd parameter: _(optional)_ offset index | | `reduce` | 1) [function pointer] to accumulator function (usually a [closure])<br/>2) _(optional)_ [function pointer] to function (usually a [closure]) that provides the initial value | reduces the array into a single value via the accumulator function:<br/>1st parameter: accumulated value ([`()`] initially)<br/>2nd parameter: array item<br/>3rd parameter: _(optional)_ offset index |
| `reduce_rev` | 1) [function pointer] to accumulator function (usually a [closure]),<br/>2) _(optional)_ [function pointer] to function (usually a [closure]) that provides the initial value | reduces the array (in reverse order) into a single value via the accumulator function:<br/>1st parameter: accumulated value ([`()`] initially),<br/>2nd parameter: array item,<br/>3rd parameter: _(optional)_ offset index | | `reduce_rev` | 1) [function pointer] to accumulator function (usually a [closure])<br/>2) _(optional)_ [function pointer] to function (usually a [closure]) that provides the initial value | reduces the array (in reverse order) into a single value via the accumulator function:<br/>1st parameter: accumulated value ([`()`] initially)<br/>2nd parameter: array item<br/>3rd parameter: _(optional)_ offset index |
| `some` | [function pointer] to predicate (usually a [closure]) | returns `true` if any item returns `true` when called with the predicate function:<br/>1st parameter: array item,<br/>2nd parameter: _(optional)_ offset index | | `some` | [function pointer] to predicate (usually a [closure]) | returns `true` if any item returns `true` when called with the predicate function:<br/>1st parameter: array item<br/>2nd parameter: _(optional)_ offset index |
| `all` | [function pointer] to predicate (usually a [closure]) | returns `true` if all items return `true` when called with the predicate function:<br/>1st parameter: array item,<br/>2nd parameter: _(optional)_ offset index | | `all` | [function pointer] to predicate (usually a [closure]) | returns `true` if all items return `true` when called with the predicate function:<br/>1st parameter: array item<br/>2nd parameter: _(optional)_ offset index |
| `sort` | [function pointer] to a comparison function (usually a [closure]) | sorts the array with a comparison function:<br/>1st parameter: first item,<br/>2nd parameter: second item,<br/>return value: `INT` < 0 if first < second, > 0 if first > second, 0 if first == second | | `sort` | [function pointer] to a comparison function (usually a [closure]) | sorts the array with a comparison function:<br/>1st parameter: first item<br/>2nd parameter: second item<br/>return value: `INT` < 0 if first < second, > 0 if first > second, 0 if first == second |
Use Custom Types With Arrays Use Custom Types With Arrays

View File

@ -226,7 +226,7 @@ engine.register_raw_fn("super_call",
`FnPtr::call_dynamic` takes a parameter of type `NativeCallContext` which holds the _native call context_ `FnPtr::call_dynamic` takes a parameter of type `NativeCallContext` which holds the _native call context_
of the particular call to a registered Rust function. of the particular call to a registered Rust function.
This type is normally provided by the [`Engine`] (e.g. when using `Engine::register_fn_raw`(../rust/register-raw.md)). This type is normally provided by the [`Engine`] (e.g. when using [`Engine::register_fn_raw`](../rust/register-raw.md)).
However, it may also be manually constructed from a tuple: However, it may also be manually constructed from a tuple:
```rust ```rust

View File

@ -9,7 +9,7 @@ using a [raw `Engine`]) operate on [strings]:
| Function | Parameter(s) | Description | | Function | Parameter(s) | Description |
| ------------------------- | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | | ------------------------- | ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `len` method and property | _none_ | returns the number of characters (not number of bytes) in the string | | `len` method and property | _none_ | returns the number of characters (not number of bytes) in the string |
| `pad` | 1) character to pad<br/>2) target length | pads the string with an character to at least a specified length | | `pad` | 1) target length<br/>2) character/string to pad | pads the string with a character or a string to at least a specified length |
| `+=` operator, `append` | character/string to append | Adds a character or a string to the end of another string | | `+=` operator, `append` | character/string to append | Adds a character or a string to the end of another string |
| `clear` | _none_ | empties the string | | `clear` | _none_ | empties the string |
| `truncate` | target length | cuts off the string at exactly a specified number of characters | | `truncate` | target length | cuts off the string at exactly a specified number of characters |

View File

@ -102,9 +102,10 @@ as a parameter to the function, thereby implementing a _callback_:
use rhai::{Dynamic, FnPtr, NativeCallContext, EvalAltResult}; use rhai::{Dynamic, FnPtr, NativeCallContext, EvalAltResult};
use rhai::plugin::*; // a "prelude" import for macros use rhai::plugin::*; // a "prelude" import for macros
#[export_fn(return_raw)] #[export_fn]
#[rhai_fn(return_raw)]
pub fn greet(context: NativeCallContext, callback: FnPtr) pub fn greet(context: NativeCallContext, callback: FnPtr)
-> Result<Dynamic, Box<EvalAltResult>> -> Result<Dynamic, Box<EvalAltResult>>
{ {
// Call the callback closure with the current context // Call the callback closure with the current context
// to obtain the name to greet! // to obtain the name to greet!
@ -116,15 +117,16 @@ pub fn greet(context: NativeCallContext, callback: FnPtr)
The native call context is also useful in another scenario: protecting a function from malicious scripts. The native call context is also useful in another scenario: protecting a function from malicious scripts.
```rust ```rust
use rhai::{Dynamic, INT, Array, NativeCallContext, EvalAltResult, Position}; use rhai::{Dynamic, Array, NativeCallContext, EvalAltResult, Position};
use rhai::plugin::*; // a "prelude" import for macros use rhai::plugin::*; // a "prelude" import for macros
// This function builds an array of arbitrary size, but is protected // This function builds an array of arbitrary size, but is protected
// against attacks by first checking with the allowed limit set // against attacks by first checking with the allowed limit set
// into the 'Engine'. // into the 'Engine'.
#[export_fn(return_raw)] #[export_fn]
pub fn grow(context: NativeCallContext, size: INT) #[rhai_fn(return_raw)]
-> Result<Dynamic, Box<EvalAltResult>> pub fn grow(context: NativeCallContext, size: i64)
-> Result<Dynamic, Box<EvalAltResult>>
{ {
// Make sure the function does not generate a // Make sure the function does not generate a
// data structure larger than the allowed limit // data structure larger than the allowed limit

View File

@ -363,7 +363,7 @@ use rhai::plugin::*; // a "prelude" import for macros
mod my_module { mod my_module {
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn greet(context: NativeCallContext, callback: FnPtr) pub fn greet(context: NativeCallContext, callback: FnPtr)
-> Result<Dynamic, Box<EvalAltResult>> -> Result<Dynamic, Box<EvalAltResult>>
{ {
// Call the callback closure with the current context // Call the callback closure with the current context
// to obtain the name to greet! // to obtain the name to greet!
@ -376,7 +376,7 @@ mod my_module {
The native call context is also useful in another scenario: protecting a function from malicious scripts. The native call context is also useful in another scenario: protecting a function from malicious scripts.
```rust ```rust
use rhai::{Dynamic, INT, Array, NativeCallContext, EvalAltResult, Position}; use rhai::{Dynamic, Array, NativeCallContext, EvalAltResult, Position};
use rhai::plugin::*; // a "prelude" import for macros use rhai::plugin::*; // a "prelude" import for macros
#[export_module] #[export_module]
@ -385,8 +385,8 @@ mod my_module {
// against attacks by first checking with the allowed limit set // against attacks by first checking with the allowed limit set
// into the 'Engine'. // into the 'Engine'.
#[rhai_fn(return_raw)] #[rhai_fn(return_raw)]
pub fn grow(context: NativeCallContext, size: INT) pub fn grow(context: NativeCallContext, size: i64)
-> Result<Dynamic, Box<EvalAltResult>> -> Result<Dynamic, Box<EvalAltResult>>
{ {
// Make sure the function does not generate a // Make sure the function does not generate a
// data structure larger than the allowed limit // data structure larger than the allowed limit

View File

@ -60,7 +60,8 @@ Function Signature
The function signature passed to `Engine::register_raw_fn` takes the following form: The function signature passed to `Engine::register_raw_fn` takes the following form:
> `Fn(context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result<T, Box<EvalAltResult>> + 'static` > `Fn(context: NativeCallContext, args: &mut [&mut Dynamic])`
> `-> Result<T, Box<EvalAltResult>> + 'static`
where: where:

View File

@ -58,54 +58,6 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str
combine_with_exported_module!(lib, "string", string_functions); combine_with_exported_module!(lib, "string", string_functions);
lib.set_raw_fn(
"pad",
&[TypeId::of::<ImmutableString>(), TypeId::of::<INT>(), TypeId::of::<char>()],
|_context, args| {
let len = *args[1].read_lock::<INT>().unwrap();
// Check if string will be over max size limit
#[cfg(not(feature = "unchecked"))]
if _context.engine().max_string_size() > 0 && len > 0
&& (len as usize) > _context.engine().max_string_size()
{
return EvalAltResult::ErrorDataTooLarge(
"Length of string".to_string(),
_context.engine().max_string_size(),
len as usize,
Position::none(),
).into();
}
if len > 0 {
let ch = mem::take(args[2]).cast::<char>();
let mut s = args[0].write_lock::<ImmutableString>().unwrap();
let orig_len = s.chars().count();
if len as usize > orig_len {
let p = s.make_mut();
for _ in 0..(len as usize - orig_len) {
p.push(ch);
}
#[cfg(not(feature = "unchecked"))]
if _context.engine().max_string_size() > 0 && s.len() > _context.engine().max_string_size() {
return EvalAltResult::ErrorDataTooLarge(
"Length of string".to_string(),
_context.engine().max_string_size(),
s.len(),
Position::none(),
).into();
}
}
}
Ok(())
},
);
// Register string iterator // Register string iterator
lib.set_iter( lib.set_iter(
TypeId::of::<ImmutableString>(), TypeId::of::<ImmutableString>(),
@ -176,7 +128,7 @@ mod string_functions {
pub fn index_of_char_starting_from(s: &str, ch: char, start: INT) -> INT { pub fn index_of_char_starting_from(s: &str, ch: char, start: INT) -> INT {
let start = if start < 0 { let start = if start < 0 {
0 0
} else if (start as usize) >= s.chars().count() { } else if start as usize >= s.chars().count() {
return -1 as INT; return -1 as INT;
} else { } else {
s.chars().take(start as usize).collect::<String>().len() s.chars().take(start as usize).collect::<String>().len()
@ -197,7 +149,7 @@ mod string_functions {
pub fn index_of_string_starting_from(s: &str, find: ImmutableString, start: INT) -> INT { pub fn index_of_string_starting_from(s: &str, find: ImmutableString, start: INT) -> INT {
let start = if start < 0 { let start = if start < 0 {
0 0
} else if (start as usize) >= s.chars().count() { } else if start as usize >= s.chars().count() {
return -1 as INT; return -1 as INT;
} else { } else {
s.chars().take(start as usize).collect::<String>().len() s.chars().take(start as usize).collect::<String>().len()
@ -220,7 +172,7 @@ mod string_functions {
return "".to_string().into(); return "".to_string().into();
} else if start < 0 { } else if start < 0 {
0 0
} else if (start as usize) >= s.chars().count() { } else if start as usize >= s.chars().count() {
return "".to_string().into(); return "".to_string().into();
} else { } else {
start as usize start as usize
@ -228,7 +180,7 @@ mod string_functions {
let chars: StaticVec<_> = s.chars().collect(); let chars: StaticVec<_> = s.chars().collect();
let len = if offset + (len as usize) > chars.len() { let len = if offset + len as usize > chars.len() {
chars.len() - offset chars.len() - offset
} else { } else {
len as usize len as usize
@ -255,7 +207,7 @@ mod string_functions {
return; return;
} else if start < 0 { } else if start < 0 {
0 0
} else if (start as usize) >= s.chars().count() { } else if start as usize >= s.chars().count() {
s.make_mut().clear(); s.make_mut().clear();
return; return;
} else { } else {
@ -264,7 +216,7 @@ mod string_functions {
let chars: StaticVec<_> = s.chars().collect(); let chars: StaticVec<_> = s.chars().collect();
let len = if offset + (len as usize) > chars.len() { let len = if offset + len as usize > chars.len() {
chars.len() - offset chars.len() - offset
} else { } else {
len as usize len as usize
@ -295,6 +247,104 @@ mod string_functions {
pub fn replace_char(s: &mut ImmutableString, find: char, sub: char) { pub fn replace_char(s: &mut ImmutableString, find: char, sub: char) {
*s = s.replace(&find.to_string(), &sub.to_string()).into(); *s = s.replace(&find.to_string(), &sub.to_string()).into();
} }
#[rhai_fn(return_raw)]
pub fn pad(
_context: NativeCallContext,
s: &mut ImmutableString,
len: INT,
ch: char,
) -> Result<Dynamic, Box<EvalAltResult>> {
// Check if string will be over max size limit
#[cfg(not(feature = "unchecked"))]
if _context.engine().max_string_size() > 0
&& len as usize > _context.engine().max_string_size()
{
return EvalAltResult::ErrorDataTooLarge(
"Length of string".to_string(),
_context.engine().max_string_size(),
len as usize,
Position::none(),
)
.into();
}
if len > 0 {
let orig_len = s.chars().count();
if len as usize > orig_len {
let p = s.make_mut();
for _ in 0..(len as usize - orig_len) {
p.push(ch);
}
#[cfg(not(feature = "unchecked"))]
if _context.engine().max_string_size() > 0
&& s.len() > _context.engine().max_string_size()
{
return EvalAltResult::ErrorDataTooLarge(
"Length of string".to_string(),
_context.engine().max_string_size(),
s.len(),
Position::none(),
)
.into();
}
}
}
Ok(().into())
}
#[rhai_fn(name = "pad", return_raw)]
pub fn pad_with_string(
_context: NativeCallContext,
s: &mut ImmutableString,
len: INT,
padding: &str,
) -> Result<Dynamic, Box<EvalAltResult>> {
// Check if string will be over max size limit
#[cfg(not(feature = "unchecked"))]
if _context.engine().max_string_size() > 0
&& len as usize > _context.engine().max_string_size()
{
return EvalAltResult::ErrorDataTooLarge(
"Length of string".to_string(),
_context.engine().max_string_size(),
len as usize,
Position::none(),
)
.into();
}
if len > 0 {
let mut str_len = s.chars().count();
let padding_len = padding.chars().count();
if len as usize > str_len {
let p = s.make_mut();
while str_len < len as usize {
p.push_str(padding);
str_len += padding_len;
}
#[cfg(not(feature = "unchecked"))]
if _context.engine().max_string_size() > 0
&& s.len() > _context.engine().max_string_size()
{
return EvalAltResult::ErrorDataTooLarge(
"Length of string".to_string(),
_context.engine().max_string_size(),
s.len(),
Position::none(),
)
.into();
}
}
}
Ok(().into())
}
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
pub mod arrays { pub mod arrays {