From 319c0f88188d0ad43ee703d610c6a4aa8ff4c534 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Vincent?= <28714795+leovct@users.noreply.github.com> Date: Fri, 11 Oct 2024 20:44:32 +0200 Subject: [PATCH] feat(cheatcodes): implement new cheatcode to check if a string contains another string (#9085) * feat: implement new cheatcode to check if a string contains another string * chore: make clippy and rustfmt happy * chore: vm.contains should return a boolean * Update testdata/cheats/Vm.sol Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * Update crates/cheatcodes/spec/src/vm.rs Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> * chore: update `cheatcodes.json` * chore: update var names * chore: rename to `vm.contains` * Update crates/cheatcodes/spec/src/vm.rs Co-authored-by: Matt Solomon * Update crates/cheatcodes/spec/src/vm.rs Co-authored-by: Matt Solomon * chore: address PR comments --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Co-authored-by: Matt Solomon --- crates/cheatcodes/assets/cheatcodes.json | 20 ++++++++++++++++++++ crates/cheatcodes/spec/src/vm.rs | 3 +++ crates/cheatcodes/src/string.rs | 8 ++++++++ testdata/cheats/Vm.sol | 1 + testdata/default/cheats/StringUtils.t.sol | 6 ++++++ 5 files changed, 38 insertions(+) diff --git a/crates/cheatcodes/assets/cheatcodes.json b/crates/cheatcodes/assets/cheatcodes.json index d64df4860abe1..b41f736677bcb 100644 --- a/crates/cheatcodes/assets/cheatcodes.json +++ b/crates/cheatcodes/assets/cheatcodes.json @@ -3347,6 +3347,26 @@ "status": "stable", "safety": "safe" }, + { + "func": { + "id": "contains", + "description": "Returns true if `search` is found in `subject`, false otherwise.", + "declaration": "function contains(string calldata subject, string calldata search) external returns (bool result);", + "visibility": "external", + "mutability": "", + "signature": "contains(string,string)", + "selector": "0x3fb18aec", + "selectorBytes": [ + 63, + 177, + 138, + 236 + ] + }, + "group": "string", + "status": "stable", + "safety": "safe" + }, { "func": { "id": "cool", diff --git a/crates/cheatcodes/spec/src/vm.rs b/crates/cheatcodes/spec/src/vm.rs index 761d64e9bedbc..2a342a04438ec 100644 --- a/crates/cheatcodes/spec/src/vm.rs +++ b/crates/cheatcodes/spec/src/vm.rs @@ -1976,6 +1976,9 @@ interface Vm { /// Returns 0 in case of an empty `key`. #[cheatcode(group = String)] function indexOf(string calldata input, string calldata key) external pure returns (uint256); + /// Returns true if `search` is found in `subject`, false otherwise. + #[cheatcode(group = String)] + function contains(string calldata subject, string calldata search) external returns (bool result); // ======== JSON Parsing and Manipulation ======== diff --git a/crates/cheatcodes/src/string.rs b/crates/cheatcodes/src/string.rs index e7435d541048c..a4c06eef650b1 100644 --- a/crates/cheatcodes/src/string.rs +++ b/crates/cheatcodes/src/string.rs @@ -144,6 +144,14 @@ impl Cheatcode for indexOfCall { } } +// contains +impl Cheatcode for containsCall { + fn apply(&self, _state: &mut Cheatcodes) -> Result { + let Self { subject, search } = self; + Ok(subject.contains(search).abi_encode()) + } +} + pub(super) fn parse(s: &str, ty: &DynSolType) -> Result { parse_value(s, ty).map(|v| v.abi_encode()) } diff --git a/testdata/cheats/Vm.sol b/testdata/cheats/Vm.sol index 2572108972b17..e47a79cc65ddb 100644 --- a/testdata/cheats/Vm.sol +++ b/testdata/cheats/Vm.sol @@ -162,6 +162,7 @@ interface Vm { function computeCreate2Address(bytes32 salt, bytes32 initCodeHash, address deployer) external pure returns (address); function computeCreate2Address(bytes32 salt, bytes32 initCodeHash) external pure returns (address); function computeCreateAddress(address deployer, uint256 nonce) external pure returns (address); + function contains(string calldata subject, string calldata search) external returns (bool result); function cool(address target) external; function copyFile(string calldata from, string calldata to) external returns (uint64 copied); function copyStorage(address from, address to) external; diff --git a/testdata/default/cheats/StringUtils.t.sol b/testdata/default/cheats/StringUtils.t.sol index b65346a7a4e0a..256d65302a445 100644 --- a/testdata/default/cheats/StringUtils.t.sol +++ b/testdata/default/cheats/StringUtils.t.sol @@ -51,4 +51,10 @@ contract StringManipulationTest is DSTest { assertEq(vm.indexOf(input, key3), 0); assertEq(vm.indexOf(input, key4), type(uint256).max); } + + function testContains() public { + string memory subject = "this is a test"; + assert(vm.contains(subject, "test")); + assert(!vm.contains(subject, "foundry")); + } }