Skip to main content

yt/api/
tags.rs

1use serde::Deserialize;
2use serde_json::json;
3
4use crate::api::{Client, Projection};
5use crate::error::Result;
6use crate::fields::{choices, closest, same, usage};
7
8#[derive(Deserialize)]
9struct Tag {
10    name: String,
11}
12
13impl Projection for Tag {
14    const FIELDS: &'static str = "name";
15}
16
17/// Names of the tags the user can see.
18pub async fn visible(client: &Client) -> Result<Vec<String>> {
19    let tags: Vec<Tag> = client.get_all("tags").await?;
20    Ok(tags.into_iter().map(|tag| tag.name).collect())
21}
22
23/// Creates a tag owned by the user.
24pub async fn create(client: &Client, name: &str) -> Result<()> {
25    client.post_unit("tags", &json!({"name": name})).await
26}
27
28/// The instance's spelling of a tag name; `hint` ends the error for an unknown name.
29pub fn resolve(tags: &[String], name: &str, hint: &str) -> Result<String> {
30    if let Some(tag) = tags.iter().find(|tag| same(tag, name)) {
31        return Ok(tag.clone());
32    }
33    let mut message = format!("there is no tag '{name}'");
34    if let Some(close) = closest(name, tags.iter().map(String::as_str)) {
35        message.push_str(&format!(" (did you mean '{close}'?)"));
36    } else if !tags.is_empty() {
37        message.push_str(&format!("; tags: {}", choices(tags)));
38    }
39    message.push_str(&format!("; {hint}"));
40    Err(usage(message))
41}
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46
47    fn tags() -> Vec<String> {
48        vec!["Needs review".into(), "ui".into()]
49    }
50
51    #[test]
52    fn tags_take_the_instance_spelling() {
53        assert_eq!(
54            resolve(&tags(), "needs REVIEW", "").unwrap(),
55            "Needs review"
56        );
57    }
58
59    #[test]
60    fn unknown_tags_suggest_the_closest() {
61        let error = resolve(&tags(), "Neds review", "pass --no-validate to create it").unwrap_err();
62        assert!(
63            error.to_string().contains("did you mean 'Needs review'"),
64            "{error}"
65        );
66        assert!(error.to_string().contains("--no-validate"), "{error}");
67    }
68}