sophia_api/term/
var_name.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 VARNAME: Regex = Regex::new(r"(?x)
26 ^
27 [_A-Za-z0-9\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}]
28 [_A-Za-z0-9\u{B7}\u{C0}-\u{D6}\u{D8}-\u{F6}\u{F8}-\u{2FF}\u{300}-\u{37D}\u{37F}-\u{1FFF}\u{200C}-\u{200D}\u{203F}-\u{2040}\u{2070}-\u{218F}\u{2C00}-\u{2FEF}\u{3001}-\u{D7FF}\u{F900}-\u{FDCF}\u{FDF0}-\u{FFFD}\U{10000}-\U{EFFFF}]*
29 $
30 ").unwrap();
31}
32
33wrap! { VarName borrowing str :
34 pub fn new(name: T) -> Result<Self, InvalidVarName> {
37 if VARNAME.is_match(name.borrow()) {
38 Ok(VarName(name))
39 } else {
40 Err(InvalidVarName(name.borrow().to_string()))
41 }
42 }
43}
44#[derive(Debug, Error)]
46#[error("The given variable name '{0}' does not comply with SPARQL's VARNAME")]
47pub struct InvalidVarName(pub String);
48
49impl<T> Term for VarName<T>
50where
51 T: Borrow<str>,
52{
53 type BorrowTerm<'x> = &'x Self where T: 'x;
54
55 fn kind(&self) -> TermKind {
56 TermKind::Variable
57 }
58 fn variable(&self) -> Option<VarName<MownStr>> {
59 Some(self.as_ref().map_unchecked(MownStr::from_ref))
60 }
61 fn borrow_term(&self) -> Self::BorrowTerm<'_> {
62 self
63 }
64}
65
66#[cfg(test)]
67mod test {
68 use super::*;
69 use test_case::test_case;
70
71 #[test_case("x")]
72 #[test_case("foo_bar_baz")]
73 #[test_case("hé_hé")]
74 #[test_case("1")]
75 #[test_case("abc42")]
76 fn valid(tag: &str) {
77 assert!(VarName::new(tag).is_ok());
78 }
79
80 #[test_case(""; "empty")]
81 #[test_case(" "; "space")]
82 #[test_case("."; "dot")]
83 #[test_case("a.b"; "with dot")]
84 #[test_case("a,b"; "with comma")]
85 #[test_case("a b"; "with space")]
86 fn invalid(tag: &str) {
87 assert!(VarName::new(tag).is_err());
88 }
89}