wrut/backend/
tag.rs

1use crate::backend::utils::unregister;
2use crate::setup::{dir, Dirs};
3use crate::Type;
4use anyhow::Result;
5use std::os::unix::fs::symlink;
6use std::process::Command;
7
8pub struct Tag {
9    name: String,
10}
11
12impl Tag {
13    pub fn from(name: &str) -> Self {
14        Self {
15            name: name.to_string(),
16        }
17    }
18
19    /// Register a new tag and/or add projects/templates to it.
20    ///
21    /// If the provided tag does not exist, this function will create a new tag directory under
22    /// `~/.wrut/tags`. All entries in `templates` and `projects` will be added to their respective
23    /// directories.
24    pub fn init(&self, templates: &Vec<&str>, projects: &Vec<&str>) -> Result<()> {
25        let tag_data_dir = dir(Dirs::Tags)?;
26        let tag_dir = tag_data_dir.join(&self.name);
27        let tag_templates_dir = &tag_dir.join("templates");
28        let tag_projects_dir = &tag_dir.join("projects");
29
30        // create tag_dir and projects/templates subdirs if they don't exist
31        if !tag_dir.is_dir() {
32            std::fs::create_dir(&tag_dir)?;
33            std::fs::create_dir(&tag_templates_dir)?;
34            std::fs::create_dir(&tag_projects_dir)?;
35        }
36
37        // add templates/projects to appropriate dirs
38        // check if already exists, don't try to create if it does
39        let templates_dir = dir(Dirs::Templates)?;
40        for template in templates {
41            let template_path = &templates_dir.join(&template).canonicalize()?;
42            let tag_template_symlink = &tag_templates_dir.join(&template);
43            if !tag_template_symlink.is_symlink() {
44                symlink(template_path, tag_template_symlink)?;
45            }
46        }
47
48        let projects_dir = dir(Dirs::Projects)?;
49        for project in projects {
50            let project_path = &projects_dir.join(&project).canonicalize()?;
51            let tag_project_symlink = &tag_projects_dir.join(&project);
52            if !tag_project_symlink.is_symlink() {
53                symlink(project_path, tag_project_symlink)?;
54            }
55        }
56
57        Ok(())
58    }
59
60    // TODO use termtree instead of this hack
61    /// List the projects/templates of a given tag. If `tag` is `None`, list all tags and their
62    /// projects/templates.
63    pub fn list(tag: &Option<String>) -> Result<String> {
64        let tag_dir = if let Some(tag) = tag {
65            dir(Dirs::Tags)?.join(tag)
66        } else {
67            dir(Dirs::Tags)?
68        };
69
70        let output = Command::new("tree")
71            .arg(tag_dir.display().to_string())
72            .output()?;
73        Ok(std::str::from_utf8(&output.stdout)?.to_string())
74    }
75
76    pub fn remove(&self, templates: &Vec<&str>, projects: &Vec<&str>) -> Result<()> {
77        if templates.len() == 0 && projects.len() == 0 {
78            unregister(Type::Tag, &self.name)
79        } else {
80            let tag_templates_dir = dir(Dirs::Tags)?.join(&self.name).join("templates");
81            let tag_projects_dir = dir(Dirs::Tags)?.join(&self.name).join("projects");
82
83            for template in templates {
84                let template_link = tag_templates_dir.join(template);
85                if template_link.is_symlink() {
86                    std::fs::remove_file(template_link)?;
87                }
88            }
89
90            for project in projects {
91                let project_link = tag_projects_dir.join(project);
92                if project_link.is_symlink() {
93                    std::fs::remove_file(project_link)?;
94                }
95            }
96
97            Ok(())
98        }
99    }
100}