use std::time::Duration;
#[allow(clippy::struct_excessive_bools)]
#[derive(Debug, Clone, Default)]
pub struct Config {
pub system_prompt: Option<String>,
pub append_system_prompt: Option<String>,
pub model: Option<String>,
pub allowed_tools: Vec<String>,
pub disallowed_tools: Vec<String>,
pub continue_session: bool,
pub resume: Option<String>,
pub cwd: Option<std::path::PathBuf>,
pub env: Vec<(String, String)>,
pub isolated: bool,
pub dangerously_skip_permissions: bool,
pub json_schema: Option<serde_json::Value>,
pub timeout: Option<Duration>,
}
impl Config {
#[must_use]
pub fn build_args(&self, prompt: &str) -> Vec<String> {
let mut args = Vec::new();
args.push("-p".to_string());
args.push(prompt.to_string());
if let Some(system_prompt) = &self.system_prompt {
args.push("--system-prompt".to_string());
args.push(system_prompt.clone());
}
if let Some(append_system_prompt) = &self.append_system_prompt {
args.push("--append-system-prompt".to_string());
args.push(append_system_prompt.clone());
}
if let Some(model) = &self.model {
args.push("--model".to_string());
args.push(model.clone());
}
for tool in &self.allowed_tools {
args.push("--allowedTools".to_string());
args.push(tool.clone());
}
for tool in &self.disallowed_tools {
args.push("--disallowedTools".to_string());
args.push(tool.clone());
}
if self.continue_session {
args.push("--continue".to_string());
}
if let Some(resume) = &self.resume {
args.push("--resume".to_string());
args.push(resume.clone());
}
if self.dangerously_skip_permissions {
args.push("--dangerously-skip-permissions".to_string());
}
if let Some(json_schema) = &self.json_schema {
args.push("--json-schema".to_string());
args.push(json_schema.to_string());
}
args.push("--output-format".to_string());
args.push("json".to_string());
args
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn minimal_config_appends_output_format() {
let config = Config::default();
let args = config.build_args("hello");
assert_eq!(
args,
vec!["-p", "hello", "--output-format", "json"]
.into_iter()
.map(String::from)
.collect::<Vec<_>>()
);
}
#[test]
fn full_config_orders_flags() {
let config = Config {
system_prompt: Some("be helpful".to_string()),
append_system_prompt: Some("also be terse".to_string()),
model: Some("claude-opus-4".to_string()),
allowed_tools: vec!["bash".to_string()],
disallowed_tools: vec!["web".to_string()],
continue_session: true,
resume: Some("session-123".to_string()),
dangerously_skip_permissions: true,
json_schema: Some(serde_json::json!({"type": "object"})),
..Config::default()
};
let args = config.build_args("hi");
assert_eq!(
args,
vec![
"-p",
"hi",
"--system-prompt",
"be helpful",
"--append-system-prompt",
"also be terse",
"--model",
"claude-opus-4",
"--allowedTools",
"bash",
"--disallowedTools",
"web",
"--continue",
"--resume",
"session-123",
"--dangerously-skip-permissions",
"--json-schema",
"{\"type\":\"object\"}",
"--output-format",
"json",
]
.into_iter()
.map(String::from)
.collect::<Vec<_>>()
);
}
#[test]
fn dangerously_skip_permissions_defaults_to_false_and_omits_flag() {
let config = Config::default();
assert!(!config.dangerously_skip_permissions);
assert!(!config
.build_args("hi")
.contains(&"--dangerously-skip-permissions".to_string()));
}
#[test]
fn json_schema_defaults_to_none_and_omits_flag() {
let config = Config::default();
assert!(config.json_schema.is_none());
assert!(!config
.build_args("hi")
.contains(&"--json-schema".to_string()));
}
}