1use std::str::FromStr;
2use url::Url;
3use scp::ScpPath;
4
5
6pub enum Query {
16 Url(Url),
17 Scp(ScpPath),
18 Path(String),
19}
20
21impl Query {
22 pub fn host(&self) -> Option<&str> {
24 match *self {
25 Query::Url(ref url) => url.host_str(),
26 Query::Scp(ref scp) => Some(scp.host()),
27 Query::Path(_) => None,
28 }
29 }
30
31 pub fn path(&self) -> &str {
32 match *self {
33 Query::Url(ref url) => url.path().trim_left_matches("/").trim_right_matches(".git"),
34 Query::Scp(ref scp) => scp.path(),
35 Query::Path(ref path) => path,
36 }
37 }
38}
39
40impl FromStr for Query {
41 type Err = ::Error;
42
43 fn from_str(s: &str) -> ::Result<Query> {
44 if let Ok(url) = Url::parse(s) {
45 match url.scheme() {
46 "http" | "https" | "ssh" | "git" => {}
47 scheme => return Err(format!("'{}' is invalid scheme", scheme).into()),
48 }
49 Ok(Query::Url(url))
50 } else if let Ok(scp) = ScpPath::from_str(s) {
51 Ok(Query::Scp(scp))
52 } else {
53 if s.starts_with("./") || s.starts_with("../") || s.starts_with(".\\") || s.starts_with("..\\") {
54 Err("The path must be not a relative path.")?;
55 }
56 Ok(Query::Path(s.to_owned()))
57 }
58 }
59}
60
61
62#[cfg(test)]
63mod tests_query {
64 use super::Query;
65
66 #[test]
67 fn https_url() {
68 let s = "https://github.com/peco/peco.git";
69
70 if let Ok(Query::Url(url)) = s.parse() {
71 assert_eq!(url.scheme(), "https");
72 assert_eq!(url.username(), "");
73 assert_eq!(url.password(), None);
74 assert_eq!(url.host_str(), Some("github.com"));
75 assert_eq!(url.path(), "/peco/peco.git");
76 } else {
77 panic!("does not matches");
78 }
79 }
80
81 #[test]
82 fn ssh_url() {
83 let s = "ssh://gituser@github.com:2222/peco/peco.git";
84
85 if let Ok(Query::Url(url)) = s.parse() {
86 assert_eq!(url.scheme(), "ssh");
87 assert_eq!(url.username(), "gituser");
88 assert_eq!(url.password(), None);
89 assert_eq!(url.host_str(), Some("github.com"));
90 assert_eq!(url.port(), Some(2222));
91 assert_eq!(url.path(), "/peco/peco.git");
92 } else {
93 panic!("does not matches");
94 }
95 }
96
97 #[test]
98 fn scp_pattern() {
99 let ss = &["git@github.com:peco/peco.git", "git@github.com:peco/peco"];
100 for s in ss {
101 if let Ok(Query::Scp(scp)) = s.parse() {
102 assert_eq!(scp.username(), "git");
103 assert_eq!(scp.host(), "github.com");
104 assert_eq!(scp.path(), "peco/peco");
105 } else {
106 panic!("does not matches");
107 }
108 }
109 }
110
111 #[test]
112 fn short_pattern_with_host() {
113 let s = "github.com/peco/peco";
114
115 if let Ok(Query::Path(path)) = s.parse() {
116 assert_eq!(path, "github.com/peco/peco");
117 } else {
118 panic!("does not matches")
119 }
120 }
121
122 #[test]
123 fn short_pattern_without_host() {
124 let s = "peco/peco";
125
126 if let Ok(Query::Path(path)) = s.parse() {
127 assert_eq!(path, "peco/peco");
128 } else {
129 panic!("does not matches")
130 }
131 }
132}
133
134
135#[cfg(test)]
136mod test_methods {
137 use super::Query;
138
139 #[test]
140 fn case_url() {
141 let url = "https://github.com/ubnt-intrepid/dot.git";
142 let query: Query = url.parse().unwrap();
143
144 assert_eq!(query.host(), Some("github.com"));
145 assert_eq!(query.path(), "ubnt-intrepid/dot");
146 }
147
148 #[test]
149 fn case_scp() {
150 let s = "git@github.com:ubnt-intrepid/dot.git";
151 let query: Query = s.parse().unwrap();
152
153 assert_eq!(query.host(), Some("github.com"));
154 assert_eq!(query.path(), "ubnt-intrepid/dot");
155 }
156
157 #[test]
158 fn case_relative() {
159 let s = "ubnt-intrepid/dot";
160 let query: Query = s.parse().unwrap();
161
162 assert_eq!(query.host(), None);
163 assert_eq!(query.path(), "ubnt-intrepid/dot");
164 }
165}