use std::ops::Index;
use std::sync::Arc;
use crate::error::{Error, Result};
use crate::value::{DataValue, FromSql, SqlType};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Column {
name: String,
sql_type: SqlType,
}
impl Column {
pub fn new(name: impl Into<String>, sql_type: SqlType) -> Self {
Self {
name: name.into(),
sql_type,
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn sql_type(&self) -> SqlType {
self.sql_type
}
}
pub trait RowIndex {
fn index_of(&self, columns: &[Column]) -> Result<usize>;
fn label(&self) -> String;
}
impl RowIndex for usize {
fn index_of(&self, columns: &[Column]) -> Result<usize> {
if *self < columns.len() {
Ok(*self)
} else {
Err(Error::ColumnIndexOutOfRange {
index: *self,
len: columns.len(),
})
}
}
fn label(&self) -> String {
self.to_string()
}
}
impl RowIndex for str {
fn index_of(&self, columns: &[Column]) -> Result<usize> {
columns
.iter()
.position(|c| c.name == self)
.ok_or_else(|| Error::ColumnNotFound(self.to_string()))
}
fn label(&self) -> String {
self.to_string()
}
}
impl<T: RowIndex + ?Sized> RowIndex for &T {
fn index_of(&self, columns: &[Column]) -> Result<usize> {
(**self).index_of(columns)
}
fn label(&self) -> String {
(**self).label()
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Row {
columns: Arc<[Column]>,
values: Vec<DataValue>,
}
impl Row {
pub fn new(columns: Arc<[Column]>, values: Vec<DataValue>) -> Self {
Self { columns, values }
}
pub fn columns(&self) -> &[Column] {
&self.columns
}
pub fn len(&self) -> usize {
self.values.len()
}
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}
pub fn values(&self) -> &[DataValue] {
&self.values
}
pub fn get<T: FromSql, I: RowIndex>(&self, idx: I) -> T {
match self.try_get(&idx) {
Ok(v) => v,
Err(e) => panic!("failed to get column {}: {e}", idx.label()),
}
}
pub fn try_get<T: FromSql, I: RowIndex>(&self, idx: I) -> Result<T> {
let pos = idx.index_of(&self.columns)?;
T::from_sql(&self.values[pos], self.columns[pos].name())
}
pub fn value<I: RowIndex>(&self, idx: I) -> Option<&DataValue> {
idx.index_of(&self.columns).ok().map(|i| &self.values[i])
}
}
impl Index<&str> for Row {
type Output = DataValue;
fn index(&self, column: &str) -> &Self::Output {
match column.index_of(&self.columns) {
Ok(i) => &self.values[i],
Err(e) => panic!("{e}"),
}
}
}
impl Index<usize> for Row {
type Output = DataValue;
fn index(&self, column: usize) -> &Self::Output {
match column.index_of(&self.columns) {
Ok(i) => &self.values[i],
Err(e) => panic!("{e}"),
}
}
}
#[cfg(test)]
mod tests;