142 lines
4.2 KiB
Rust
Executable file
142 lines
4.2 KiB
Rust
Executable file
//! Various ID types used by Wikidata.
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
use std::{fmt, num::ParseIntError, str::FromStr};
|
|
|
|
pub mod consts;
|
|
|
|
/// An error parsing an ID.
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub enum IdParseError {
|
|
/// The number couldn't be parsed.
|
|
UnparseableNumber(ParseIntError),
|
|
/// The ID had an invalid prefix letter.
|
|
InvalidPrefix,
|
|
}
|
|
|
|
macro_rules! id_def {
|
|
($name:ident, $full_name:expr, $letter:expr, $khar:expr) => {
|
|
#[derive(
|
|
Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize,
|
|
)]
|
|
#[doc = "A Wikidata"]
|
|
#[doc = $full_name]
|
|
pub struct $name(pub u64);
|
|
|
|
impl $name {
|
|
/// Get the URL to access data about the claim on Wikidata.
|
|
#[must_use]
|
|
pub fn json_url(&self) -> String {
|
|
format!(
|
|
concat!(
|
|
"https://www.wikidata.org/wiki/Special:EntityData/",
|
|
$letter,
|
|
"{}.json"
|
|
),
|
|
self.0
|
|
)
|
|
}
|
|
}
|
|
impl FromStr for $name {
|
|
type Err = IdParseError;
|
|
|
|
/// Parse the identifier from a string.
|
|
fn from_str(x: &str) -> Result<Self, Self::Err> {
|
|
if x.chars().next() != Some($khar) {
|
|
return Err(IdParseError::InvalidPrefix);
|
|
}
|
|
let num_str = &x[1..];
|
|
match num_str.parse() {
|
|
Ok(num) => Ok(Self(num)),
|
|
Err(e) => Err(IdParseError::UnparseableNumber(e)),
|
|
}
|
|
}
|
|
}
|
|
impl fmt::Display for $name {
|
|
/// Display the ID as it would be in a URI.
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, concat!($letter, "{}"), self.0)
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
id_def!(Qid, "entity ID", "Q", 'Q');
|
|
id_def!(Pid, "property ID", "P", 'P');
|
|
id_def!(Lid, "lexeme ID", "L", 'L');
|
|
|
|
/// A lexeme ID and associated form ID
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
|
|
pub struct Fid(pub Lid, pub u16);
|
|
|
|
/// A lexeme ID and assoicated sense ID
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
|
|
pub struct Sid(pub Lid, pub u16);
|
|
|
|
impl fmt::Display for Sid {
|
|
/// Display the ID as it would be in a URI.
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "{}-S{}", self.0, self.1)
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Fid {
|
|
/// Display the ID as it would be in a URI.
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "{}-F{}", self.0, self.1)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
pub mod test {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn json_url() {
|
|
assert_eq!(
|
|
Qid(42).json_url(),
|
|
"https://www.wikidata.org/wiki/Special:EntityData/Q42.json"
|
|
);
|
|
assert_eq!(
|
|
Pid(31).json_url(),
|
|
"https://www.wikidata.org/wiki/Special:EntityData/P31.json"
|
|
);
|
|
assert_eq!(
|
|
Lid(1).json_url(),
|
|
"https://www.wikidata.org/wiki/Special:EntityData/L1.json"
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn to_string() {
|
|
let entity = Qid(42);
|
|
assert_eq!(format!("{}", entity), "Q42");
|
|
|
|
let prop = Pid(6);
|
|
assert_eq!(format!("{}", prop), "P6");
|
|
|
|
let lexeme = Lid(2);
|
|
assert_eq!(format!("{}", lexeme), "L2");
|
|
|
|
let sense = Sid(Lid(5), 9);
|
|
assert_eq!(format!("{}", sense), "L5-S9");
|
|
|
|
let form = Fid(Lid(3), 11);
|
|
assert_eq!(format!("{}", form), "L3-F11");
|
|
}
|
|
|
|
#[test]
|
|
fn from_str() {
|
|
assert_eq!(Qid::from_str("Q42").unwrap(), Qid(42));
|
|
assert_eq!(Lid::from_str("L944114").unwrap(), Lid(944114));
|
|
assert_eq!(Pid::from_str("P1341").unwrap(), Pid(1341));
|
|
assert_eq!(Pid::from_str("Q1341"), Err(IdParseError::InvalidPrefix));
|
|
assert_eq!(Pid::from_str("1341"), Err(IdParseError::InvalidPrefix));
|
|
}
|
|
|
|
#[test]
|
|
fn unit_suffix() {
|
|
assert_eq!(consts::unit_suffix(consts::METRE).unwrap(), " m");
|
|
assert_eq!(consts::unit_suffix(consts::DEGREE).unwrap(), "°");
|
|
}
|
|
}
|