sophia_api/term/
bnode_id.rs1use super::*;
6use lazy_static::lazy_static;
7use regex::Regex;
8use sophia_iri::wrap;
9use std::borrow::Borrow;
10use std::fmt::Debug;
11use thiserror::Error;
12
13lazy_static! {
14 static ref BNODE_ID: Regex = Regex::new(r"(?x)
32 ^
33 [A-Za-z\u{c0}-\u{d6}\u{d8}-\u{f6}\u{f8}-\u{2ff}\u{370}-\u{37D}\u{37F}-\u{1FFF}\u{200C}-\u{200D}\u{2070}-\u{218F}\u{2C00}-\u{2FEF}\u{3001}-\u{D7FF}\u{F900}-\u{FDCF}\u{FDF0}-\u{FFFD}\u{10000}-\u{EFFFF}_0-9]
34 (
35 [A-Za-z\u{c0}-\u{d6}\u{d8}-\u{f6}\u{f8}-\u{2ff}\u{370}-\u{37D}\u{37F}-\u{1FFF}\u{200C}-\u{200D}\u{2070}-\u{218F}\u{2C00}-\u{2FEF}\u{3001}-\u{D7FF}\u{F900}-\u{FDCF}\u{FDF0}-\u{FFFD}\u{10000}-\u{EFFFF}_\u{2d}0-9\u{00B7}\u{0300}-\u{036F}\u{203F}-\u{2040}]
36 |
37 \u{2e} [A-Za-z\u{c0}-\u{d6}\u{d8}-\u{f6}\u{f8}-\u{2ff}\u{370}-\u{37D}\u{37F}-\u{1FFF}\u{200C}-\u{200D}\u{2070}-\u{218F}\u{2C00}-\u{2FEF}\u{3001}-\u{D7FF}\u{F900}-\u{FDCF}\u{FDF0}-\u{FFFD}\u{10000}-\u{EFFFF}_\u{2d}0-9\u{00B7}\u{0300}-\u{036F}\u{203F}-\u{2040}]
38 )*
39 $
40 ").unwrap();
41}
42
43wrap! { BnodeId borrowing str :
44 pub fn new(id: T) -> Result<Self, InvalidBnodeId> {
48 if BNODE_ID.is_match(id.borrow()) {
49 Ok(BnodeId(id))
50 } else {
51 Err(InvalidBnodeId(id.borrow().to_string()))
52 }
53 }
54}
55#[derive(Debug, Error)]
57#[error("The given blank node identifier '{0}' does not comply with Turtle's BLANK_NODE_LABEL")]
58pub struct InvalidBnodeId(pub String);
59
60impl<T> Term for BnodeId<T>
61where
62 T: Borrow<str>,
63{
64 type BorrowTerm<'x> = &'x Self where T: 'x;
65
66 fn kind(&self) -> TermKind {
67 TermKind::BlankNode
68 }
69 fn bnode_id(&self) -> Option<BnodeId<MownStr>> {
70 Some(self.as_ref().map_unchecked(MownStr::from_ref))
71 }
72 fn borrow_term(&self) -> Self::BorrowTerm<'_> {
73 self
74 }
75}
76
77#[cfg(test)]
78mod test {
79 use super::*;
80 use test_case::test_case;
81
82 #[test_case("x")]
83 #[test_case("_"; "underscore")]
84 #[test_case("foo_bar_baz")]
85 #[test_case("hé_hé")]
86 #[test_case("1")]
87 #[test_case("abc42")]
88 #[test_case("a.b"; "with dot")]
89 fn valid(tag: &str) {
90 assert!(BnodeId::new(tag).is_ok());
91 }
92
93 #[test_case(""; "empty")]
94 #[test_case(" "; "space")]
95 #[test_case("a."; "trailing dot")]
96 #[test_case(".b"; "leading dot")]
97 #[test_case("a,b"; "with comma")]
98 #[test_case("a:b"; "with colon")]
99 #[test_case("a b"; "with space")]
100 fn invalid(tag: &str) {
101 assert!(BnodeId::new(tag).is_err());
102 }
103}