fix(config): emit error.v1 code=config_not_found for missing --config path (Bug #10)
이전: `kebab search "rust" --config /tmp/nonexistent.toml --json` 가 exit=0 +
`{"hits":[]}` silent fallback to XDG default. typo / wrong path 가 0-hit 으로만
surface — debugging nightmare.
이후: kebab_config::ConfigNotFound thiserror::Error 추가, Config::load 의
`Some(p) if !p.exists()` arm 이 anyhow::Error::new(ConfigNotFound { path })
return. kebab_app::error_wire::classify 가 downcast → ErrorV1 code=config_not_found,
hint, details.path 채워서 stderr 에 ndjson 으로 emit.
R-1 (relative path): std::path::Path::exists() 는 cwd-relative — 별도 작업 없이
absolute + relative 모두 cover. integration test 두 개로 검증.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,15 @@ pub struct ConfigInvalid {
|
||||
pub cause: String,
|
||||
}
|
||||
|
||||
/// p20-bugfix3 Bug #10: explicit `--config <path>` was missing → silent
|
||||
/// fallback to defaults instead of fail-fast. `kebab-app::error_wire::classify`
|
||||
/// downcasts → `code: "config_not_found"` ErrorV1.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("config file does not exist: {path}")]
|
||||
pub struct ConfigNotFound {
|
||||
pub path: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Config {
|
||||
pub schema_version: u32,
|
||||
@@ -688,7 +697,11 @@ impl Config {
|
||||
pub fn load(path: Option<&Path>) -> anyhow::Result<Self> {
|
||||
let from_disk = match path {
|
||||
Some(p) if p.exists() => Self::from_file(p)?,
|
||||
Some(_) => Self::defaults(),
|
||||
Some(p) => {
|
||||
return Err(anyhow::Error::new(ConfigNotFound {
|
||||
path: p.to_path_buf(),
|
||||
}));
|
||||
}
|
||||
None => {
|
||||
let p = Self::xdg_config_path();
|
||||
if p.exists() {
|
||||
@@ -1817,4 +1830,17 @@ mod fb27_tests {
|
||||
signal.cause
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn config_load_explicit_nonexistent_path_returns_config_not_found() {
|
||||
// Bug #10: --config /tmp/nonexistent.toml → silent fallback 금지.
|
||||
let p = std::path::Path::new("/tmp/__kebab_bugfix3_nonexistent.toml");
|
||||
assert!(!p.exists(), "test precondition: path must not exist");
|
||||
|
||||
let err = Config::load(Some(p)).expect_err("expected ConfigNotFound");
|
||||
let signal = err
|
||||
.downcast_ref::<ConfigNotFound>()
|
||||
.expect("from_load error should downcast to ConfigNotFound");
|
||||
assert_eq!(signal.path, p.to_path_buf());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user