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