1use std::error;
2use std::fmt;
3use std::fs::File;
4use std::io;
5use std::io::Read;
6use std::path::{Path, PathBuf};
7
8use petgraph;
9use petgraph::graphmap::DiGraphMap;
10
11use bidir_map::BidirMap;
12
13use indexmap::IndexMap;
14
15use toml::de;
16
17use crate::PackageMeta;
18use crate::Repo;
19
20#[derive(Debug)]
24pub enum DatabaseError {
25 Io(io::Error),
26 Toml(de::Error),
27 Cycle(String),
28}
29
30impl fmt::Display for DatabaseError {
31 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32 match *self {
33 DatabaseError::Io(ref err) => write!(f, "IO error: {}", err),
34 DatabaseError::Toml(ref err) => write!(f, "TOML parsing error: {}", err),
35 DatabaseError::Cycle(ref err) => write!(f, "Cyclic dependency: {}", err),
36 }
37 }
38}
39
40impl error::Error for DatabaseError {}
41
42impl From<io::Error> for DatabaseError {
43 fn from(err: io::Error) -> DatabaseError {
44 DatabaseError::Io(err)
45 }
46}
47
48impl From<de::Error> for DatabaseError {
49 fn from(err: de::Error) -> DatabaseError {
50 DatabaseError::Toml(err)
51 }
52}
53
54#[derive(Debug)]
55pub enum PackageDepends {
56 Directory(PathBuf),
57 Repository(Repo),
58}
59
60impl PackageDepends {
61 pub fn get_depends(&self, pkg_name: &str) -> Result<Vec<String>, DatabaseError> {
64 match *self {
65 PackageDepends::Directory(ref pathbuf) => {
66 let path = pathbuf.as_path().join(format!("{}.toml", pkg_name));
67
68 let mut input = String::new();
69 File::open(path.as_path().to_str().unwrap())
70 .and_then(|mut f| f.read_to_string(&mut input))?;
71
72 Ok(PackageMeta::from_toml(&input)?.depends)
73 }
74 PackageDepends::Repository(ref repo) => Ok(repo.fetch_meta(pkg_name)?.depends),
75 }
76 }
77}
78
79#[derive(Debug)]
84pub struct Database {
85 installed_path: PathBuf,
88
89 pkgdepends: PackageDepends,
92}
93
94impl Database {
99 pub fn open<P: AsRef<Path>>(installed_path: P, pkgdepends: PackageDepends) -> Self {
101 Database {
102 installed_path: installed_path.as_ref().to_path_buf(),
103 pkgdepends: pkgdepends,
104 }
105 }
106
107 pub fn is_pkg_installed(&self, pkg_name: &str) -> bool {
109 let pkg_path_buf = self
110 .installed_path
111 .as_path()
112 .join(format!("{}.toml", pkg_name));
113 let installed = pkg_path_buf.as_path().exists();
114 installed
115 }
116
117 pub fn get_pkg_depends(&self, pkg_name: &str) -> Result<Vec<String>, DatabaseError> {
120 self.pkgdepends.get_depends(pkg_name)
121 }
122
123 pub fn calculate_depends(
126 &self,
127 pkg_name: &str,
128 ordered_dependencies: &mut IndexMap<String, ()>,
129 ) -> Result<(), DatabaseError> {
130 let mut graph = DiGraphMap::new();
131
132 let mut map = BidirMap::new();
135
136 map.insert(pkg_name.to_string(), 0);
137
138 self.calculate_depends_rec(pkg_name, &mut map, &mut graph)?;
139
140 let dependency_ids = petgraph::algo::toposort(&graph, None).or_else(|err| {
142 Err(DatabaseError::Cycle(
146 map.get_by_second(&err.node_id()).unwrap().to_string(),
147 ))
148 })?;
149
150 for i in dependency_ids {
151 if !ordered_dependencies.contains_key(map.get_by_second(&i).unwrap()) {
152 if let Some((name, _)) = map.remove_by_second(&i) {
153 ordered_dependencies.insert(name, ());
154 }
155 }
156 }
157
158 Ok(())
159 }
160
161 fn calculate_depends_rec(
163 &self,
164 pkg_name: &str,
165 map: &mut BidirMap<String, usize>,
166 graph: &mut DiGraphMap<usize, u8>,
167 ) -> Result<(), DatabaseError> {
168 let curr_node = *map.get_by_first(pkg_name).unwrap();
169
170 let mut depends = self.get_pkg_depends(pkg_name)?;
171
172 if depends.len() == 0 {
173 return Ok(());
174 }
175
176 while !depends.is_empty() {
178 let index = depends.len() - 1;
179 let dependency = depends.remove(index);
180
181 if !self.is_pkg_installed(&dependency) {
183 if !map.contains_first_key(&dependency) {
186 let dependency_node = map.len();
187 graph.add_node(dependency_node);
188 map.insert(dependency, dependency_node);
189
190 graph.add_edge(dependency_node, curr_node, 0);
191 let dependency_name = map.get_mut_by_second(&dependency_node).unwrap().clone();
192 self.calculate_depends_rec(&dependency_name, map, graph)?;
193 } else {
194 let dependency_node = *map.get_by_first(&dependency).unwrap();
197 graph.add_edge(dependency_node, curr_node, 0);
198 }
199 }
200 }
201
202 Ok(())
203 }
204}