repo_cli/
repository.rs

1use crate::{config::Config, Cache, Location, Remote, Tag};
2use anyhow::{anyhow, Context, Result};
3use serde::{Deserialize, Serialize};
4use std::{
5    collections::BTreeSet,
6    path::{Path, PathBuf},
7};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct Repository {
11    pub name: String,
12    pub path: Option<PathBuf>,
13    pub work: Option<String>,
14    pub clone: Option<String>,
15    pub use_cli: Option<bool>,
16
17    pub tags: BTreeSet<String>,
18    pub remotes: Vec<Remote>,
19
20    #[serde(skip)]
21    pub config: PathBuf,
22
23    #[serde(skip)]
24    pub location: Location,
25}
26
27pub struct RepositoryBuilder {
28    name: String,
29    remotes: Vec<Remote>,
30    tags: BTreeSet<String>,
31    location: Location,
32    path: Option<PathBuf>,
33    work: Option<String>,
34    clone: Option<String>,
35    use_cli: Option<bool>,
36}
37
38impl Repository {
39    pub fn resolve_workspace_path(&self, cache: &Cache) -> PathBuf {
40        self.path
41            .as_ref()
42            .map(|s| s.join(&self.name))
43            .or_else(|| {
44                self.resolve_from_tags(cache, |tag| tag.path.clone())
45                    .pop()
46                    .map(|p| p.join(&self.name))
47            })
48            .unwrap_or_else(|| PathBuf::from(&self.name))
49    }
50
51    pub fn path_from_location(location: Location) -> PathBuf {
52        match location {
53            Location::Global => Config::global_path().join("repository"),
54            Location::Local => Config::local_path().join("repository"),
55        }
56    }
57
58    pub fn resolve_from_tags<F, T>(&self, cache: &Cache, resolver: F) -> Vec<T>
59    where
60        F: Fn(&Tag) -> Option<T>,
61    {
62        let tags = cache.tags();
63        let mut priority: Vec<(T, i32)> = tags
64            .iter()
65            .filter(|t| self.tags.contains(&t.name))
66            .flat_map(|t| resolver(t).map(|value| (value, t.priority.unwrap_or(50))))
67            .collect();
68
69        priority.sort_by_key(|v| v.1);
70        priority.into_iter().map(|v| v.0).collect()
71    }
72
73    pub fn set_location(&mut self, location: Location) {
74        if self.location == location {
75            return;
76        }
77
78        self.location = location;
79        self.config = Repository::path_from_location(location).join(format!("{}.toml", self.name));
80    }
81
82    pub fn del_cache_file(&self) -> Result<()> {
83        std::fs::remove_file(&self.config)
84            .context(format!(
85                "failed to remove repository config file: {}",
86                &self.config.display()
87            ))
88            .map_err(Into::into)
89    }
90}
91
92impl RepositoryBuilder {
93    pub fn new(name: &str) -> Self {
94        Self {
95            name: name.to_owned(),
96            remotes: Vec::new(),
97            tags: BTreeSet::new(),
98            location: Location::default(),
99            use_cli: None,
100            path: None,
101            work: None,
102            clone: None,
103        }
104    }
105
106    pub fn remote(mut self, remote: Remote) -> Self {
107        self.remotes.push(remote);
108        self
109    }
110
111    pub fn tag(mut self, tag: String) -> Self {
112        self.tags.insert(tag);
113        self
114    }
115
116    pub fn location(mut self, location: Location) -> Self {
117        self.location = location;
118        self
119    }
120
121    pub fn path<P: AsRef<Path>>(mut self, path: P) -> Self {
122        self.path = Some(path.as_ref().to_path_buf());
123        self
124    }
125
126    pub fn cli(mut self, use_cli: bool) -> Self {
127        self.use_cli = Some(use_cli);
128        self
129    }
130
131    pub fn clone(mut self, command: String) -> Self {
132        self.clone = Some(command);
133        self
134    }
135
136    pub fn work(mut self, command: String) -> Self {
137        self.work = Some(command);
138        self
139    }
140
141    pub fn build(self) -> Repository {
142        let config =
143            Repository::path_from_location(self.location).join(format!("{}.toml", self.name));
144
145        Repository {
146            name: self.name,
147            remotes: self.remotes,
148            tags: self.tags,
149            path: self.path,
150            clone: self.clone,
151            work: self.work,
152            use_cli: self.use_cli,
153            location: self.location,
154            config,
155        }
156    }
157}
158
159#[cfg(test)]
160mod tests {
161    use super::*;
162    use crate::Query;
163    use url::Url;
164
165    #[test]
166    fn build() -> Result<()> {
167        let url: Url = "https://github.com/edeneast/repo".parse()?;
168        let remote = Remote::new(url);
169        let repo = RepositoryBuilder::new("repo")
170            .remote(remote.clone())
171            .build();
172
173        assert_eq!(repo.name, "repo");
174        assert_eq!(repo.remotes.len(), 1);
175        // assert_eq!(repo.remotes.first().unwrap(), remote);
176        Ok(())
177    }
178}