doiget_cli/commands/
tag.rs1use std::io::Write;
13
14use anyhow::{bail, Context, Result};
15
16use doiget_core::store::{FsStore, Store};
17
18use super::output::OutputMode;
19use super::resolve_store_root;
20
21#[allow(clippy::too_many_arguments)]
29pub fn run(
30 ref_str: String,
31 add: Vec<String>,
32 remove: Vec<String>,
33 collection_add: Vec<String>,
34 collection_remove: Vec<String>,
35 list: bool,
36 mode: OutputMode,
37 quiet_was_explicit: bool,
38) -> Result<()> {
39 let ref_ = super::parse_ref_or_exit(&ref_str)?;
40 let safekey = ref_.safekey();
41
42 let store_root = resolve_store_root()?;
43 let store = FsStore::new(store_root)?;
44
45 let mut metadata = store
46 .read(&safekey)
47 .with_context(|| format!("failed to read store entry for {ref_str}"))?
48 .with_context(|| {
49 format!("no store entry for {ref_str}; run `doiget fetch {ref_str}` first")
50 })?;
51
52 {
53 let ext = metadata.doiget.as_mut().with_context(|| {
54 format!(
55 "entry {ref_str} has no [doiget] table; \
56 run `doiget fetch {ref_str}` first"
57 )
58 })?;
59
60 if list {
61 if mode == OutputMode::Quiet && quiet_was_explicit {
62 return Ok(());
63 }
64 let stdout = std::io::stdout();
65 let mut out = stdout.lock();
66 if mode == OutputMode::Json {
67 let v = serde_json::json!({
68 "ref": ref_str,
69 "tags": &ext.tags,
70 "collections": &ext.collections,
71 "annotation": &ext.annotation,
72 });
73 let s = serde_json::to_string_pretty(&v)
74 .context("failed to serialize tag info to JSON")?;
75 writeln!(out, "{s}").context("failed to write tag info JSON to stdout")?;
76 } else {
77 let tags_str = if ext.tags.is_empty() {
78 "-".to_string()
79 } else {
80 ext.tags.join(", ")
81 };
82 let cols_str = if ext.collections.is_empty() {
83 "-".to_string()
84 } else {
85 ext.collections.join(", ")
86 };
87 let ann_str = ext.annotation.as_deref().unwrap_or("-");
88 writeln!(out, "tags: {tags_str}").context("stdout write")?;
89 writeln!(out, "collections: {cols_str}").context("stdout write")?;
90 writeln!(out, "annotation: {ann_str}").context("stdout write")?;
91 }
92 return Ok(());
93 }
94
95 if add.is_empty()
96 && remove.is_empty()
97 && collection_add.is_empty()
98 && collection_remove.is_empty()
99 {
100 bail!(
101 "no action specified; provide <tag>... to add, \
102 or use --remove / --collection / --list"
103 );
104 }
105
106 for t in &add {
107 if !ext.tags.contains(t) {
108 ext.tags.push(t.clone());
109 }
110 }
111 for t in &remove {
112 ext.tags.retain(|x| x != t);
113 }
114 for c in &collection_add {
115 if !ext.collections.contains(c) {
116 ext.collections.push(c.clone());
117 }
118 }
119 for c in &collection_remove {
120 ext.collections.retain(|x| x != c);
121 }
122 }
123
124 store
125 .write_user_authored(&safekey, &metadata, None)
126 .with_context(|| format!("failed to write updated metadata for {ref_str}"))?;
127
128 Ok(())
129}
130
131pub fn run_annotate(ref_str: String, text: Option<String>, clear: bool) -> Result<()> {
134 if !clear && text.is_none() {
135 super::output::print_err(format_args!("error: provide annotation <text> or --clear"));
139 return Err(anyhow::Error::new(super::fetch::CliExit(2)));
140 }
141
142 let ref_ = super::parse_ref_or_exit(&ref_str)?;
143 let safekey = ref_.safekey();
144
145 let store_root = resolve_store_root()?;
146 let store = FsStore::new(store_root)?;
147
148 let mut metadata = store
149 .read(&safekey)
150 .with_context(|| format!("failed to read store entry for {ref_str}"))?
151 .with_context(|| {
152 format!("no store entry for {ref_str}; run `doiget fetch {ref_str}` first")
153 })?;
154
155 {
156 let ext = metadata.doiget.as_mut().with_context(|| {
157 format!(
158 "entry {ref_str} has no [doiget] table; \
159 run `doiget fetch {ref_str}` first"
160 )
161 })?;
162
163 if clear {
164 ext.annotation = None;
165 } else if let Some(t) = text {
166 if t.is_empty() {
167 bail!(
168 "annotation text must not be empty; \
169 use --clear to remove the annotation"
170 );
171 }
172 ext.annotation = Some(t);
173 }
174 }
175
176 store
177 .write_user_authored(&safekey, &metadata, None)
178 .with_context(|| format!("failed to write updated metadata for {ref_str}"))?;
179
180 Ok(())
181}