2017-09-15 18:28:23 +03:00
|
|
|
use std::collections::HashMap;
|
|
|
|
|
2017-10-02 00:24:16 +03:00
|
|
|
use futures::{Future, finished, failed};
|
|
|
|
use percent_encoding::percent_decode;
|
2017-09-15 18:28:23 +03:00
|
|
|
|
2017-10-13 16:21:23 +03:00
|
|
|
use resources::*;
|
2017-10-02 00:26:57 +03:00
|
|
|
use assets::*;
|
2017-09-15 18:28:23 +03:00
|
|
|
use state::State;
|
|
|
|
use web::{Lookup, Resource};
|
|
|
|
|
2017-10-01 23:35:06 +03:00
|
|
|
type BoxResource = Box<Resource + Sync + Send>;
|
2017-10-02 22:47:21 +03:00
|
|
|
type ResourceFn = Box<Fn(&State) -> BoxResource + Sync + Send>;
|
2017-10-01 23:35:06 +03:00
|
|
|
|
2017-09-15 18:28:23 +03:00
|
|
|
lazy_static! {
|
2017-10-02 18:11:18 +03:00
|
|
|
static ref LOOKUP_MAP: HashMap<String, ResourceFn> = {
|
2017-09-15 18:28:23 +03:00
|
|
|
let mut lookup_map = HashMap::new();
|
|
|
|
|
2017-10-03 11:37:18 +03:00
|
|
|
lookup_map.insert(
|
|
|
|
"/_changes".to_string(),
|
|
|
|
Box::new(|state: &State|
|
|
|
|
// TODO Use query arguments to fill in the `before` parameter below
|
|
|
|
Box::new(ChangesResource::new(state.clone(), None)) as BoxResource
|
|
|
|
) as ResourceFn
|
|
|
|
);
|
|
|
|
|
2017-10-13 16:06:16 +03:00
|
|
|
lookup_map.insert(
|
|
|
|
"/_sitemap".to_string(),
|
|
|
|
Box::new(|state: &State|
|
|
|
|
Box::new(SitemapResource::new(state.clone())) as BoxResource
|
|
|
|
) as ResourceFn
|
|
|
|
);
|
|
|
|
|
2017-10-02 18:08:59 +03:00
|
|
|
lookup_map.insert(
|
|
|
|
"/_new".to_string(),
|
2017-10-02 22:47:21 +03:00
|
|
|
Box::new(|state: &State|
|
|
|
|
Box::new(NewArticleResource::new(state.clone(), None)) as BoxResource
|
2017-10-02 18:11:18 +03:00
|
|
|
) as ResourceFn
|
2017-10-02 18:08:59 +03:00
|
|
|
);
|
|
|
|
|
2017-09-15 18:28:23 +03:00
|
|
|
lookup_map.insert(
|
|
|
|
format!("/_assets/style-{}.css", StyleCss::checksum()),
|
2017-10-02 22:47:21 +03:00
|
|
|
Box::new(|_: &State| Box::new(StyleCss) as BoxResource) as ResourceFn
|
2017-09-15 18:28:23 +03:00
|
|
|
);
|
|
|
|
|
|
|
|
lookup_map.insert(
|
|
|
|
format!("/_assets/script-{}.js", ScriptJs::checksum()),
|
2017-10-02 22:47:21 +03:00
|
|
|
Box::new(|_: &State| Box::new(ScriptJs) as BoxResource) as ResourceFn
|
2017-09-15 18:28:23 +03:00
|
|
|
);
|
|
|
|
|
|
|
|
lookup_map.insert(
|
|
|
|
format!("/_assets/amatic-sc-v9-latin-regular.woff"),
|
2017-10-02 22:47:21 +03:00
|
|
|
Box::new(|_: &State| Box::new(AmaticFont) as BoxResource) as ResourceFn
|
2017-09-15 18:28:23 +03:00
|
|
|
);
|
|
|
|
|
|
|
|
lookup_map
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct WikiLookup {
|
|
|
|
state: State
|
|
|
|
}
|
|
|
|
|
|
|
|
impl WikiLookup {
|
|
|
|
pub fn new(state: State) -> WikiLookup {
|
|
|
|
WikiLookup { state }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Lookup for WikiLookup {
|
2017-10-01 23:35:06 +03:00
|
|
|
type Resource = BoxResource;
|
2017-09-15 18:28:23 +03:00
|
|
|
type Error = Box<::std::error::Error + Send + Sync>;
|
2017-09-17 13:08:42 +03:00
|
|
|
type Future = Box<Future<Item = Option<Self::Resource>, Error = Self::Error>>;
|
2017-09-15 18:28:23 +03:00
|
|
|
|
2017-10-02 22:35:52 +03:00
|
|
|
fn lookup(&self, path: &str, query: Option<&str>) -> Self::Future {
|
2017-09-15 18:28:23 +03:00
|
|
|
assert!(path.starts_with("/"));
|
|
|
|
|
|
|
|
if path.starts_with("/_") {
|
|
|
|
// Reserved namespace
|
|
|
|
|
2017-09-17 16:43:31 +03:00
|
|
|
return Box::new(finished(
|
2017-10-02 22:47:21 +03:00
|
|
|
LOOKUP_MAP.get(path).map(|x| x(&self.state))
|
2017-09-17 12:45:35 +03:00
|
|
|
));
|
2017-09-15 18:28:23 +03:00
|
|
|
}
|
|
|
|
|
2017-09-17 12:27:50 +03:00
|
|
|
let mut split = path[1..].split('/');
|
|
|
|
|
2017-10-02 00:24:16 +03:00
|
|
|
let slug = split.next().expect("Always at least one element");
|
|
|
|
let slug = match percent_decode(slug.as_bytes()).decode_utf8() {
|
|
|
|
Ok(x) => x,
|
|
|
|
Err(x) => return Box::new(failed(x.into()))
|
|
|
|
}.to_string();
|
2017-09-17 12:27:50 +03:00
|
|
|
|
|
|
|
if split.next() != None {
|
|
|
|
// Currently disallow any URLs of the form /slug/...
|
2017-09-17 16:43:31 +03:00
|
|
|
return Box::new(finished(None));
|
2017-09-17 12:27:50 +03:00
|
|
|
}
|
|
|
|
|
2017-09-21 11:58:54 +03:00
|
|
|
// Normalize all user-generated slugs:
|
|
|
|
let slugified_slug = ::slug::slugify(&slug);
|
|
|
|
if slugified_slug != slug {
|
|
|
|
return Box::new(finished(Some(
|
2017-10-01 23:35:06 +03:00
|
|
|
Box::new(ArticleRedirectResource::new(slugified_slug)) as BoxResource
|
2017-09-21 11:58:54 +03:00
|
|
|
)));
|
|
|
|
}
|
|
|
|
|
2017-09-20 12:54:26 +03:00
|
|
|
let state = self.state.clone();
|
2017-10-02 22:30:31 +03:00
|
|
|
let edit = query == Some("edit");
|
2017-09-20 12:54:26 +03:00
|
|
|
|
|
|
|
use state::SlugLookup;
|
2017-09-21 11:58:54 +03:00
|
|
|
Box::new(self.state.lookup_slug(slug.clone())
|
2017-10-02 22:30:31 +03:00
|
|
|
.and_then(move |x| Ok(Some(match x {
|
2017-09-21 11:58:54 +03:00
|
|
|
SlugLookup::Miss =>
|
2017-10-02 18:08:59 +03:00
|
|
|
Box::new(NewArticleResource::new(state, Some(slug))) as BoxResource,
|
2017-09-20 12:54:26 +03:00
|
|
|
SlugLookup::Hit { article_id, revision } =>
|
2017-10-02 22:30:31 +03:00
|
|
|
Box::new(ArticleResource::new(state, article_id, revision, edit)) as BoxResource,
|
2017-09-20 12:54:26 +03:00
|
|
|
SlugLookup::Redirect(slug) =>
|
2017-10-01 23:35:06 +03:00
|
|
|
Box::new(ArticleRedirectResource::new(slug)) as BoxResource,
|
2017-09-20 12:54:26 +03:00
|
|
|
})))
|
2017-09-21 11:58:54 +03:00
|
|
|
)
|
2017-09-15 18:28:23 +03:00
|
|
|
}
|
|
|
|
}
|