dars_catalog/
lib.rs

1use lazy_static::lazy_static;
2use rust_embed::RustEmbed;
3use std::sync::Arc;
4use tera::Tera;
5use warp::Filter;
6
7mod filters;
8mod handlers;
9
10#[derive(RustEmbed)]
11#[folder = "src/templates/"]
12struct Templates;
13
14/// Builds a catalog with root-url `url`. The handlers for this filter takes list of datasets.
15pub fn catalog<T: Catalog + Clone>(
16    root: String,
17    catalog: T,
18) -> Result<impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone, anyhow::Error>
19{
20    lazy_static! {
21        static ref TERA: Arc<Tera> = {
22            let mut tera = Tera::default();
23            for t in &["base.html", "folder.html", "index.html"] {
24                let template = Templates::get(&t).unwrap();
25                let template = std::str::from_utf8(&template).unwrap();
26                tera.add_raw_template(&t, &template).unwrap();
27            }
28            Arc::new(tera)
29        };
30    }
31
32    Ok(filters::catalog(root, Arc::clone(&TERA), catalog))
33}
34
35pub trait Catalog: Send + Sync {
36    /// List of all paths to data sources.
37    fn paths<'a>(&'a self) -> Box<dyn Iterator<Item = &str> + 'a>;
38}
39
40impl<T: Catalog> Catalog for Arc<T> {
41    fn paths<'a>(&'a self) -> Box<dyn Iterator<Item = &str> + 'a> {
42        T::paths(self)
43    }
44}
45
46#[cfg(test)]
47pub(crate) mod tests {
48    use super::*;
49    use futures::executor::block_on;
50
51    #[derive(Debug)]
52    pub struct TestCatalog {
53        paths: Vec<String>,
54    }
55
56    impl TestCatalog {
57        pub fn test() -> Arc<TestCatalog> {
58            Arc::new(TestCatalog {
59                paths: [
60                    "coads1.nc",
61                    "coads2.nc",
62                    "path1/hula.nc",
63                    "path1/hula2.nc",
64                    "path1/sub/hula3.nc",
65                    "path2/bula.nc",
66                ]
67                .iter()
68                .map(|s| s.to_string())
69                .collect(),
70            })
71        }
72    }
73
74    impl Catalog for Arc<TestCatalog> {
75        fn paths<'a>(&'a self) -> Box<dyn Iterator<Item = &str> + 'a> {
76            Box::new(self.paths.iter().map(|s| s.as_str()))
77        }
78    }
79
80    #[test]
81    fn setup_catalog() {
82        catalog("http://localhost:8001".into(), TestCatalog::test()).unwrap();
83    }
84
85    #[test]
86    fn does_not_match_data_source() {
87        let f = catalog("http://localhost:8001".into(), TestCatalog::test()).unwrap();
88
89        assert_eq!(
90            block_on(
91                warp::test::request()
92                    .method("GET")
93                    .path("/data/coads1.nc")
94                    .reply(&f)
95            )
96            .status(),
97            404
98        );
99
100        assert_eq!(
101            block_on(
102                warp::test::request()
103                    .method("GET")
104                    .path("/data/path1/hula.nc")
105                    .reply(&f)
106            )
107            .status(),
108            404
109        );
110
111        assert_eq!(
112            block_on(
113                warp::test::request()
114                    .method("GET")
115                    .path("/data/path1/non-exist.nc")
116                    .reply(&f)
117            )
118            .status(),
119            404
120        );
121    }
122
123    #[test]
124    fn matches_root() {
125        let f = catalog("http://localhost:8001".into(), TestCatalog::test()).unwrap();
126
127        assert_eq!(
128            block_on(warp::test::request().method("GET").path("/data/").reply(&f)).status(),
129            200
130        );
131
132        assert_eq!(
133            block_on(warp::test::request().method("GET").path("/data").reply(&f)).status(),
134            200
135        );
136    }
137
138    #[test]
139    fn matches_subpath() {
140        let f = catalog("http://localhost:8001".into(), TestCatalog::test()).unwrap();
141
142        assert_eq!(
143            block_on(
144                warp::test::request()
145                    .method("GET")
146                    .path("/data/path1/")
147                    .reply(&f)
148            )
149            .status(),
150            200
151        );
152
153        assert_eq!(
154            block_on(
155                warp::test::request()
156                    .method("GET")
157                    .path("/data/path1")
158                    .reply(&f)
159            )
160            .status(),
161            200
162        );
163    }
164
165    #[test]
166    fn does_not_match_missing_subpath() {
167        let f = catalog("http://localhost:8001".into(), TestCatalog::test()).unwrap();
168
169        assert_eq!(
170            block_on(
171                warp::test::request()
172                    .method("GET")
173                    .path("/data/missing_path1/")
174                    .reply(&f)
175            )
176            .status(),
177            404
178        );
179    }
180}