Skip to main content

rudof_rdf/rdf_core/visualizer/
visual_rdf_node.rs

1use crate::rdf_core::vocabs::RdfVocab;
2use crate::rdf_core::{
3    NeighsRDF, RDFError, Rdf,
4    term::{Iri, IriOrBlankNode, Object},
5    visualizer::{NodeId, VisualRDFGraph, errors::RdfVisualizerError},
6};
7use std::fmt::Display;
8
9/// Represents a visual node in an RDF graph for visualization purposes.
10/// Each variant corresponds to different types of RDF terms or special constructs
11/// used in rendering RDF graphs.
12#[derive(Debug, Clone, PartialEq, Eq, Hash)]
13pub enum VisualRDFNode {
14    /// Special node representing the "reifies" predicate.
15    Reifies,
16    /// An IRI node with a qualified label and full URL.
17    Iri { label: String, url: String },
18    /// A blank node with a label.
19    BlankNode { label: String },
20    /// A literal value node.
21    Literal { value: String },
22    /// A predicate IRI node with a qualified label and full URL.
23    Predicate { label: String, url: String },
24    /// A non-asserted triple (subject, predicate, object) as a node.
25    NonAssertedTriple(Box<VisualRDFNode>, Box<VisualRDFNode>, Box<VisualRDFNode>),
26    /// An asserted triple (subject, predicate, object) as a node.
27    AssertedTriple(Box<VisualRDFNode>, Box<VisualRDFNode>, Box<VisualRDFNode>),
28}
29
30/// Implementation of methods for `VisualRDFNode`.
31impl VisualRDFNode {
32    /// Creates a `VisualRDFNode` from a predicate IRI.
33    /// If the predicate is the special "reifies" IRI, returns `Reifies`.
34    /// Otherwise, returns a `Predicate` with qualified label and URL.
35    ///
36    /// # Arguments
37    /// * `rdf` - The RDF data source
38    /// * `predicate` - The predicate IRI
39    ///
40    /// # Returns
41    /// * `VisualRDFNode` - The created visual node
42    pub fn from_predicate<R: Rdf>(rdf: &R, predicate: &R::IRI) -> VisualRDFNode {
43        if predicate.as_str() == RdfVocab::RDF_REIFIES {
44            VisualRDFNode::Reifies
45        } else {
46            let iri_label = rdf.qualify_iri(predicate);
47            let iri_str = predicate.to_string();
48            VisualRDFNode::Predicate {
49                label: iri_label,
50                url: iri_str,
51            }
52        }
53    }
54
55    /// Creates a `VisualRDFNode` from a subject term.
56    /// Converts the subject to a term and delegates to `term_to_visual_node`.
57    ///
58    /// # Arguments
59    /// * `rdf` - The RDF data source
60    /// * `subject` - The subject term
61    /// * `graph` - The visual graph
62    ///
63    /// # Returns
64    /// * `Result<Self, RDFError>` - The visual node or an error
65    pub fn from_subject<R: NeighsRDF>(
66        rdf: &R,
67        subject: &R::Subject,
68        graph: &mut VisualRDFGraph,
69    ) -> Result<Self, RDFError> {
70        let term = R::subject_as_term(subject);
71        term_to_visual_node(rdf, &term, graph)
72    }
73
74    /// Creates a `VisualRDFNode` from a general term.
75    /// Delegates to `term_to_visual_node`.
76    ///
77    /// # Arguments
78    /// * `rdf` - The RDF data source
79    /// * `term` - The term to convert
80    /// * `graph` - The visual graph
81    ///
82    /// # Returns
83    /// * `Result<Self, RDFError>` - The visual node or an error
84    pub fn from_term<R: NeighsRDF>(rdf: &R, term: &R::Term, graph: &mut VisualRDFGraph) -> Result<Self, RDFError> {
85        term_to_visual_node(rdf, term, graph)
86    }
87
88    /// Creates a `NonAssertedTriple` node from subject, predicate, and object nodes.
89    ///
90    /// # Arguments
91    /// * `s` - The subject node
92    /// * `p` - The predicate node
93    /// * `o` - The object node
94    ///
95    /// # Returns
96    /// * `Self` - The non-asserted triple node
97    pub fn non_asserted_triple(s: VisualRDFNode, p: VisualRDFNode, o: VisualRDFNode) -> Self {
98        VisualRDFNode::NonAssertedTriple(Box::new(s), Box::new(p), Box::new(o))
99    }
100
101    /// Creates an `AssertedTriple` node from subject, predicate, and object nodes.
102    ///
103    /// # Arguments
104    /// * `s` - The subject node
105    /// * `p` - The predicate node
106    /// * `o` - The object node
107    ///
108    /// # Returns
109    /// * `Self` - The asserted triple node
110    pub fn asserted_triple(s: VisualRDFNode, p: VisualRDFNode, o: VisualRDFNode) -> Self {
111        VisualRDFNode::AssertedTriple(Box::new(s), Box::new(p), Box::new(o))
112    }
113
114    /// Converts the node to a PlantUML string representation.
115    /// The `node_id` is used for unique identification in the diagram.
116    /// `show_if_predicate` controls whether predicate nodes are displayed.
117    /// Returns a string suitable for PlantUML rendering or an error.
118    ///
119    /// # Arguments
120    /// * `node_id` - The unique ID for the node
121    /// * `show_if_predicate` - Whether to show predicate nodes
122    /// * `_graph` - The visual graph (unused)
123    ///
124    /// # Returns
125    /// * `Result<String, RdfVisualizerError>` - The PlantUML string or an error
126    pub fn as_plantuml(
127        &self,
128        node_id: NodeId,
129        show_if_predicate: bool,
130        _graph: &VisualRDFGraph,
131    ) -> Result<String, RdfVisualizerError> {
132        match self {
133            VisualRDFNode::Iri { label, url } => Ok(format!("rectangle \"[[{url} {label}]]\" <<uri>> as {node_id}")),
134            VisualRDFNode::BlankNode { label: _ } => Ok(format!("rectangle \" \" <<bnode>> as {node_id}")),
135            VisualRDFNode::Literal { value } => Ok(format!("rectangle \"{value}\" <<literal>> as {node_id}")),
136            VisualRDFNode::NonAssertedTriple(_subj, _pred, _obj) => {
137                let mut str = String::new();
138                str.push_str(format!("cloud \" \" <<non_asserted>> as {node_id}\n").as_str());
139                Ok(str)
140            },
141            VisualRDFNode::AssertedTriple(_subj, _pred, _obj) => {
142                let mut str = String::new();
143                str.push_str(format!("rectangle \" \" <<asserted>> as {node_id}\n").as_str());
144                Ok(str)
145            },
146            VisualRDFNode::Predicate { label, url } => {
147                if show_if_predicate {
148                    Ok(format!("rectangle \"[[{url} {label}]]\" <<uri>> as {node_id}"))
149                } else {
150                    Ok(String::new())
151                }
152            },
153            VisualRDFNode::Reifies => {
154                if show_if_predicate {
155                    Ok(format!("rectangle \"reifies\" <<reifies>> as {node_id}"))
156                } else {
157                    Ok("".to_string())
158                }
159            },
160        }
161    }
162}
163
164/// Converts a term to a visual node.
165/// First converts the term to an object, then delegates to `object_to_visual_node`.
166///
167/// # Arguments
168/// * `rdf` - The RDF data source
169/// * `term` - The term to convert
170/// * `graph` - The visual graph
171///
172/// # Returns
173/// * `Result<VisualRDFNode, RDFError>` - The visual node or an error
174fn term_to_visual_node<R: NeighsRDF>(
175    rdf: &R,
176    term: &R::Term,
177    graph: &mut VisualRDFGraph,
178) -> Result<VisualRDFNode, RDFError> {
179    let object = R::term_as_object(term)?;
180    let object_node = object_to_visual_node(rdf, &object, graph)?;
181    Ok(object_node)
182}
183
184/// Converts an RDF object to a visual node.
185/// Matches on the object type to create the appropriate `VisualRDFNode` variant.
186///
187/// # Arguments
188/// * `rdf` - The RDF data source
189/// * `object` - The RDF object
190/// * `graph` - The visual graph
191///
192/// # Returns
193/// * `Result<VisualRDFNode, RDFError>` - The visual node or an error
194fn object_to_visual_node<R: NeighsRDF>(
195    rdf: &R,
196    object: &Object,
197    graph: &mut VisualRDFGraph,
198) -> Result<VisualRDFNode, RDFError> {
199    match object {
200        Object::Iri(iri_s) => {
201            let iri: R::IRI = iri_s.clone().into();
202            Ok(VisualRDFNode::Iri {
203                label: rdf.qualify_iri(&iri),
204                url: iri_s.as_str().to_string(),
205            })
206        },
207        Object::BlankNode(bnode) => Ok(VisualRDFNode::BlankNode {
208            label: bnode.to_string(),
209        }),
210        Object::Literal(literal) => Ok(VisualRDFNode::Literal {
211            value: literal.to_string(),
212        }),
213        Object::Triple {
214            subject,
215            predicate,
216            object,
217        } => {
218            // Convert the triple components to appropriate types
219            let sub: IriOrBlankNode = (**subject).clone();
220            let s: R::Subject = R::Subject::from(sub);
221            let p: R::IRI = R::IRI::from(predicate.clone());
222            let o: R::Term = R::Term::from((**object).clone());
223            // Create a triple term in the graph
224            let triple = graph.create_triple_term(rdf, s, p, o)?;
225            Ok(triple)
226        },
227    }
228}
229
230/// Implementation of `Display` for `VisualRDFNode`.
231/// Provides a human-readable string representation of the node.
232impl Display for VisualRDFNode {
233    /// Formats the visual node as a string.
234    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
235        match self {
236            VisualRDFNode::Iri { label, url } => write!(f, "Iri: {label} ({url})"),
237            VisualRDFNode::BlankNode { label } => {
238                write!(f, "BlankNode: {label}")
239            },
240            VisualRDFNode::Literal { value } => {
241                write!(f, "Literal: {value}")
242            },
243            VisualRDFNode::NonAssertedTriple(_, _, _) => write!(f, "NonAssertedTriple"),
244            VisualRDFNode::AssertedTriple(_, _, _) => write!(f, "AssertedTriple"),
245            VisualRDFNode::Predicate { label, url } => {
246                write!(f, "Predicate: {label} ({url})")
247            },
248            VisualRDFNode::Reifies => {
249                write!(f, "Reifies")
250            },
251        }
252    }
253}