From e5fcde172b38d9a71292d0b4dd7f5d347da2e748 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Fri, 19 Jun 2026 23:54:12 +0900 Subject: feat(command): implement --help output --- crates/shirabe-php-shim/Cargo.toml | 1 + crates/shirabe-php-shim/src/lib.rs | 52 +++++++++++++++++++++++++++++++++----- 2 files changed, 47 insertions(+), 6 deletions(-) (limited to 'crates/shirabe-php-shim') diff --git a/crates/shirabe-php-shim/Cargo.toml b/crates/shirabe-php-shim/Cargo.toml index b577c1f..abfe89e 100644 --- a/crates/shirabe-php-shim/Cargo.toml +++ b/crates/shirabe-php-shim/Cargo.toml @@ -10,6 +10,7 @@ fastrand.workspace = true indexmap.workspace = true regex.workspace = true serde.workspace = true +serde_json.workspace = true zip.workspace = true [lints] diff --git a/crates/shirabe-php-shim/src/lib.rs b/crates/shirabe-php-shim/src/lib.rs index 6b8a229..5821819 100644 --- a/crates/shirabe-php-shim/src/lib.rs +++ b/crates/shirabe-php-shim/src/lib.rs @@ -492,8 +492,14 @@ pub fn array_keys(_array: &IndexMap) -> Vec { _array.keys().cloned().collect() } -pub fn str_replace(_search: &str, _replace: &str, _subject: &str) -> String { - todo!() +pub fn str_replace(search: &str, replace: &str, subject: &str) -> String { + // PHP returns the subject unchanged when the search string is empty, whereas Rust's + // `str::replace` would insert `replace` between every character. + if search.is_empty() { + return subject.to_string(); + } + + subject.replace(search, replace) } pub fn php_to_string(value: &PhpMixed) -> String { @@ -1362,8 +1368,35 @@ pub fn filesize(_path: &str) -> Option { std::fs::metadata(_path).ok().map(|m| m.len() as i64) } -pub fn json_encode_ex(_value: &T, _flags: i64) -> Option { - todo!() +pub fn json_encode_ex(value: &T, flags: i64) -> Option { + // serde_json's compact output already matches PHP's `json_encode` with both + // JSON_UNESCAPED_SLASHES and JSON_UNESCAPED_UNICODE set: forward slashes and non-ASCII + // characters are emitted verbatim. The two flags below re-apply PHP's default escaping when + // they are absent. + // TODO(phase-c): other flags (e.g. JSON_PRETTY_PRINT, JSON_HEX_*, JSON_THROW_ON_ERROR) are not + // handled yet; add them when a call site needs them. + let mut s = serde_json::to_string(value).ok()?; + + if flags & JSON_UNESCAPED_SLASHES == 0 { + s = s.replace('/', "\\/"); + } + + if flags & JSON_UNESCAPED_UNICODE == 0 { + let mut out = String::with_capacity(s.len()); + for c in s.chars() { + if (c as u32) <= 0x7F { + out.push(c); + } else { + let mut buf = [0u16; 2]; + for unit in c.encode_utf16(&mut buf) { + out.push_str(&format!("\\u{:04x}", unit)); + } + } + } + s = out; + } + + Some(s) } pub const JSON_INVALID_UTF8_IGNORE: i64 = 1048576; @@ -1650,8 +1683,15 @@ pub fn feof(_stream: PhpMixed) -> bool { todo!() } -pub fn str_replace_array(_search: &[String], _replace: &[String], _subject: &str) -> String { - todo!() +pub fn str_replace_array(search: &[String], replace: &[String], subject: &str) -> String { + // PHP's array form of str_replace replaces each search element in order with the replace + // element at the same index, falling back to an empty string when replace is shorter. + let mut result = subject.to_string(); + for (i, s) in search.iter().enumerate() { + let r = replace.get(i).map(String::as_str).unwrap_or(""); + result = str_replace(s, r, &result); + } + result } pub fn file(_filename: &str, _flags: i64) -> Option> { -- cgit v1.3.1