singularity_cli/commands/
tag.rs1use anyhow::Result;
2use clap::Subcommand;
3use tabled::Table;
4
5use crate::client::ApiClient;
6use crate::models::tag::{Tag, TagCreate, TagListResponse, TagUpdate};
7
8#[derive(Subcommand)]
9pub enum TagCmd {
10 #[command(about = "List all tags")]
11 List {
12 #[arg(long, help = "Filter by parent tag ID")]
13 parent: Option<String>,
14 #[arg(long, help = "Maximum number of tags to return (max 1000)")]
15 max_count: Option<u32>,
16 #[arg(long, help = "Number of tags to skip for pagination")]
17 offset: Option<u32>,
18 #[arg(long, help = "Include soft-deleted tags")]
19 include_removed: bool,
20 },
21 #[command(about = "Get a single tag by ID")]
22 Get {
23 #[arg(help = "Tag ID")]
24 id: String,
25 },
26 #[command(about = "Create a new tag")]
27 Create {
28 #[arg(long, help = "Tag title (required)")]
29 title: String,
30 #[arg(long, help = "Parent tag ID for nesting")]
31 parent: Option<String>,
32 #[arg(long, help = "Display order")]
33 order: Option<f64>,
34 },
35 #[command(about = "Update an existing tag (only specified fields are changed)")]
36 Update {
37 #[arg(help = "Tag ID to update")]
38 id: String,
39 #[arg(long, help = "New tag title")]
40 title: Option<String>,
41 #[arg(long, help = "New parent tag ID")]
42 parent: Option<String>,
43 #[arg(long, help = "New display order")]
44 order: Option<f64>,
45 },
46 #[command(about = "Delete a tag by ID (soft-delete)")]
47 Delete {
48 #[arg(help = "Tag ID to delete")]
49 id: String,
50 },
51}
52
53pub fn run(client: &ApiClient, cmd: TagCmd, json: bool) -> Result<()> {
54 match cmd {
55 TagCmd::List {
56 parent,
57 max_count,
58 offset,
59 include_removed,
60 } => {
61 let mut query: Vec<(&str, String)> = Vec::new();
62 if let Some(ref v) = parent {
63 query.push(("parent", v.to_string()));
64 }
65 if let Some(v) = max_count {
66 query.push(("maxCount", v.to_string()));
67 }
68 if let Some(v) = offset {
69 query.push(("offset", v.to_string()));
70 }
71 if include_removed {
72 query.push(("includeRemoved", "true".to_string()));
73 }
74
75 if json {
76 let resp: serde_json::Value = client.get("/v2/tag", &query)?;
77 println!("{}", serde_json::to_string_pretty(&resp)?);
78 } else {
79 let resp: TagListResponse = client.get("/v2/tag", &query)?;
80 if resp.tags.is_empty() {
81 println!("No tags found.");
82 } else {
83 println!("{}", Table::new(&resp.tags));
84 }
85 }
86 }
87 TagCmd::Get { id } => {
88 if json {
89 let resp: serde_json::Value = client.get(&format!("/v2/tag/{}", id), &[])?;
90 println!("{}", serde_json::to_string_pretty(&resp)?);
91 } else {
92 let tag: Tag = client.get(&format!("/v2/tag/{}", id), &[])?;
93 tag.display_detail();
94 }
95 }
96 TagCmd::Create {
97 title,
98 parent,
99 order,
100 } => {
101 let data = TagCreate {
102 title,
103 parent,
104 order,
105 };
106 if json {
107 let resp: serde_json::Value = client.post("/v2/tag", &data)?;
108 println!("{}", serde_json::to_string_pretty(&resp)?);
109 } else {
110 let tag: Tag = client.post("/v2/tag", &data)?;
111 println!("Created tag {}", tag.id);
112 }
113 }
114 TagCmd::Update {
115 id,
116 title,
117 parent,
118 order,
119 } => {
120 let data = TagUpdate {
121 title,
122 parent,
123 order,
124 };
125 if json {
126 let resp: serde_json::Value = client.patch(&format!("/v2/tag/{}", id), &data)?;
127 println!("{}", serde_json::to_string_pretty(&resp)?);
128 } else {
129 let tag: Tag = client.patch(&format!("/v2/tag/{}", id), &data)?;
130 println!("Updated tag {}", tag.id);
131 }
132 }
133 TagCmd::Delete { id } => {
134 client.delete(&format!("/v2/tag/{}", id))?;
135 println!("Deleted tag {}", id);
136 }
137 }
138 Ok(())
139}