wdl_cli/analysis/
source.rs1use std::path::Path;
4use std::path::PathBuf;
5
6use anyhow::Result;
7use anyhow::anyhow;
8use anyhow::bail;
9use path_clean::clean;
10use url::Url;
11use wdl_analysis::Analyzer;
12use wdl_engine::path::parse_url;
13
14#[derive(Clone, Debug)]
16pub enum Source {
17 Remote(Url),
19
20 File(Url),
22
23 Directory(PathBuf),
25}
26
27impl Source {
28 pub fn as_url(&self) -> Option<&Url> {
30 match self {
31 Source::Remote(url) | Source::File(url) => Some(url),
32 Source::Directory(_) => None,
33 }
34 }
35
36 pub async fn register<T: Send + Clone + 'static>(
38 self,
39 analyzer: &mut Analyzer<T>,
40 ) -> Result<()> {
41 match self {
42 Source::Remote(url) | Source::File(url) => analyzer.add_document(url).await,
43 Source::Directory(path) => analyzer.add_directory(path).await,
44 }
45 }
46}
47
48impl std::fmt::Display for Source {
49 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50 match self {
51 Source::Remote(url) => write!(f, "{url}"),
52 Source::File(url) => write!(f, "{url}"),
53 Source::Directory(path) => write!(f, "{path}", path = path.display()),
54 }
55 }
56}
57
58impl std::str::FromStr for Source {
59 type Err = anyhow::Error;
60
61 fn from_str(s: &str) -> Result<Self, Self::Err> {
62 if let Some(url) = parse_url(s) {
63 return Ok(Self::Remote(url));
64 }
65
66 let path = Path::new(s);
67
68 let path = std::path::absolute(path)
69 .map_err(|_| anyhow!("failed to convert `{path}` to a URI", path = path.display()))
70 .map(|path| clean(&path))?;
71
72 if !path.exists() {
73 bail!("source file `{s}` does not exist");
74 }
75
76 if path.is_dir() {
77 return Ok(Source::Directory(path));
78 } else if path.is_file() {
79 if let Ok(url) = Url::from_file_path(&path) {
80 return Ok(Source::File(url));
81 }
82 }
83
84 bail!("failed to convert `{s}` to a URI")
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn file() {
94 let file = tempfile::NamedTempFile::new().unwrap();
95 let path = std::path::absolute(file.path()).unwrap();
96
97 let source = path.to_str().unwrap().parse::<Source>().unwrap();
98 assert!(matches!(source, Source::File(_)));
99 let url = source.as_url().unwrap();
100 assert_eq!(url.scheme(), "file");
101 assert_eq!(url.to_file_path().unwrap(), path);
102 }
103
104 #[test]
105 fn directory() {
106 let dir = tempfile::TempDir::new().unwrap();
107 let name = dir.path().as_os_str().to_str().unwrap();
108
109 assert!(matches!(name.parse().unwrap(),
110 Source::Directory(path)
111 if path.as_os_str().to_str().unwrap() == name));
112 }
113
114 #[test]
115 fn url() {
116 const EXAMPLE: &str = "https://example.com/";
117 assert!(matches!(EXAMPLE.parse().unwrap(),
118 Source::Remote(url)
119 if url.as_str()
120 == EXAMPLE
121 ));
122 }
123
124 #[test]
125 fn missing_file() {
126 let err = "a-random-file-that-doesnt-exist.txt"
127 .parse::<Source>()
128 .unwrap_err();
129
130 assert_eq!(
131 err.to_string(),
132 "source file `a-random-file-that-doesnt-exist.txt` does not exist"
133 );
134 }
135
136 #[test]
137 fn invalid_source() {
138 let err = "".parse::<Source>().unwrap_err();
139
140 assert_eq!(err.to_string(), "failed to convert `` to a URI");
141 }
142}