1use crate::{config::Config, util, Location, Repository, Tag};
2use anyhow::{anyhow, Context, Result};
3use std::{
4 collections::HashMap,
5 io::Write,
6 path::{Path, PathBuf},
7};
8
9#[derive(Debug)]
10pub struct Cache {
11 data: CacheData,
12}
13
14#[derive(Debug)]
15pub struct CacheData {
16 repositories: HashMap<String, Repository>,
17 tags: HashMap<String, Tag>,
18}
19
20impl Cache {
21 pub fn new() -> Result<Self> {
22 trace!("Loading cache data");
23 Ok(Self {
24 data: CacheData::new()?,
25 })
26 }
27
28 pub fn add_repository(&mut self, repository: Repository) {
29 self.data
30 .repositories
31 .insert(repository.name.clone(), repository);
32 }
33
34 pub fn add_tag(&mut self, tag: Tag) {
35 self.data.tags.insert(tag.name.clone(), tag);
36 }
37
38 pub fn get_repository(&self, name: &str) -> Option<&Repository> {
39 self.data.repositories.get(name)
40 }
41
42 pub fn get_repository_mut(&mut self, name: &str) -> Option<&mut Repository> {
43 self.data.repositories.get_mut(name)
44 }
45
46 pub fn take_repository(&mut self, name: &str) -> Option<Repository> {
47 self.data.repositories.remove(name)
48 }
49
50 pub fn get_tag(&self, name: &str) -> Option<&Tag> {
51 self.data.tags.get(name)
52 }
53
54 pub fn get_tag_mut(&mut self, name: &str) -> Option<&mut Tag> {
55 self.data.tags.get_mut(name)
56 }
57
58 pub fn take_tag(&mut self, name: &str) -> Option<Tag> {
59 self.data.tags.remove(name)
60 }
61
62 pub fn has_repository(&self, name: &str) -> bool {
64 self.data.repositories.contains_key(name)
65 }
66
67 pub fn has_tag(&self, name: &str) -> bool {
68 self.data.tags.contains_key(name)
69 }
70
71 pub fn remove_repository(&mut self, name: &str) -> Result<()> {
72 match self.get_repository(name) {
73 Some(repo) => {
74 std::fs::remove_file(&repo.config).context(format!(
75 "failed to remove repository config file: {}",
76 &repo.config.display()
77 ))?;
78 self.data.repositories.remove(name);
79 Ok(())
80 }
81 None => Err(anyhow!("Repository: '{}' is not tracked by repo", name)),
82 }
83 }
84
85 pub fn remove_tag(&mut self, name: &str) -> Result<()> {
86 match self.get_tag(name) {
87 Some(tag) => std::fs::remove_file(&tag.config)
88 .context(format!(
89 "failed to remove tag config file: {:#?}",
90 &tag.config
91 ))
92 .map_err(Into::into),
93 None => Err(anyhow!("Tag: '{}' is not in repo", name)),
94 }
95 }
96
97 pub fn repositories(&self) -> Vec<&Repository> {
98 self.data.repositories.values().collect()
99 }
100
101 pub fn tags(&self) -> Vec<&Tag> {
102 self.data.tags.values().collect()
103 }
104}
105
106impl CacheData {
107 pub fn new() -> Result<Self> {
108 let paths = vec![
109 (Config::global_path(), Location::Global),
110 (Config::local_path(), Location::Local),
111 ];
112
113 let mut repositories = HashMap::new();
114 let mut tags = HashMap::new();
115
116 for (path, location) in paths {
117 let repo_path = PathBuf::from(&path).join("repository");
118 debug!("Checking if repository folder exists: {:#?}", repo_path);
119
120 if repo_path.is_dir() {
121 debug!("Repository folder exists");
122 let pattern = format!("{}/*.toml", repo_path.display());
123
124 for entry in glob::glob(&pattern).expect("failed repository glob") {
125 let file = match entry {
126 Ok(file) => file,
127 Err(e) => {
128 return Err(e).context("file is unreadable");
129 }
130 };
131
132 debug!("Loading Repository: {:#?}", file);
133 let content = util::read_content(&file)?;
134 let mut repository: Repository = toml::from_str(&content).context(format!(
135 "could not serialize content into Repository:\n\n{}",
136 content
137 ))?;
138
139 repository.config = file;
140 repository.location = location;
141
142 debug!("Inserting into cache: {}", repository.name);
143 repositories.insert(repository.name.clone(), repository);
144 }
145 } else {
146 debug!("Repository folder does not exists");
147 };
148
149 let tag_path = PathBuf::from(&path).join("tag");
150 debug!("Checking if tag folder exists: {:#?}", tag_path);
151
152 if tag_path.is_dir() {
153 debug!("Tag folder exists");
154 let pattern = format!("{}/*.toml", tag_path.display());
155
156 for entry in glob::glob(&pattern).expect("failed tag glob") {
157 let file = match entry {
158 Ok(file) => file,
159 Err(e) => {
160 return Err(e).context("file is unreadable");
161 }
162 };
163
164 debug!("Loading Tag: {:#?}", file);
165 let content = util::read_content(&file)?;
166 let mut tag: Tag = toml::from_str(&content).context(format!(
167 "could not serialize content into Tag:\n\n{}",
168 content
169 ))?;
170
171 tag.config = file;
172 tag.location = location;
173
174 debug!("Inserting into cache: {}", tag.name);
175 tags.insert(tag.name.clone(), tag);
176 }
177 } else {
178 debug!("Tag folder does not exists");
179 };
180 }
181 Ok(Self { repositories, tags })
182 }
183}