use ref_map::*;
use std::borrow::Cow;
use std::collections::HashMap;
use std::fmt::{self, Display};
#[derive(Serialize, Deserialize, Debug, Clone, Hash, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub struct PageRef<'t> {
site: Option<Cow<'t, str>>,
page: Cow<'t, str>,
}
impl<'t> PageRef<'t> {
#[inline]
pub fn new(site: Option<Cow<'t, str>>, page: Cow<'t, str>) -> Self {
PageRef { site, page }
}
#[inline]
pub fn page_and_site<S1, S2>(site: S1, page: S2) -> Self
where
S1: Into<Cow<'t, str>>,
S2: Into<Cow<'t, str>>,
{
let site = site.into();
let page = page.into();
PageRef::new(Some(site), page)
}
#[inline]
pub fn page_only<S>(page: S) -> Self
where
S: Into<Cow<'t, str>>,
{
let page = page.into();
PageRef::new(None, page)
}
#[inline]
pub fn site(&self) -> Option<&str> {
self.site.ref_map(|s| s.as_ref())
}
#[inline]
pub fn page(&self) -> &str {
self.page.as_ref()
}
pub fn parse(s: &'t str) -> Option<PageRef<'t>> {
let s = s.trim();
if s.is_empty() {
return None;
}
let result = match s.find(':') {
Some(0) => {
let idx = match s[1..].find(':') {
Some(idx) => idx + 1,
None => return None,
};
let site = &s[1..idx];
let page = &s[idx + 1..];
PageRef::page_and_site(site, page)
}
Some(_) => PageRef::page_only(s),
None => PageRef::page_only(s),
};
Some(result)
}
pub fn to_owned(&self) -> PageRef<'static> {
macro_rules! owned {
($value:expr) => {
Cow::Owned($value.as_ref().to_owned())
};
}
let site = self.site.ref_map(|value| owned!(value));
let page = owned!(&self.page);
PageRef { site, page }
}
}
impl Display for PageRef<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(site) = self.site() {
write!(f, ":{}:", &site)?;
}
write!(f, "{}", &self.page)
}
}
pub type IncludeVariables<'t> = HashMap<Cow<'t, str>, Cow<'t, str>>;
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub struct IncludeRef<'t> {
page_ref: PageRef<'t>,
variables: IncludeVariables<'t>,
}
impl<'t> IncludeRef<'t> {
#[inline]
pub fn new(page_ref: PageRef<'t>, variables: IncludeVariables<'t>) -> Self {
IncludeRef {
page_ref,
variables,
}
}
#[inline]
pub fn page_only(page_ref: PageRef<'t>) -> Self {
IncludeRef::new(page_ref, HashMap::new())
}
#[inline]
pub fn page_ref(&self) -> &PageRef<'t> {
&self.page_ref
}
#[inline]
pub fn variables(&self) -> &IncludeVariables<'t> {
&self.variables
}
}
impl<'t> From<IncludeRef<'t>> for (PageRef<'t>, IncludeVariables<'t>) {
#[inline]
fn from(include: IncludeRef<'t>) -> (PageRef<'t>, IncludeVariables<'t>) {
let IncludeRef {
page_ref,
variables,
} = include;
(page_ref, variables)
}
}
#[test]
fn page_ref() {
macro_rules! test {
($input:expr $(,)?) => {
test!($input => None)
};
($input:expr, $expected:expr $(,)?) => {
test!($input => Some($expected))
};
($input:expr => $expected:expr) => {{
let actual = PageRef::parse($input);
println!("Input: {:?}", $input);
println!("Output: {:?}", actual);
println!();
assert_eq!(actual, $expected, "Actual parse results don't match expected");
}};
}
test!("");
test!(":page");
test!("page", PageRef::page_only("page"));
test!("component:page", PageRef::page_only("component:page"));
test!(
"deleted:secret:fragment:page",
PageRef::page_only("deleted:secret:fragment:page"),
);
test!(":scp-wiki:page", PageRef::page_and_site("scp-wiki", "page"));
test!(
":scp-wiki:component:page",
PageRef::page_and_site("scp-wiki", "component:page"),
);
test!(
":scp-wiki:deleted:secret:fragment:page",
PageRef::page_and_site("scp-wiki", "deleted:secret:fragment:page"),
);
}