use alloc::string::String;
use alloc::string::ToString;
use alloc::vec::Vec;
use core::iter::Iterator;
use core::option::Option;
use core::result::Result as CoreResult;
use crate::{types::TypedValue, DataType, RemDbError};
pub struct ResultSet {
pub columns: Vec<String>,
pub rows: Vec<ResultRow>,
current_row: usize,
}
impl ResultSet {
pub fn new(columns: Vec<String>) -> Self {
ResultSet {
columns,
rows: Vec::new(),
current_row: 0,
}
}
pub fn add_row(&mut self, values: Vec<TypedValue>) {
self.rows.push(ResultRow::new(values));
}
pub fn column_count(&self) -> usize {
self.columns.len()
}
pub fn row_count(&self) -> usize {
self.rows.len()
}
pub fn columns(&self) -> &Vec<String> {
&self.columns
}
pub fn get_row(&self, index: usize) -> Option<&ResultRow> {
self.rows.get(index)
}
pub fn get_row_mut(&mut self, index: usize) -> Option<&mut ResultRow> {
self.rows.get_mut(index)
}
pub fn reset_iterator(&mut self) {
self.current_row = 0;
}
pub fn next_row(&mut self) -> Option<&ResultRow> {
if self.current_row < self.rows.len() {
let row = &self.rows[self.current_row];
self.current_row += 1;
Some(row)
} else {
None
}
}
pub fn iter(&self) -> ResultRowIter<'_> {
ResultRowIter {
result_set: self,
current: 0,
}
}
pub fn to_string(&self) -> String {
if self.rows.is_empty() {
return "Empty result set".to_string();
}
let mut result = String::new();
for (i, column) in self.columns.iter().enumerate() {
if i > 0 {
result.push_str(", ");
}
result.push_str(column);
}
result.push('\n');
for (i, _) in self.columns.iter().enumerate() {
if i > 0 {
result.push_str("--+");
}
result.push_str("----");
}
result.push('\n');
for row in &self.rows {
for (i, value) in row.values.iter().enumerate() {
if i > 0 {
result.push_str(", ");
}
result.push_str(&value_to_string_repr(value));
}
result.push('\n');
}
result
}
}
pub struct ResultRow {
pub values: Vec<TypedValue>,
}
impl ResultRow {
pub fn new(values: Vec<TypedValue>) -> Self {
ResultRow { values }
}
pub fn get(&self, index: usize) -> CoreResult<&TypedValue, RemDbError> {
self.values.get(index).ok_or(RemDbError::FieldNotFound)
}
pub fn get_by_name(
&self,
columns: &[String],
column_name: &str,
) -> CoreResult<&TypedValue, RemDbError> {
if let Some(index) = columns.iter().position(|col| col == column_name) {
self.get(index)
} else {
Err(RemDbError::FieldNotFound)
}
}
pub fn len(&self) -> usize {
self.values.len()
}
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}
}
pub struct ResultRowIter<'a> {
result_set: &'a ResultSet,
current: usize,
}
impl<'a> Iterator for ResultRowIter<'a> {
type Item = &'a ResultRow;
fn next(&mut self) -> Option<Self::Item> {
if self.current < self.result_set.rows.len() {
let row = &self.result_set.rows[self.current];
self.current += 1;
Some(row)
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.result_set.rows.len() - self.current;
(remaining, Some(remaining))
}
}
impl<'a> ExactSizeIterator for ResultRowIter<'a> {
fn len(&self) -> usize {
self.result_set.rows.len() - self.current
}
}
pub fn string_to_columns(s: &str) -> Vec<String> {
s.split(",")
.map(|col| col.trim().to_string())
.filter(|col: &String| !col.is_empty())
.collect()
}
fn value_to_string_repr(value: &TypedValue) -> String {
unsafe {
match value.value_type {
DataType::UInt8 => alloc::format!("{}", value.value.u8),
DataType::UInt16 => alloc::format!("{}", value.value.u16),
DataType::UInt32 => alloc::format!("{}", value.value.u32),
DataType::UInt64 => alloc::format!("{}", value.value.u64),
DataType::Int8 => alloc::format!("{}", value.value.i8),
DataType::Int16 => alloc::format!("{}", value.value.i16),
DataType::Int32 => alloc::format!("{}", value.value.i32),
DataType::Int64 => alloc::format!("{}", value.value.i64),
DataType::Float32 => alloc::format!("{}", value.value.float32),
DataType::Float64 => alloc::format!("{}", value.value.float64),
DataType::Bool => alloc::format!("{}", value.value.bool),
DataType::Timestamp => alloc::format!("{}", value.value.time.value),
DataType::TimestampTZ => alloc::format!("{}", value.value.time.value),
DataType::VarChar | DataType::Char => {
let string_slice = core::str::from_utf8(&value.value.string).unwrap_or("");
string_slice.trim_end_matches(char::from(0)).to_string()
}
DataType::Text => {
if value.value.text_storage.is_inline() {
if let Some(data) = value.value.text_storage.as_inline() {
let end = data.iter().position(|b| *b == 0).unwrap_or(data.len());
core::str::from_utf8(&data[..end]).unwrap_or("").to_string()
} else {
String::new()
}
} else if value.value.text_storage.is_external() {
if let Some(ext) = value.value.text_storage.as_external() {
if !ext.data_ptr.is_null() {
let bytes = unsafe {
core::slice::from_raw_parts(ext.data_ptr, ext.length as usize)
};
core::str::from_utf8(bytes).unwrap_or("").to_string()
} else {
String::new()
}
} else {
String::new()
}
} else {
String::new()
}
}
DataType::Interval => {
alloc::format!("{}", value.value.interval.value)
}
DataType::Vector => {
"[vector]".to_string()
}
DataType::Json => {
"<json>".to_string()
}
}
}
}
pub fn values_to_string(values: &[TypedValue]) -> String {
let mut result = String::new();
for (i, value) in values.iter().enumerate() {
if i > 0 {
result.push_str(", ");
}
result.push_str(&value_to_string_repr(value));
}
result
}