diff --git a/src/lib/js/string.rs b/src/lib/js/string.rs index 5d536fdeaf..16697f2ec4 100644 --- a/src/lib/js/string.rs +++ b/src/lib/js/string.rs @@ -340,6 +340,86 @@ pub fn last_index_of(this: Value, _: Value, args: Vec) -> ResultValue { Ok(to_value(highest_index)) } +/// Abstract method StringPad +/// Performs the actual string padding for padStart/End. +/// https://tc39.es/ecma262/#sec-stringpad +fn string_pad( + primitive: String, + max_length: i32, + fill_string: Option, + at_start: bool, +) -> ResultValue { + let primitive_length = primitive.len() as i32; + + if max_length <= primitive_length { + return Ok(to_value(primitive)); + } + + let filler = match fill_string { + Some(filler) => filler, + None => String::from(" "), + }; + + if filler == String::from("") { + return Ok(to_value(primitive)); + } + + let fill_len = max_length - primitive_length; + let mut fill_str = String::new(); + + while fill_str.len() < fill_len as usize { + fill_str.push_str(&filler); + } + // Cut to size max_length + let concat_fill_str: String = fill_str.chars().take(fill_len as usize).collect(); + + if at_start { + return Ok(to_value(concat_fill_str + &primitive)); + } else { + return Ok(to_value(primitive + &concat_fill_str)); + } +} + +/// String.prototype.padEnd ( maxLength [ , fillString ] ) +/// +/// Pads the string with the given filler at the end of the string. +/// Filler defaults to single space. +/// https://tc39.es/ecma262/#sec-string.prototype.padend +pub fn pad_end(this: Value, _: Value, args: Vec) -> ResultValue { + let primitive_val: String = + from_value(this.get_private_field(String::from("PrimitiveValue"))).unwrap(); + if args.len() < 1 { + return Err(to_value("padEnd requires maxLength argument")); + } + let max_length = from_value(args[0].clone()).unwrap(); + let fill_string: Option = match args.len() { + 1 => None, + _ => Some(from_value(args[1].clone()).unwrap()), + }; + + string_pad(primitive_val, max_length, fill_string, false) +} + +/// String.prototype.padStart ( maxLength [ , fillString ] ) +/// +/// Pads the string with the given filler at the start of the string. +/// Filler defaults to single space. +/// https://tc39.es/ecma262/#sec-string.prototype.padstart +pub fn pad_start(this: Value, _: Value, args: Vec) -> ResultValue { + let primitive_val: String = + from_value(this.get_private_field(String::from("PrimitiveValue"))).unwrap(); + if args.len() < 1 { + return Err(to_value("padStart requires maxLength argument")); + } + let max_length = from_value(args[0].clone()).unwrap(); + let fill_string: Option = match args.len() { + 1 => None, + _ => Some(from_value(args[1].clone()).unwrap()), + }; + + string_pad(primitive_val, max_length, fill_string, true) +} + fn is_trimmable_whitespace(c: char) -> bool { // The rust implementation of `trim` does not regard the same characters whitespace as ecma standard does // @@ -399,6 +479,8 @@ pub fn _create(global: &Value) -> Value { proto.set_field_slice("includes", to_value(includes as NativeFunctionData)); proto.set_field_slice("indexOf", to_value(index_of as NativeFunctionData)); proto.set_field_slice("lastIndexOf", to_value(last_index_of as NativeFunctionData)); + proto.set_field_slice("padEnd", to_value(pad_end as NativeFunctionData)); + proto.set_field_slice("padStart", to_value(pad_start as NativeFunctionData)); proto.set_field_slice("trim", to_value(trim as NativeFunctionData)); proto.set_field_slice("trimStart", to_value(trim_start as NativeFunctionData)); string.set_field_slice(PROTOTYPE, proto);