1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// Copyright (c) 2018-2023, agnos.ai UK Ltd, all rights reserved.
//---------------------------------------------------------------

use crate::{Literal, Namespace, RDFStoreError};

/// The `Class` struct represents an RDFS or OWL class identifier
/// consisting of a [`Namespace`] (i.e. a namespace) and a "local name".
#[derive(Debug, Clone)]
pub struct Class {
    pub namespace:  Namespace,
    pub local_name: String,
}

impl std::fmt::Display for Class {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{}{}",
            self.namespace.name.as_str(),
            self.local_name.as_str()
        )
    }
}

impl Class {
    pub fn declare(namespace: Namespace, local_name: &str) -> Self {
        Self { namespace, local_name: local_name.to_string() }
    }

    pub fn as_iri(&self) -> Result<iref::IriBuf, RDFStoreError> {
        let iri = iref::IriBuf::new(format!("{}{}", self.namespace.iri, self.local_name).as_str())?;
        Ok(iri)
    }

    #[allow(clippy::needless_lifetimes)]
    pub fn display_turtle<'a>(&'a self) -> impl std::fmt::Display + 'a {
        struct TurtleClass<'a>(&'a Class);
        impl<'a> std::fmt::Display for TurtleClass<'a> {
            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
                write!(
                    f,
                    "{}{}",
                    self.0.namespace.name, self.0.local_name
                )
            }
        }
        TurtleClass(self)
    }

    pub fn plural_label(&self) -> String { format!("{}s", self.local_name) }

    // TODO: Make this slightly smarter

    pub fn is_literal(&self, literal: &Literal) -> bool {
        if let Some(that_iri) = literal.as_iri() {
            if let Ok(this_iri) = self.as_iri() {
                that_iri == this_iri.as_iri()
            } else {
                let iri = self.to_string();
                literal.to_string() == iri
            }
        } else {
            false
        }
    }
}

#[cfg(test)]
mod tests {
    use {
        super::Namespace,
        crate::{class::Class, DataType, Literal},
    };

    #[test]
    fn test_a_class_01() {
        let namespace = Namespace::declare(
            "test:",
            iref::Iri::new("https://whatever.com/test#").unwrap(),
        );
        let class = Class::declare(namespace, "SomeClass");
        let s = format!("{:}", class);
        assert_eq!(s, "test:SomeClass")
    }

    #[test]
    fn test_a_class_02() {
        let namespace = Namespace::declare(
            "test:",
            iref::Iri::new("https://whatever.com/test#").unwrap(),
        );
        let class = Class::declare(namespace, "SomeClass");
        let s = format!("{}", class.as_iri().unwrap());
        assert_eq!(s, "https://whatever.com/test#SomeClass");
    }

    #[test]
    fn test_is_literal() {
        let namespace = Namespace::declare(
            "test:",
            iref::Iri::new("https://whatever.com/test#").unwrap(),
        );
        let class = Class::declare(namespace, "SomeClass");
        let literal = Literal::from_type_and_buffer(
            DataType::AnyUri,
            "https://whatever.com/test#SomeClass",
            None,
        )
        .unwrap();
        assert!(literal.is_some());
        assert_eq!(
            class.as_iri().unwrap().as_str(),
            "https://whatever.com/test#SomeClass"
        );
        assert!(class.is_literal(&literal.unwrap()))
    }
}