surwiki/src/wiki_lookup.rs

109 lines
3.3 KiB
Rust
Raw Normal View History

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
use article_redirect_resource::ArticleRedirectResource;
use article_resource::ArticleResource;
use assets::*;
use new_article_resource::NewArticleResource;
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 18:11:18 +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();
lookup_map.insert(
"/_new".to_string(),
Box::new(|state|
2017-10-02 18:11:18 +03:00
Box::new(NewArticleResource::new(state, None)) as BoxResource
) as ResourceFn
);
2017-09-15 18:28:23 +03:00
lookup_map.insert(
format!("/_assets/style-{}.css", StyleCss::checksum()),
2017-10-02 18:11:18 +03:00
Box::new(|_| 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 18:11:18 +03:00
Box::new(|_| 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 18:11:18 +03:00
Box::new(|_| 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>;
type Future = Box<Future<Item = Option<Self::Resource>, Error = Self::Error>>;
2017-09-15 18:28:23 +03:00
fn lookup(&self, path: &str, _query: Option<&str>, _fragment: Option<&str>) -> Self::Future {
assert!(path.starts_with("/"));
if path.starts_with("/_") {
// Reserved namespace
2017-09-17 16:43:31 +03:00
return Box::new(finished(
LOOKUP_MAP.get(path).map(|x| x(self.state.clone()))
));
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
}
// 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
)));
}
let state = self.state.clone();
use state::SlugLookup;
Box::new(self.state.lookup_slug(slug.clone())
.and_then(|x| Ok(Some(match x {
SlugLookup::Miss =>
Box::new(NewArticleResource::new(state, Some(slug))) as BoxResource,
SlugLookup::Hit { article_id, revision } =>
2017-10-01 23:35:06 +03:00
Box::new(ArticleResource::new(state, article_id, revision)) as BoxResource,
SlugLookup::Redirect(slug) =>
2017-10-01 23:35:06 +03:00
Box::new(ArticleRedirectResource::new(slug)) as BoxResource,
})))
)
2017-09-15 18:28:23 +03:00
}
}