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}