use std::{
fmt,
fmt::{Debug, Formatter},
sync::Arc,
};
use itertools::Itertools;
use crate::{
analyze::{conjunction::ConjunctionID, pipeline::Pipeline},
answer::QueryType,
common::Result,
concept::Concept,
error::ConceptError,
};
#[derive(Debug)]
pub struct ConceptRowHeader {
pub column_names: Vec<String>,
pub query_type: QueryType,
pub query_structure: Option<Pipeline>,
}
impl ConceptRowHeader {
fn get_index(&self, name: &str) -> Option<usize> {
self.column_names.iter().find_position(|column_name| **column_name == name).map(|(pos, _)| pos)
}
}
#[derive(Clone)]
pub struct ConceptRow {
header: Arc<ConceptRowHeader>,
pub row: Vec<Option<Concept>>,
involved_conjunctions: Option<Vec<u8>>,
}
impl ConceptRow {
pub fn new(
header: Arc<ConceptRowHeader>,
row: Vec<Option<Concept>>,
involved_conjunctions: Option<Vec<u8>>,
) -> Self {
Self { header, involved_conjunctions, row }
}
pub fn get_column_names(&self) -> &[String] {
&self.header.column_names
}
pub fn get_query_type(&self) -> QueryType {
self.header.query_type
}
pub fn get_query_structure(&self) -> Option<&Pipeline> {
self.header.query_structure.as_ref()
}
pub fn get_involved_conjunctions(&self) -> Option<impl Iterator<Item = ConjunctionID> + '_> {
self.involved_conjunctions.as_ref().map(|involved| {
(0..(involved.len() * 8))
.filter(|conjunction| {
let index = conjunction / 8;
let mask = 1 << (conjunction % 8);
involved[index] & mask != 0
})
.map(ConjunctionID)
})
}
pub fn get_involved_conjunctions_cloned(&self) -> Option<impl Iterator<Item = ConjunctionID> + 'static> {
let cloned = self.involved_conjunctions.clone();
cloned.map(|involved| {
(0..(involved.len() * 8))
.filter(move |conjunction| {
let index = conjunction / 8;
let mask = 1 << (conjunction % 8);
involved[index] & mask != 0
})
.map(ConjunctionID)
})
}
pub fn get(&self, column_name: &str) -> Result<Option<&Concept>> {
let index = self
.header
.get_index(column_name)
.ok_or(ConceptError::UnavailableRowVariable { variable: column_name.to_string() })?;
self.get_index(index)
}
pub fn get_index(&self, column_index: usize) -> Result<Option<&Concept>> {
let concept = self.row.get(column_index).ok_or(ConceptError::UnavailableRowIndex { index: column_index })?;
Ok(concept.as_ref())
}
pub fn get_concepts(&self) -> impl Iterator<Item = &Concept> {
self.row.iter().filter_map(|concept| concept.as_ref())
}
}
impl PartialEq for ConceptRow {
fn eq(&self, other: &Self) -> bool {
self.row.eq(&other.row) && self.involved_conjunctions.eq(&other.involved_conjunctions)
}
}
impl fmt::Display for ConceptRow {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
impl fmt::Debug for ConceptRow {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "|")?;
for (concept, name) in self.row.iter().zip(self.header.column_names.iter()) {
match concept {
None => write!(f, " ${}: empty ", name)?,
Some(concept) => write!(f, " ${}: {} |", name, concept)?,
}
}
Ok(())
}
}