diff --git a/Cargo.lock b/Cargo.lock index 5bee2a94..58bc9172 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -409,37 +409,12 @@ version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "bytes" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" -[[package]] -name = "bzip2" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47" -dependencies = [ - "bzip2-sys", -] - -[[package]] -name = "bzip2-sys" -version = "0.1.13+1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" -dependencies = [ - "cc", - "pkg-config", -] - [[package]] name = "cc" version = "1.2.66" @@ -447,8 +422,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996" dependencies = [ "find-msvc-tools", - "jobserver", - "libc", "shlex", ] @@ -583,12 +556,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "constant_time_eq" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" - [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -613,21 +580,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crc" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" -dependencies = [ - "crc-catalog", -] - -[[package]] -name = "crc-catalog" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" - [[package]] name = "crc32fast" version = "1.5.0" @@ -697,18 +649,6 @@ dependencies = [ "syn", ] -[[package]] -name = "deflate64" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac6b926516df9c60bfa16e107b21086399f8285a44ca9711344b9e553c5146e2" - -[[package]] -name = "deranged" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" - [[package]] name = "derive_arbitrary" version = "1.4.2" @@ -988,20 +928,6 @@ dependencies = [ "wasi", ] -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi 5.3.0", - "wasip2", - "wasm-bindgen", -] - [[package]] name = "getrandom" version = "0.4.3" @@ -1010,7 +936,7 @@ checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", "libc", - "r-efi 6.0.0", + "r-efi", "rand_core 0.10.1", ] @@ -1365,16 +1291,6 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" -[[package]] -name = "jobserver" -version = "0.1.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" -dependencies = [ - "getrandom 0.4.3", - "libc", -] - [[package]] name = "js-sys" version = "0.3.103" @@ -1442,27 +1358,6 @@ version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" -[[package]] -name = "lzma-rs" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e" -dependencies = [ - "byteorder", - "crc", -] - -[[package]] -name = "lzma-sys" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - [[package]] name = "matchit" version = "0.8.4" @@ -1523,12 +1418,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "num-conv" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" - [[package]] name = "num-traits" version = "0.2.19" @@ -1674,12 +1563,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - [[package]] name = "ppv-lite86" version = "0.2.21" @@ -1731,12 +1614,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - [[package]] name = "r-efi" version = "6.0.0" @@ -2181,17 +2058,6 @@ dependencies = [ "unsafe-libyaml", ] -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures 0.2.17", - "digest", -] - [[package]] name = "sha2" version = "0.10.9" @@ -2416,25 +2282,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "time" -version = "0.3.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50" -dependencies = [ - "deranged", - "num-conv", - "powerfmt", - "serde_core", - "time-core", -] - -[[package]] -name = "time-core" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" - [[package]] name = "tinystr" version = "0.8.3" @@ -2761,15 +2608,6 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" -[[package]] -name = "wasip2" -version = "1.0.4+wasi-0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" -dependencies = [ - "wit-bindgen", -] - [[package]] name = "wasm-bindgen" version = "0.2.126" @@ -3169,12 +3007,6 @@ version = "0.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" -[[package]] -name = "wit-bindgen" -version = "0.57.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" - [[package]] name = "writeable" version = "0.6.3" @@ -3191,15 +3023,6 @@ dependencies = [ "rustix 1.1.4", ] -[[package]] -name = "xz2" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" -dependencies = [ - "lzma-sys", -] - [[package]] name = "yansi" version = "1.0.1" @@ -3275,20 +3098,6 @@ name = "zeroize" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] [[package]] name = "zerotrie" @@ -3329,28 +3138,15 @@ version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50" dependencies = [ - "aes", "arbitrary", - "bzip2", - "constant_time_eq", "crc32fast", "crossbeam-utils", - "deflate64", "displaydoc", "flate2", - "getrandom 0.3.4", - "hmac", "indexmap", - "lzma-rs", "memchr", - "pbkdf2", - "sha1", "thiserror", - "time", - "xz2", - "zeroize", "zopfli", - "zstd", ] [[package]] @@ -3370,31 +3166,3 @@ dependencies = [ "log", "simd-adler32", ] - -[[package]] -name = "zstd" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "7.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" -dependencies = [ - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.16+zstd.1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" -dependencies = [ - "cc", - "pkg-config", -] diff --git a/Cargo.toml b/Cargo.toml index d6e56427..c30d415b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,7 @@ hex = "0.4" base64 = "0.22" flate2 = "1" tar = "0.4" -zip = "2" +zip = { version = "2", default-features = false, features = ["deflate"] } rusqlite = { version = "0.40", features = ["bundled"] } rand = "0.8" aes-gcm = "0.10" diff --git a/src/components.rs b/src/components.rs index b2e65d6f..80348fd6 100644 --- a/src/components.rs +++ b/src/components.rs @@ -68,11 +68,8 @@ fn remove_claude_mcp_server(name: &str) -> bool { .is_ok() } -fn json_at(path: &PathBuf) -> Value { - fs::read_to_string(path) - .ok() - .and_then(|s| crate::jsonc::parse_jsonc(&s).ok()) - .unwrap_or(Value::Null) +fn json_at(path: &std::path::Path) -> Value { + crate::jsonc::read_jsonc(path, || Value::Null) } fn plugin_enabled(settings: &Value, key: &str) -> bool { @@ -102,10 +99,7 @@ fn write_pinned_mode(path: &PathBuf) -> bool { } fn merge_json(path: &PathBuf, root_key: &str, key: &str, value: Value) -> bool { - let mut existing: Value = fs::read_to_string(path) - .ok() - .and_then(|s| crate::jsonc::parse_jsonc(&s).ok()) - .unwrap_or_else(|| serde_json::json!({})); + let mut existing: Value = crate::jsonc::read_jsonc(path, || serde_json::json!({})); if !existing.is_object() { existing = serde_json::json!({}); } @@ -125,10 +119,7 @@ fn merge_json(path: &PathBuf, root_key: &str, key: &str, value: Value) -> bool { } fn merge_opencode_mcp(path: &PathBuf, key: &str, entry: Value) -> bool { - let mut existing: Value = fs::read_to_string(path) - .ok() - .and_then(|s| crate::jsonc::parse_jsonc(&s).ok()) - .unwrap_or_else(|| serde_json::json!({})); + let mut existing: Value = crate::jsonc::read_jsonc(path, || serde_json::json!({})); if !existing.is_object() { existing = serde_json::json!({}); } diff --git a/src/init.rs b/src/init.rs index c7143926..5ceb44b4 100644 --- a/src/init.rs +++ b/src/init.rs @@ -508,10 +508,7 @@ fn wire_opencode() { let rules_dir = home().join(".config").join("opencode").join("rules"); let rule_files: &[&str] = &["exa.md", "git.md", "lean-ctx.md"]; - let mut config: Value = fs::read_to_string(&path) - .ok() - .and_then(|s| crate::jsonc::parse_jsonc(&s).ok()) - .unwrap_or_else(|| json!({})); + let mut config: Value = crate::jsonc::read_jsonc(&path, || json!({})); if !config.is_object() { config = json!({}); } diff --git a/src/jsonc.rs b/src/jsonc.rs index 4de4e0b7..6543a835 100644 --- a/src/jsonc.rs +++ b/src/jsonc.rs @@ -14,6 +14,32 @@ pub fn parse_jsonc(input: &str) -> Result { serde_json::from_str(&cleaned) } +/// Reads `path`, parses it as JSONC, and falls back to `default()` on any +/// failure (missing file, unreadable, or invalid JSON/JSONC) — the shared +/// read-then-fallback contract every agent-config call site needs. +pub fn read_jsonc(path: &std::path::Path, default: impl FnOnce() -> Value) -> Value { + std::fs::read_to_string(path) + .ok() + .and_then(|s| parse_jsonc(&s).ok()) + .unwrap_or_else(default) +} +/// Advances past a `"..."` string starting at `bytes[i] == b'"'`, honoring +/// backslash escapes. Returns the index just past the closing quote (or +/// `bytes.len()` if unterminated). +fn skip_string(bytes: &[u8], mut i: usize) -> usize { + let len = bytes.len(); + i += 1; + while i < len { + let c = bytes[i]; + i += 1; + if c == b'\\' && i < len { + i += 1; + } else if c == b'"' { + break; + } + } + i +} fn strip_json_comments(input: &str) -> String { let bytes = input.as_bytes(); let len = bytes.len(); @@ -25,16 +51,7 @@ fn strip_json_comments(input: &str) -> String { let b = bytes[i]; if b == b'"' { - i += 1; - while i < len { - let c = bytes[i]; - i += 1; - if c == b'\\' && i < len { - i += 1; - } else if c == b'"' { - break; - } - } + i = skip_string(bytes, i); continue; } @@ -86,16 +103,7 @@ fn strip_trailing_commas(input: &str) -> String { let b = bytes[i]; if b == b'"' { - i += 1; - while i < len { - let c = bytes[i]; - i += 1; - if c == b'\\' && i < len { - i += 1; - } else if c == b'"' { - break; - } - } + i = skip_string(bytes, i); continue; }