1use clap::{Parser, Subcommand};
4
5use crate::config::{self, ConfigFile, ENV_CLIENT_ID, ENV_PROFILE, ENV_TENANT, ResolvedConfig};
6use crate::error::Result;
7use crate::output::OutputConfig;
8
9#[derive(Debug, Parser)]
10#[command(
11 name = "sharepoint",
12 about = "Agent-friendly SharePoint Online CLI",
13 version,
14 propagate_version = true,
15 disable_help_subcommand = true
16)]
17pub struct Cli {
18 #[arg(long, global = true)]
20 pub json: bool,
21
22 #[arg(long, global = true)]
24 pub quiet: bool,
25
26 #[arg(long, global = true, env = ENV_PROFILE)]
28 pub profile: Option<String>,
29
30 #[arg(long, global = true, env = ENV_TENANT)]
32 pub tenant: Option<String>,
33
34 #[arg(long, global = true, env = ENV_CLIENT_ID)]
36 pub client_id: Option<String>,
37
38 #[command(subcommand)]
39 pub command: Command,
40}
41
42#[derive(Debug, Subcommand)]
43pub enum Command {
44 Init,
46 #[command(subcommand)]
48 Auth(AuthCmd),
49 #[command(subcommand)]
51 Config(ConfigCmd),
52 #[command(subcommand)]
54 Sites(SitesCmd),
55 #[command(subcommand)]
57 Drives(DrivesCmd),
58 #[command(subcommand)]
60 Files(FilesCmd),
61}
62
63#[derive(Debug, Subcommand)]
64pub enum AuthCmd {
65 Login,
67 Logout,
69 Status,
71}
72
73#[derive(Debug, Subcommand)]
74pub enum ConfigCmd {
75 Show,
77 Path,
79}
80
81#[derive(Debug, Subcommand)]
82pub enum SitesCmd {
83 List {
85 #[arg(long)]
86 query: Option<String>,
87 #[arg(long, default_value_t = 50)]
88 limit: usize,
89 #[arg(long)]
90 all: bool,
91 #[arg(long)]
92 page: Option<String>,
93 },
94 Use {
96 site: String,
98 },
99}
100
101#[derive(Debug, Subcommand)]
102pub enum DrivesCmd {
103 List {
105 site: String,
106 #[arg(long, default_value_t = 50)]
107 limit: usize,
108 #[arg(long)]
109 all: bool,
110 },
111}
112
113#[derive(Debug, Subcommand)]
114pub enum FilesCmd {
115 Ls {
117 #[arg(value_name = "REF")]
118 reference: String,
119 #[arg(short = 'r', long)]
120 recursive: bool,
121 #[arg(long, default_value_t = 200)]
122 limit: usize,
123 #[arg(long)]
124 all: bool,
125 #[arg(long)]
126 page: Option<String>,
127 },
128 Stat {
130 #[arg(value_name = "REF")]
131 reference: String,
132 },
133 Download {
135 #[arg(value_name = "REF")]
136 reference: String,
137 #[arg(long, short = 'o')]
138 output: Option<String>,
139 #[arg(long)]
140 overwrite: bool,
141 },
142 Find {
144 #[arg(value_name = "REF")]
145 reference: String,
146 #[arg(long)]
147 query: Option<String>,
148 #[arg(long)]
149 name: Option<String>,
150 #[arg(long, default_value_t = 200)]
151 limit: usize,
152 #[arg(long)]
153 all: bool,
154 #[arg(long)]
155 page: Option<String>,
156 },
157}
158
159pub struct Runtime {
160 pub out: OutputConfig,
161 pub cfg: ResolvedConfig,
162 pub config_file: ConfigFile,
163 pub config_path: std::path::PathBuf,
164 pub cache_path: std::path::PathBuf,
165}
166
167impl Runtime {
168 pub fn build(cli: &Cli) -> Result<Self> {
169 let config_path = config::config_path()?;
170 let config_file = config::load_file(&config_path)?;
171 let env_lookup =
172 |k: &str| -> Option<String> { std::env::var(k).ok().filter(|s| !s.is_empty()) };
173 let mut cfg = config::resolve(&config_file, cli.profile.as_deref(), &env_lookup)?;
174 if let Some(t) = &cli.tenant {
175 cfg.tenant_id = Some(t.clone());
176 }
177 if let Some(c) = &cli.client_id {
178 cfg.client_id = Some(c.clone());
179 }
180 let cache_path = config::token_cache_path()?;
181 Ok(Self {
182 out: OutputConfig::new(cli.json, cli.quiet),
183 cfg,
184 config_file,
185 config_path,
186 cache_path,
187 })
188 }
189}
190
191pub async fn run(cli: Cli) -> Result<()> {
192 let rt = Runtime::build(&cli)?;
193 match cli.command {
194 Command::Init => crate::commands::init::run(&rt).await,
195 Command::Auth(sub) => crate::commands::auth::run(&rt, sub).await,
196 Command::Config(sub) => crate::commands::config::run(&rt, sub).await,
197 Command::Sites(sub) => crate::commands::sites::run(&rt, sub).await,
198 Command::Drives(sub) => crate::commands::drives::run(&rt, sub).await,
199 Command::Files(sub) => crate::commands::files::run(&rt, sub).await,
200 }
201}