1use std::ffi::OsStr;
4use std::fmt::Display;
5use std::path::{Path, PathBuf};
6
7use remote::Remote;
8use util::{self, process};
9use vcs::Vcs;
10
11
12#[derive(Clone, Debug, Serialize, Deserialize)]
14pub struct Repository {
15 name: String,
17 path: PathBuf,
19 vcs: Vcs,
21 #[serde(skip_serializing_if = "Option::is_none")]
23 remote: Option<Remote>,
24}
25
26impl Repository {
27 pub fn new<P: AsRef<Path>, R: Into<Option<Remote>>>(path: P, vcs: Vcs, remote: R) -> ::Result<Self> {
29 let path = util::canonicalize_pretty(path)?;
30 let name = path.file_name()
31 .map(|s| s.to_string_lossy().into_owned())
32 .ok_or("cannot determine repository name")?;
33 Ok(Repository {
34 name,
35 path,
36 vcs,
37 remote: remote.into(),
38 })
39 }
40
41 pub fn refresh(self) -> Option<Self> {
43 match self.vcs.get_remote_url(&self.path) {
44 Ok(url) => Self::new(self.path, self.vcs, url.map(Remote::new)).ok(),
45 _ => None,
46 }
47 }
48
49 pub fn is_same_local(&self, other: &Self) -> bool {
50 self.path.as_path() == other.path.as_path()
51 }
52
53 pub fn is_contained<P: AsRef<Path>>(&self, path: P) -> bool {
54 self.path.starts_with(path)
55 }
56
57 pub fn run_command<I, S>(&self, command: &str, args: I) -> ::Result<bool>
59 where
60 I: IntoIterator<Item = S>,
61 S: AsRef<OsStr> + Display,
62 {
63 let output = process::inherit(command)
64 .args(args)
65 .current_dir(&self.path)
66 .output()?;
67 Ok(output.status.success())
68 }
69
70 pub fn name(&self) -> &str {
71 &self.name
72 }
73
74 pub fn path_string(&self) -> String {
75 format!("{}", self.path.display())
76 }
77
78 pub fn remote(&self) -> Option<&Remote> {
79 self.remote.as_ref()
80 }
81}