Skip to content

Commit

Permalink
native string utils cheatcodes (#6891)
Browse files Browse the repository at this point in the history
* string utils cheatcodes created

* fmt: run rustfmt

* tests: solidity tests added

* cargo cheats

* update

* update

* chore: update defs

---------

Co-authored-by: Matthias Seitz <[email protected]>
Co-authored-by: Enrique Ortiz <[email protected]>
  • Loading branch information
3 people authored Jan 25, 2024
1 parent 3cc7bda commit c61dc80
Show file tree
Hide file tree
Showing 5 changed files with 208 additions and 2 deletions.
100 changes: 100 additions & 0 deletions crates/cheatcodes/assets/cheatcodes.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions crates/cheatcodes/spec/src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1623,6 +1623,22 @@ interface Vm {
#[cheatcode(group = String)]
function parseBool(string calldata stringifiedValue) external pure returns (bool parsedValue);

/// Converts the given `string` value to Lowercase.
#[cheatcode(group = String)]
function toLowercase(string calldata input) external pure returns (string memory output);
/// Converts the given `string` value to Uppercase.
#[cheatcode(group = String)]
function toUppercase(string calldata input) external pure returns (string memory output);
/// Trims leading and trailing whitespace from the given `string` value.
#[cheatcode(group = String)]
function trim(string calldata input) external pure returns (string memory output);
/// Replaces occurrences of `from` in the given `string` with `to`.
#[cheatcode(group = String)]
function replace(string calldata input, string calldata from, string calldata to) external pure returns (string memory output);
/// Splits the given `string` into an array of strings divided by the `delimiter`.
#[cheatcode(group = String)]
function split(string calldata input, string calldata delimiter) external pure returns (string[] memory outputs);

// ======== JSON Parsing and Manipulation ========

// -------- Reading --------
Expand Down
47 changes: 45 additions & 2 deletions crates/cheatcodes/src/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,47 @@ impl Cheatcode for parseBoolCall {
}
}

// toLowercase
impl Cheatcode for toLowercaseCall {
fn apply(&self, _state: &mut Cheatcodes) -> Result {
let Self { input } = self;
Ok(input.to_lowercase().abi_encode())
}
}

// toUppercase
impl Cheatcode for toUppercaseCall {
fn apply(&self, _state: &mut Cheatcodes) -> Result {
let Self { input } = self;
Ok(input.to_uppercase().abi_encode())
}
}

// trim
impl Cheatcode for trimCall {
fn apply(&self, _state: &mut Cheatcodes) -> Result {
let Self { input } = self;
Ok(input.trim().abi_encode())
}
}

// Replace
impl Cheatcode for replaceCall {
fn apply(&self, _state: &mut Cheatcodes) -> Result {
let Self { input, from, to } = self;
Ok(input.replace(from, to).abi_encode())
}
}

// Split
impl Cheatcode for splitCall {
fn apply(&self, _state: &mut Cheatcodes) -> Result {
let Self { input, delimiter } = self;
let parts: Vec<&str> = input.split(delimiter).collect();
Ok(parts.abi_encode())
}
}

pub(super) fn parse(s: &str, ty: &DynSolType) -> Result {
parse_value(s, ty).map(|v| v.abi_encode())
}
Expand Down Expand Up @@ -136,7 +177,9 @@ fn parse_value_fallback(s: &str, ty: &DynSolType) -> Option<Result<DynSolValue,
"0" => false,
s if s.eq_ignore_ascii_case("true") => true,
s if s.eq_ignore_ascii_case("false") => false,
_ => return None,
_ => {
return None;
}
};
return Some(Ok(DynSolValue::Bool(b)));
}
Expand All @@ -145,7 +188,7 @@ fn parse_value_fallback(s: &str, ty: &DynSolType) -> Option<Result<DynSolValue,
DynSolType::FixedBytes(_) |
DynSolType::Bytes => {
if !s.starts_with("0x") && s.chars().all(|c| c.is_ascii_hexdigit()) {
return Some(Err("missing hex prefix (\"0x\") for hex string"))
return Some(Err("missing hex prefix (\"0x\") for hex string"));
}
}
_ => {}
Expand Down
42 changes: 42 additions & 0 deletions testdata/cheats/StringUtils.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.18;

import "ds-test/test.sol";
import "./Vm.sol";

contract StringManipulationTest is DSTest {
Vm constant vm = Vm(HEVM_ADDRESS);

function testToLowercase() public {
string memory original = "Hello World";
string memory lowercased = vm.toLowercase(original);
assertEq("hello world", lowercased);
}

function testToUppercase() public {
string memory original = "Hello World";
string memory uppercased = vm.toUppercase(original);
assertEq("HELLO WORLD", uppercased);
}

function testTrim() public {
string memory original = " Hello World ";
string memory trimmed = vm.trim(original);
assertEq("Hello World", trimmed);
}

function testReplace() public {
string memory original = "Hello World";
string memory replaced = vm.replace(original, "World", "Reth");
assertEq("Hello Reth", replaced);
}

function testSplit() public {
string memory original = "Hello,World,Reth";
string[] memory splitResult = vm.split(original, ",");
assertEq(3, splitResult.length);
assertEq("Hello", splitResult[0]);
assertEq("World", splitResult[1]);
assertEq("Reth", splitResult[2]);
}
}
5 changes: 5 additions & 0 deletions testdata/cheats/Vm.sol

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit c61dc80

Please sign in to comment.