use std::collections::HashMap;
use crate::error::Result;
pub(super) fn parse_params(raw: &[String]) -> Result<HashMap<String, String>> {
let mut map = HashMap::with_capacity(raw.len());
for s in raw {
let (k, v) = s.split_once('=').ok_or_else(|| {
anyhow::anyhow!(
"invalid --param '{}' — expected KEY=VALUE (the '=' is missing). \
Example: --param table=orders",
s
)
})?;
map.insert(k.to_string(), v.to_string());
}
Ok(map)
}
pub(super) fn resolve_init_source(
source: Option<String>,
source_env: Option<String>,
source_file: Option<String>,
) -> Result<(String, crate::init::SourceProvenance)> {
use crate::init::SourceProvenance;
if let Some(url) = source {
return Ok((url, SourceProvenance::Inline));
}
if let Some(var) = source_env {
let url = std::env::var(&var)
.map_err(|_| anyhow::anyhow!("--source-env '{}' is not set in the environment", var))?;
return Ok((url, SourceProvenance::Env(var)));
}
if let Some(path) = source_file {
let raw = std::fs::read_to_string(&path)
.map_err(|e| anyhow::anyhow!("cannot read --source-file '{}': {}", path, e))?;
return Ok((raw.trim().to_string(), SourceProvenance::File(path)));
}
anyhow::bail!("--source, --source-env, or --source-file is required")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_params_basic() {
let input = vec!["KEY=value".to_string()];
let result = parse_params(&input).unwrap();
assert_eq!(result.get("KEY").unwrap(), "value");
}
#[test]
fn parse_params_equals_in_value() {
let input = vec!["FILTER=x=1 AND y=2".to_string()];
let result = parse_params(&input).unwrap();
assert_eq!(result.get("FILTER").unwrap(), "x=1 AND y=2");
}
#[test]
fn parse_params_multiple() {
let input = vec!["A=1".to_string(), "B=2".to_string()];
let result = parse_params(&input).unwrap();
assert_eq!(result.len(), 2);
assert_eq!(result.get("A").unwrap(), "1");
assert_eq!(result.get("B").unwrap(), "2");
}
#[test]
fn parse_params_empty_input() {
let result = parse_params(&[]).unwrap();
assert!(result.is_empty());
}
#[test]
fn parse_params_no_equals_is_a_loud_error() {
let input = vec!["NO_EQUALS_HERE".to_string()];
let err = parse_params(&input).unwrap_err();
assert!(
format!("{err:#}").contains("expected KEY=VALUE"),
"malformed --param must name the KEY=VALUE form: {err:#}"
);
}
#[test]
fn parse_params_empty_value() {
let input = vec!["KEY=".to_string()];
let result = parse_params(&input).unwrap();
assert_eq!(result.get("KEY").unwrap(), "");
}
#[test]
fn parse_params_duplicate_last_wins() {
let input = vec!["K=first".to_string(), "K=second".to_string()];
let result = parse_params(&input).unwrap();
assert_eq!(result.get("K").unwrap(), "second");
}
#[test]
fn json_error_output_is_valid_json() {
let err = anyhow::anyhow!("connection refused");
let output = serde_json::json!({"error": format!("{err:#}")}).to_string();
let parsed: serde_json::Value = serde_json::from_str(&output).expect("must be valid JSON");
assert_eq!(parsed["error"], "connection refused");
}
}