Skip to main content

sharepoint_cli/commands/
sites.rs

1//! `sharepoint sites list | use`
2
3use crate::auth::AuthContext;
4use crate::cli::{Runtime, SitesCmd};
5use crate::config;
6use crate::error::{CliError, Result};
7use crate::graph::{GraphClient, sites};
8
9pub async fn run(rt: &Runtime, cmd: SitesCmd) -> Result<()> {
10    match cmd {
11        SitesCmd::List {
12            query,
13            limit,
14            all,
15            page,
16        } => list(rt, query.as_deref(), limit, all, page.as_deref()).await,
17        SitesCmd::Use { site } => use_site(rt, &site).await,
18    }
19}
20
21async fn list(
22    rt: &Runtime,
23    query: Option<&str>,
24    limit: usize,
25    all: bool,
26    page: Option<&str>,
27) -> Result<()> {
28    let auth = AuthContext::new(rt.cfg.clone(), rt.cache_path.clone());
29    let graph = GraphClient::new(auth);
30
31    let mut items = Vec::new();
32    let mut next_token: Option<String> = page.map(String::from);
33    let mut source_label;
34    loop {
35        let res = sites::list(&graph, query, next_token.as_deref()).await?;
36        source_label = match res.source {
37            sites::SiteListSource::Followed => "followed",
38            sites::SiteListSource::Search => "search",
39        };
40        for s in res.items {
41            items.push(s);
42            if !all && items.len() >= limit {
43                break;
44            }
45        }
46        if !all || res.next.is_none() {
47            next_token = if all { None } else { res.next };
48            break;
49        }
50        next_token = res.next;
51    }
52
53    let total = items.len();
54    if rt.out.json {
55        let json_items: Vec<_> = items
56            .iter()
57            .map(|s| {
58                serde_json::json!({
59                    "id": s.id,
60                    "name": s.display_name,
61                    "url": s.web_url,
62                })
63            })
64            .collect();
65        rt.out.print_json(&serde_json::json!({
66            "total": total,
67            "next": next_token,
68            "source": source_label,
69            "items": json_items,
70        }));
71    } else {
72        for s in &items {
73            rt.out
74                .print_data(&format!("{:40}  {}", s.display_name, s.web_url));
75        }
76        rt.out
77            .print_message(&format!("({total} site(s), source={source_label})"));
78    }
79    Ok(())
80}
81
82async fn use_site(rt: &Runtime, value: &str) -> Result<()> {
83    if rt.cfg.read_only {
84        return Err(CliError::ReadOnly(
85            "sites use modifies the config file; not allowed in read-only mode".into(),
86        ));
87    }
88    let mut file = rt.config_file.clone();
89    let entry = file.profile.entry(rt.cfg.profile_name.clone()).or_default();
90    entry.default_site = Some(value.to_string());
91    config::save_file(&rt.config_path, &file)?;
92    rt.out.print_message(&format!(
93        "Set default_site for profile '{}' to '{}'",
94        rt.cfg.profile_name, value
95    ));
96    if rt.out.json {
97        rt.out.print_json(&serde_json::json!({
98            "profile": rt.cfg.profile_name,
99            "default_site": value,
100        }));
101    }
102    Ok(())
103}