#![deny(clippy::all)]
#![cfg_attr(docs, feature(doc_cfg))]
#[cfg(feature = "parsing")]
#[cfg_attr(docs, doc(cfg(feature = "parsing")))]
mod codec;
mod display;
mod error;
mod interwiki_set;
#[cfg(feature = "parsing")]
#[cfg_attr(docs, doc(cfg(feature = "parsing")))]
mod ip;
#[cfg(feature = "parsing")]
#[cfg_attr(docs, doc(cfg(feature = "parsing")))]
mod ipv6;
mod namespace_map;
#[cfg(feature = "parsing")]
#[cfg_attr(docs, doc(cfg(feature = "parsing")))]
mod php;
mod site_info;
#[cfg(feature = "parsing")]
pub use codec::TitleCodec;
pub use display::TitleWhitespace;
pub use error::Error;
pub use interwiki_set::InterwikiSet;
pub use namespace_map::NamespaceMap;
pub use site_info::{
Interwiki, NamespaceAlias, NamespaceInfo, Response as SiteInfoResponse,
SiteInfo,
};
pub type Result<T, E = Error> = std::result::Result<T, E>;
const NS_MAIN: i32 = 0;
const NS_FILE: i32 = 6;
const NS_CATEGORY: i32 = 14;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Title {
namespace: i32,
dbkey: String,
fragment: Option<String>,
interwiki: Option<String>,
local_interwiki: bool,
}
impl Title {
#[inline]
fn to_sortable(&self) -> impl Ord + '_ {
let Title {
namespace,
dbkey,
fragment,
interwiki,
local_interwiki,
} = self;
(
interwiki.is_some(),
!local_interwiki,
interwiki.as_deref(),
*namespace,
dbkey,
fragment.as_deref(),
)
}
}
impl PartialOrd for Title {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.to_sortable().partial_cmp(&other.to_sortable())
}
}
impl Ord for Title {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.to_sortable().cmp(&other.to_sortable())
}
}
#[cfg(test)]
macro_rules! title {
(
$local_interwiki:literal : $interwiki:literal : $namespace:literal : $dbkey:literal
) => {{
Title {
local_interwiki: $local_interwiki,
interwiki: Some($interwiki.into()),
namespace: $namespace,
dbkey: $dbkey.into(),
fragment: Default::default(),
}
}};
(
$interwiki:literal : $namespace:literal : $dbkey:literal
) => {{
Title {
interwiki: Some($interwiki.into()),
namespace: $namespace,
dbkey: $dbkey.into(),
local_interwiki: Default::default(),
fragment: Default::default(),
}
}};
(
$namespace:literal : $dbkey:literal
) => {{
Title {
interwiki: None,
namespace: $namespace,
dbkey: $dbkey.into(),
local_interwiki: Default::default(),
fragment: Default::default(),
}
}};
}
#[test]
fn title_ord() {
let mut titles = vec![
title!(true:"localinterwiki2":4:"Title"),
title!(true:"localinterwiki1":4:"Title"),
title!("interwiki2":4:"Title"),
title!("interwiki1":4:"Title"),
title!(4:"Title"),
title!(0:"Title"),
];
titles.sort();
assert_eq!(
&titles,
&[
title!(0:"Title"),
title!(4:"Title"),
title!(true:"localinterwiki1":4:"Title"),
title!(true:"localinterwiki2":4:"Title"),
title!("interwiki1":4:"Title"),
title!("interwiki2":4:"Title"),
]
);
}
impl Title {
pub unsafe fn new_unchecked(namespace: i32, dbkey: String) -> Self {
Self {
namespace,
dbkey,
fragment: None,
interwiki: None,
local_interwiki: false,
}
}
pub fn with_fragment(mut self, fragment: String) -> Self {
self.fragment = Some(fragment);
self
}
pub fn remove_fragment(mut self) -> Self {
self.fragment = None;
self
}
pub fn namespace(&self) -> i32 {
self.namespace
}
pub fn dbkey(&self) -> &str {
&self.dbkey
}
pub fn fragment(&self) -> Option<&str> {
self.fragment.as_deref()
}
pub fn interwiki(&self) -> Option<&str> {
self.interwiki.as_deref()
}
pub fn is_local_interwiki(&self) -> bool {
self.local_interwiki
}
pub fn is_local_page(&self) -> bool {
self.interwiki.is_none()
&& !self.dbkey.is_empty()
&& self.namespace >= 0
}
pub fn is_file(&self) -> bool {
self.namespace == NS_FILE
}
pub fn is_category(&self) -> bool {
self.namespace == NS_CATEGORY
}
}