1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
// externals
use filesystem;
use git2::Repository;
use semver::Version;
use std::path;
// locals
use super::package_manager::{identify_dir, PackageManager};
pub struct Project<'a> {
directory: path::PathBuf,
repository: git2::Repository,
package_manager: &'a PackageManager,
}
impl Project<'_> {
pub fn path(&self) -> &path::PathBuf {
&self.directory
}
pub fn language_name(&self) -> &'static str {
self.package_manager.language_name()
}
pub fn bump_major(&self) -> Result<semver::Version, String> {
// get the current version
let mut next_version = self.current_version().unwrap();
// increment it by one major version
next_version.increment_major();
match self.package_manager.major(&self.repository, &next_version) {
Ok(_) => (),
Err(msg) => return Err(msg),
};
// return it to the caller
return Ok(next_version);
}
pub fn bump_minor(&self) -> Result<semver::Version, String> {
// get the current version
let mut next_version = self.current_version().unwrap();
// increment it by one minor version
next_version.increment_minor();
match self.package_manager.minor(&self.repository, &next_version) {
Ok(_) => (),
Err(msg) => return Err(msg),
};
// return it to the caller
return Ok(next_version);
}
pub fn bump_patch(&self) -> Result<semver::Version, String> {
// get the current version
let mut next_version = self.current_version().unwrap();
// increment it by one patch version
next_version.increment_patch();
match self.package_manager.patch(&self.repository, &next_version) {
Ok(_) => (),
Err(msg) => return Err(msg),
};
// return it to the caller
return Ok(next_version);
}
fn current_version(&self) -> Result<semver::Version, &'static str> {
// get the list of tags for the current repository
let current_repo = match Repository::open(self.path()) {
Err(_) => return Err("Should get this."),
Ok(repo) => repo,
};
// grab the tags from the repo
let tags = match current_repo.tag_names(None) {
Err(_) => return Err("Error looking up tags"),
Ok(strings) => strings,
};
// if we dont have any versions
if tags.len() == 0 {
// then we're at v0.0.0
match Version::parse("0.0.0") {
Ok(version) => return Ok(version),
Err(_) => return Err("Shouldn't get here"),
}
} else {
// we want to sort the tags in descending order by semantic version
let mut ordered_tags = tags
.iter()
// turn the string array into a list of semver compliant tags
.fold(Vec::new(), |mut acc, tag_name| {
match tag_name {
// if we have a valid tag parse it as semver
Some(tag) => {
let name = if tag.to_string().chars().nth(0).unwrap() == 'v' {
&tag[1..]
} else {
tag
};
match Version::parse(name) {
// the tag is valid semver
Ok(version) => {
// add the parsed version to the list
acc.push(version);
acc
}
// if its not a valid semver tag then don't include it
Err(_) => acc,
}
}
// if the tag is not utf-8 then dont include it
None => acc,
}
});
// make sure we get the highest version first
ordered_tags.sort_by(|a, b| b.cmp(a));
// the first entry in the ordered list of tags is the current semantic version
return match ordered_tags.get(0) {
Some(tag) => Ok(tag.clone()),
None => Err("hello"),
};
}
}
}
// getters
pub fn find<'a>(fs: impl filesystem::FileSystem) -> Result<Project<'a>, &'static str> {
// find the directory with the git repo
let dir = match find_dir(&fs) {
Err(msg) => return Err(msg),
Ok(d) => d,
};
// and the repository associated with it
let repo = match Repository::open(&dir) {
Err(_) => return Err("Directory was not a git repo??"),
Ok(r) => r,
};
// grab the appropriate manager
let manager = identify_dir(&fs, &dir);
// return a new instance of the project
Ok(Project {
repository: repo,
directory: dir,
package_manager: manager,
})
}
fn find_dir(fs: &impl filesystem::FileSystem) -> Result<path::PathBuf, &'static str> {
// find the current directory
let cwd = match fs.current_dir() {
Err(_) => return Err("Could not find current working dir"),
Ok(dir) => dir,
};
// look for the directory
return find_dir_walk(&cwd);
}
fn find_dir_walk(dir: &path::PathBuf) -> Result<path::PathBuf, &'static str> {
// if we have a directory called .git here
if dir.join(".git").is_dir() {
// we found the repo, we're done
return Ok(dir.clone());
}
// grab the parent directory
let parent = match dir.parent() {
None => return Err("Could not find git repo"),
Some(par) => par,
};
// check if the parent is the repo
return find_dir_walk(&parent.to_path_buf());
}