1use clap::{Args, Subcommand};
2use owo_colors::OwoColorize;
3
4use chub_core::team::pins;
5
6use crate::output;
7
8#[derive(Args)]
9pub struct PinArgs {
10 #[command(subcommand)]
11 command: PinCommand,
12}
13
14#[derive(Subcommand)]
15pub enum PinCommand {
16 Add(PinAddArgs),
18 Remove(PinRemoveArgs),
20 List,
22 Get(PinGetArgs),
24}
25
26#[derive(Args)]
27pub struct PinAddArgs {
28 id: String,
30 #[arg(long)]
32 lang: Option<String>,
33 #[arg(long)]
35 version: Option<String>,
36 #[arg(long)]
38 reason: Option<String>,
39 #[arg(long)]
41 source: Option<String>,
42}
43
44#[derive(Args)]
45pub struct PinRemoveArgs {
46 id: String,
48}
49
50#[derive(Args)]
51pub struct PinGetArgs {
52 #[arg(long)]
54 lang: Option<String>,
55}
56
57pub fn run(args: PinArgs, json: bool) {
58 match args.command {
59 PinCommand::Add(add_args) => run_add(add_args, json),
60 PinCommand::Remove(rm_args) => run_remove(rm_args, json),
61 PinCommand::List => run_list(json),
62 PinCommand::Get(_) => {
63 if json {
64 println!(
65 "{}",
66 serde_json::json!({"info": "Use `chub get --pinned` to fetch all pinned docs."})
67 );
68 } else {
69 eprintln!(
70 "Use {} to fetch all pinned docs.",
71 "chub get --pinned".bold()
72 );
73 }
74 }
75 }
76}
77
78fn run_add(args: PinAddArgs, json: bool) {
79 match pins::add_pin(
80 &args.id,
81 args.lang.clone(),
82 args.version.clone(),
83 args.reason.clone(),
84 args.source.clone(),
85 ) {
86 Ok(()) => {
87 if json {
88 println!(
89 "{}",
90 serde_json::json!({
91 "status": "pinned",
92 "id": args.id,
93 "lang": args.lang,
94 "version": args.version,
95 })
96 );
97 } else {
98 let mut msg = format!("Pinned {}", args.id.bold());
99 if let Some(ref lang) = args.lang {
100 msg.push_str(&format!(" ({})", lang));
101 }
102 if let Some(ref ver) = args.version {
103 msg.push_str(&format!(" v{}", ver));
104 }
105 output::success(&msg);
106 }
107 }
108 Err(e) => {
109 output::error(&e.to_string(), json);
110 std::process::exit(1);
111 }
112 }
113}
114
115fn run_remove(args: PinRemoveArgs, json: bool) {
116 match pins::remove_pin(&args.id) {
117 Ok(true) => {
118 if json {
119 println!(
120 "{}",
121 serde_json::json!({"status": "unpinned", "id": args.id})
122 );
123 } else {
124 output::success(&format!("Unpinned {}", args.id.bold()));
125 }
126 }
127 Ok(false) => {
128 if json {
129 println!(
130 "{}",
131 serde_json::json!({"status": "not_found", "id": args.id})
132 );
133 } else {
134 eprintln!("No pin found for {}.", args.id.bold());
135 }
136 }
137 Err(e) => {
138 output::error(&e.to_string(), json);
139 std::process::exit(1);
140 }
141 }
142}
143
144fn run_list(json: bool) {
145 let pins = pins::list_pins();
146
147 if json {
148 println!(
149 "{}",
150 serde_json::to_string_pretty(&serde_json::json!({
151 "pins": pins,
152 "total": pins.len(),
153 }))
154 .unwrap_or_default()
155 );
156 } else {
157 if pins.is_empty() {
158 eprintln!(
159 "{}",
160 "No pins configured. Use `chub pin add <id>` to pin a doc.".dimmed()
161 );
162 return;
163 }
164 eprintln!("{}", format!("{} pinned docs:\n", pins.len()).bold());
165 for pin in &pins {
166 let mut line = format!(" {}", pin.id.bold());
167 if let Some(ref lang) = pin.lang {
168 line.push_str(&format!(" {}", lang.cyan()));
169 }
170 if let Some(ref ver) = pin.version {
171 line.push_str(&format!(" v{}", ver));
172 }
173 if let Some(ref src) = pin.source {
174 line.push_str(&format!(" [{}]", src.dimmed()));
175 }
176 eprintln!("{}", line);
177 if let Some(ref reason) = pin.reason {
178 eprintln!(" {}", reason.dimmed());
179 }
180 }
181 }
182}