rustbasic-core 0.1.26

Core framework logic for RustBasic - A modern web framework for Rust
Documentation
use super::error::Error;

pub trait Row {}
pub trait Column {}
pub trait TypeInfo {}

#[derive(Debug, Clone)]
pub enum DbValue {
    Null,
    Text(String),
    Blob(Vec<u8>),
    Integer(i64),
    Real(f64),
    Bool(bool),
}

#[derive(Debug, Clone)]
pub struct AnyTypeInfo {
    pub name: String,
}

impl TypeInfo for AnyTypeInfo {}

impl AnyTypeInfo {
    pub fn name(&self) -> &str {
        &self.name
    }
}

#[derive(Debug, Clone)]
pub struct AnyColumn {
    pub name: String,
    pub type_info: AnyTypeInfo,
}

impl Column for AnyColumn {}

impl AnyColumn {
    pub fn name(&self) -> &str {
        &self.name
    }

    pub fn type_info(&self) -> &AnyTypeInfo {
        &self.type_info
    }
}

#[derive(Debug, Clone)]
pub struct AnyRow {
    pub columns: Vec<AnyColumn>,
    pub values: Vec<DbValue>,
}

impl Row for AnyRow {}

impl AnyRow {
    pub fn len(&self) -> usize {
        self.values.len()
    }

    pub fn is_empty(&self) -> bool {
        self.values.is_empty()
    }

    pub fn column(&self, index: usize) -> &AnyColumn {
        &self.columns[index]
    }

    pub fn try_get<T, I>(&self, index: I) -> Result<T, Error>
    where
        T: for<'r> Decode<'r>,
        I: RowIndex,
    {
        let idx = index.index(self)?;
        let val = &self.values[idx];
        T::decode(val)
    }

    pub fn get<T, I>(&self, index: I) -> T
    where
        T: for<'r> Decode<'r>,
        I: RowIndex,
    {
        self.try_get(index).unwrap()
    }
}

pub trait RowIndex {
    fn index(&self, row: &AnyRow) -> Result<usize, Error>;
}

impl RowIndex for usize {
    fn index(&self, row: &AnyRow) -> Result<usize, Error> {
        if *self < row.len() {
            Ok(*self)
        } else {
            Err(Error::ColumnIndexOutOfBounds {
                len: row.len(),
                index: *self,
            })
        }
    }
}

impl RowIndex for &str {
    fn index(&self, row: &AnyRow) -> Result<usize, Error> {
        row.columns
            .iter()
            .position(|col| col.name == *self)
            .ok_or_else(|| Error::ColumnNotFound((*self).to_string()))
    }
}

impl RowIndex for String {
    fn index(&self, row: &AnyRow) -> Result<usize, Error> {
        row.columns
            .iter()
            .position(|col| col.name == *self)
            .ok_or_else(|| Error::ColumnNotFound((*self).to_string()))
    }
}

pub trait Decode<'r>: Sized {
    fn decode(value: &'r DbValue) -> Result<Self, Error>;
}

impl<'r, T: Decode<'r>> Decode<'r> for Option<T> {
    fn decode(value: &'r DbValue) -> Result<Self, Error> {
        match value {
            DbValue::Null => Ok(None),
            other => T::decode(other).map(Some),
        }
    }
}

impl<'r> Decode<'r> for String {
    fn decode(value: &'r DbValue) -> Result<Self, Error> {
        match value {
            DbValue::Text(s) => Ok(s.clone()),
            DbValue::Integer(i) => Ok(i.to_string()),
            DbValue::Real(f) => Ok(f.to_string()),
            DbValue::Bool(b) => Ok(b.to_string()),
            DbValue::Blob(b) => String::from_utf8(b.clone())
                .map_err(|e| Error::DecodeError(e.to_string())),
            DbValue::Null => Err(Error::DecodeError("Cannot decode NULL to String".into())),
        }
    }
}

impl<'r> Decode<'r> for i64 {
    fn decode(value: &'r DbValue) -> Result<Self, Error> {
        match value {
            DbValue::Integer(i) => Ok(*i),
            DbValue::Text(s) => s.parse::<i64>().map_err(|e| Error::DecodeError(e.to_string())),
            DbValue::Bool(b) => Ok(if *b { 1 } else { 0 }),
            DbValue::Real(f) => Ok(*f as i64),
            _ => Err(Error::DecodeError("Cannot decode to i64".into())),
        }
    }
}

impl<'r> Decode<'r> for i32 {
    fn decode(value: &'r DbValue) -> Result<Self, Error> {
        match value {
            DbValue::Integer(i) => Ok(*i as i32),
            DbValue::Text(s) => s.parse::<i32>().map_err(|e| Error::DecodeError(e.to_string())),
            DbValue::Bool(b) => Ok(if *b { 1 } else { 0 }),
            DbValue::Real(f) => Ok(*f as i32),
            _ => Err(Error::DecodeError("Cannot decode to i32".into())),
        }
    }
}

impl<'r> Decode<'r> for u64 {
    fn decode(value: &'r DbValue) -> Result<Self, Error> {
        match value {
            DbValue::Integer(i) => Ok(*i as u64),
            DbValue::Text(s) => s.parse::<u64>().map_err(|e| Error::DecodeError(e.to_string())),
            DbValue::Bool(b) => Ok(if *b { 1 } else { 0 }),
            DbValue::Real(f) => Ok(*f as u64),
            _ => Err(Error::DecodeError("Cannot decode to u64".into())),
        }
    }
}

impl<'r> Decode<'r> for f64 {
    fn decode(value: &'r DbValue) -> Result<Self, Error> {
        match value {
            DbValue::Real(f) => Ok(*f),
            DbValue::Integer(i) => Ok(*i as f64),
            DbValue::Text(s) => s.parse::<f64>().map_err(|e| Error::DecodeError(e.to_string())),
            _ => Err(Error::DecodeError("Cannot decode to f64".into())),
        }
    }
}

impl<'r> Decode<'r> for bool {
    fn decode(value: &'r DbValue) -> Result<Self, Error> {
        match value {
            DbValue::Bool(b) => Ok(*b),
            DbValue::Integer(i) => Ok(*i != 0),
            DbValue::Text(s) => {
                let s_lower = s.to_lowercase();
                if s_lower == "true" || s_lower == "1" || s_lower == "t" || s_lower == "y" || s_lower == "yes" {
                    Ok(true)
                } else if s_lower == "false" || s_lower == "0" || s_lower == "f" || s_lower == "n" || s_lower == "no" || s_lower.is_empty() {
                    Ok(false)
                } else {
                    Err(Error::DecodeError(format!("Cannot decode '{}' to bool", s)))
                }
            }
            _ => Err(Error::DecodeError("Cannot decode to bool".into())),
        }
    }
}

impl<'r> Decode<'r> for Vec<u8> {
    fn decode(value: &'r DbValue) -> Result<Self, Error> {
        match value {
            DbValue::Blob(b) => Ok(b.clone()),
            DbValue::Text(s) => Ok(s.as_bytes().to_vec()),
            _ => Err(Error::DecodeError("Cannot decode to Vec<u8>".into())),
        }
    }
}