Skip to main content

clickup_cli/commands/
folder.rs

1use crate::client::ClickUpClient;
2use crate::commands::auth::resolve_token;
3use crate::error::CliError;
4use crate::output::OutputConfig;
5use crate::Cli;
6use clap::Subcommand;
7
8#[derive(Subcommand)]
9pub enum FolderCommands {
10    /// List folders in a space
11    List {
12        /// Space ID
13        #[arg(long)]
14        space: String,
15        /// Include archived
16        #[arg(long)]
17        archived: bool,
18    },
19    /// Get folder details
20    Get {
21        /// Folder ID
22        id: String,
23    },
24    /// Create a folder
25    Create {
26        /// Space ID
27        #[arg(long)]
28        space: String,
29        /// Folder name
30        #[arg(long)]
31        name: String,
32    },
33    /// Update a folder
34    Update {
35        /// Folder ID
36        id: String,
37        /// New name
38        #[arg(long)]
39        name: String,
40    },
41    /// Delete a folder
42    Delete {
43        /// Folder ID
44        id: String,
45    },
46}
47
48pub async fn execute(command: FolderCommands, cli: &Cli) -> Result<(), CliError> {
49    let token = resolve_token(cli)?;
50    let client = ClickUpClient::new(&token, cli.timeout)?;
51    let output = OutputConfig::from_cli(&cli.output, &cli.fields, cli.no_header, cli.quiet);
52
53    match command {
54        FolderCommands::List { space, archived } => {
55            let resp = client
56                .get(&format!("/v2/space/{}/folder?archived={}", space, archived))
57                .await?;
58            let folders = resp
59                .get("folders")
60                .and_then(|f| f.as_array())
61                .cloned()
62                .unwrap_or_default();
63            // Flatten: extract list_count from lists array length
64            let items: Vec<serde_json::Value> = folders
65                .iter()
66                .map(|f| {
67                    let list_count = f
68                        .get("lists")
69                        .and_then(|l| l.as_array())
70                        .map(|a| a.len())
71                        .unwrap_or(0);
72                    serde_json::json!({
73                        "id": f.get("id"),
74                        "name": f.get("name"),
75                        "task_count": f.get("task_count"),
76                        "list_count": list_count,
77                    })
78                })
79                .collect();
80            output.print_items(&items, &["id", "name", "task_count", "list_count"], "id");
81            Ok(())
82        }
83        FolderCommands::Get { id } => {
84            let resp = client.get(&format!("/v2/folder/{}", id)).await?;
85            let list_count = resp
86                .get("lists")
87                .and_then(|l| l.as_array())
88                .map(|a| a.len())
89                .unwrap_or(0);
90            let mut item = resp.clone();
91            item.as_object_mut()
92                .map(|o| o.insert("list_count".into(), serde_json::json!(list_count)));
93            output.print_single(&item, &["id", "name", "task_count", "list_count"], "id");
94            Ok(())
95        }
96        FolderCommands::Create { space, name } => {
97            let body = serde_json::json!({ "name": name });
98            let resp = client
99                .post(&format!("/v2/space/{}/folder", space), &body)
100                .await?;
101            output.print_single(&resp, &["id", "name"], "id");
102            Ok(())
103        }
104        FolderCommands::Update { id, name } => {
105            let body = serde_json::json!({ "name": name });
106            let resp = client.put(&format!("/v2/folder/{}", id), &body).await?;
107            output.print_single(&resp, &["id", "name"], "id");
108            Ok(())
109        }
110        FolderCommands::Delete { id } => {
111            client.delete(&format!("/v2/folder/{}", id)).await?;
112            output.print_message(&format!("Folder {} deleted", id));
113            Ok(())
114        }
115    }
116}