typedb_driver/answer/concept_row.rs
1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20use std::{
21 fmt,
22 fmt::{Debug, Formatter},
23 sync::Arc,
24};
25
26use itertools::Itertools;
27
28use crate::{answer::QueryType, common::Result, concept::Concept, error::ConceptError};
29
30#[derive(Debug, PartialEq)]
31pub struct ConceptRowHeader {
32 pub column_names: Vec<String>,
33 pub query_type: QueryType,
34}
35
36impl ConceptRowHeader {
37 fn get_index(&self, name: &str) -> Option<usize> {
38 self.column_names.iter().find_position(|column_name| **column_name == name).map(|(pos, _)| pos)
39 }
40}
41
42/// A single row of concepts representing substitutions for variables in the query.
43/// Contains a Header (column names and query type), and the row of optional concepts.
44/// An empty concept in a column means the variable does not have a substitution in this answer.
45#[derive(Clone, PartialEq)]
46pub struct ConceptRow {
47 header: Arc<ConceptRowHeader>,
48 pub row: Vec<Option<Concept>>,
49}
50
51impl ConceptRow {
52 pub fn new(header: Arc<ConceptRowHeader>, row: Vec<Option<Concept>>) -> Self {
53 Self { header, row }
54 }
55
56 /// Retrieve the row column names (shared by all elements in this stream).
57 ///
58 /// # Examples
59 ///
60 /// ```rust
61 /// concept_row.get_column_names()
62 /// ```
63 pub fn get_column_names(&self) -> &[String] {
64 &self.header.column_names
65 }
66
67 /// Retrieve the executed query's type (shared by all elements in this stream).
68 ///
69 /// # Examples
70 ///
71 /// ```rust
72 /// concept_row.get_query_type()
73 /// ```
74 pub fn get_query_type(&self) -> QueryType {
75 self.header.query_type
76 }
77
78 /// Retrieves a concept for a given variable. Returns an empty optional if
79 /// the variable name has an empty answer. Returns an error if the variable name is not present.
80 ///
81 /// # Arguments
82 ///
83 /// * `var_name` — The variable name in the row to retrieve
84 ///
85 /// # Examples
86 ///
87 /// ```rust
88 /// concept_row.get(var_name)
89 /// ```
90 pub fn get(&self, column_name: &str) -> Result<Option<&Concept>> {
91 let index = self
92 .header
93 .get_index(column_name)
94 .ok_or(ConceptError::UnavailableRowVariable { variable: column_name.to_string() })?;
95 self.get_index(index)
96 }
97
98 /// Retrieves a concept for a given column index. Returns an empty optional if the index
99 /// points to an empty answer. Returns an error if the index is not in the row's range.
100 ///
101 /// # Arguments
102 ///
103 /// * `column_index` — The position in the row to retrieve
104 ///
105 /// # Examples
106 ///
107 /// ```rust
108 /// concept_row.get_position(column_index)
109 /// ```
110 pub fn get_index(&self, column_index: usize) -> Result<Option<&Concept>> {
111 let concept = self.row.get(column_index).ok_or(ConceptError::UnavailableRowIndex { index: column_index })?;
112 Ok(concept.as_ref())
113 }
114
115 /// Produces an iterator over all concepts in this `ConceptRow`, skipping empty results
116 ///
117 /// # Examples
118 ///
119 /// ```rust
120 /// concept_row.concepts()
121 /// ```
122 pub fn get_concepts(&self) -> impl Iterator<Item = &Concept> {
123 self.row.iter().filter_map(|concept| concept.as_ref())
124 }
125}
126
127impl fmt::Display for ConceptRow {
128 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
129 fmt::Debug::fmt(self, f)
130 }
131}
132
133impl fmt::Debug for ConceptRow {
134 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
135 write!(f, "|")?;
136 for (concept, name) in self.row.iter().zip(self.header.column_names.iter()) {
137 match concept {
138 None => write!(f, " ${}: empty ", name)?,
139 Some(concept) => write!(f, " ${}: {} |", name, concept)?,
140 }
141 }
142 Ok(())
143 }
144}