aether_cli/settings/
mod.rs1use crate::init::{InitRequest, InitTargetRequest, Preset};
2use clap::{Args, Subcommand};
3use llm::catalog::Provider;
4use std::path::PathBuf;
5
6#[derive(Debug, Clone, Subcommand)]
7pub enum SettingsCommand {
8 Init(SettingsInitArgs),
10}
11
12#[derive(Debug, Clone, Args)]
13#[command(group(
14 clap::ArgGroup::new("scope")
15 .required(true)
16 .args(["user", "project"])
17))]
18pub struct SettingsInitArgs {
19 #[arg(long, conflicts_with = "project")]
21 pub user: bool,
22
23 #[arg(long, conflicts_with = "user")]
25 pub project: bool,
26
27 #[arg(long, default_value = ".", requires = "project", conflicts_with = "user")]
29 pub path: PathBuf,
30
31 #[arg(long)]
33 pub provider: Option<Provider>,
34
35 #[arg(long)]
37 pub preset: Option<Preset>,
38
39 #[arg(long)]
41 pub force: bool,
42}
43
44impl From<SettingsInitArgs> for InitRequest {
45 fn from(args: SettingsInitArgs) -> Self {
46 let target = if args.user { InitTargetRequest::User } else { InitTargetRequest::Project { path: args.path } };
47 Self { target, provider: args.provider, preset: args.preset, force: args.force }
48 }
49}
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54 use clap::Parser;
55
56 #[derive(Debug, Parser)]
57 struct TestCli {
58 #[command(subcommand)]
59 command: SettingsCommand,
60 }
61
62 fn parse(args: &[&str]) -> Result<TestCli, clap::Error> {
63 TestCli::try_parse_from(std::iter::once("test").chain(args.iter().copied()))
64 }
65
66 #[test]
67 fn accepts_user_scope() {
68 let cli = parse(&["init", "--user"]).unwrap();
69 let SettingsCommand::Init(args) = cli.command;
70 assert!(args.user);
71 assert!(!args.project);
72 }
73
74 #[test]
75 fn accepts_project_scope_and_path() {
76 let cli = parse(&["init", "--project", "--path", "/tmp/repo"]).unwrap();
77 let SettingsCommand::Init(args) = cli.command;
78 assert!(args.project);
79 assert_eq!(args.path, PathBuf::from("/tmp/repo"));
80 }
81
82 #[test]
83 fn accepts_provider_preset_and_force() {
84 let cli = parse(&["init", "--user", "--provider", "codex", "--preset", "minimal", "--force"]).unwrap();
85 let SettingsCommand::Init(args) = cli.command;
86 assert_eq!(args.provider, Some(Provider::Codex));
87 assert_eq!(args.preset, Some(Preset::Minimal));
88 assert!(args.force);
89 }
90
91 #[test]
92 fn rejects_missing_scope() {
93 assert!(parse(&["init"]).is_err());
94 }
95
96 #[test]
97 fn rejects_both_scopes() {
98 assert!(parse(&["init", "--user", "--project"]).is_err());
99 }
100
101 #[test]
102 fn rejects_user_path() {
103 assert!(parse(&["init", "--user", "--path", "/tmp/repo"]).is_err());
104 }
105}