neuxdb 0.1.0

A super simple, embedded, encrypted database like SQLite, using pipe-separated files and age encryption.
Documentation
use std::fmt;
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
    Text(String),
    Int(i64),
}
impl Value {
    pub fn as_text(&self) -> Option<&str> {
        match self {
            Value::Text(s) => Some(s),
            _ => None,
        }
    }
    pub fn as_int(&self) -> Option<i64> {
        match self {
            Value::Int(i) => Some(*i),
            _ => None,
        }
    }
    pub fn to_like_string(&self) -> String {
        match self {
            Value::Text(s) => s.clone(),
            Value::Int(i) => i.to_string(),
        }
    }
}
impl From<&str> for Value {
    fn from(s: &str) -> Self {
        if let Ok(i) = s.parse::<i64>() {
            Value::Int(i)
        } else {
            Value::Text(s.to_string())
        }
    }
}
impl From<String> for Value {
    fn from(s: String) -> Self {
        Value::from(s.as_str())
    }
}
impl fmt::Display for Value {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Value::Text(s) => write!(f, "{}", s),
            Value::Int(i) => write!(f, "{}", i),
        }
    }
}
pub type Row = Vec<Value>;
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum ColumnType {
    Text,
    Int,
}
impl fmt::Display for ColumnType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            ColumnType::Text => write!(f, "Text"),
            ColumnType::Int => write!(f, "Int"),
        }
    }
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct TableSchema {
    pub columns: Vec<String>,
    pub types: Vec<ColumnType>,
}
impl TableSchema {
    pub fn new(columns: Vec<String>) -> Self {
        let types = vec![ColumnType::Text; columns.len()];
        Self { columns, types }
    }
    pub fn validate_value(&self, col_index: usize, value: &Value) -> crate::error::Result<()> {
        if col_index >= self.types.len() {
            return Ok(());
        }
        let expected_type = &self.types[col_index];
        match (expected_type, value) {
            (ColumnType::Int, Value::Int(_)) => Ok(()),
            (ColumnType::Text, Value::Text(_)) => Ok(()),
            _ => Err(crate::error::NeuxDbError::TypeMismatch {
                expected: expected_type.clone(),
                column: self.columns[col_index].clone(),
                found: value.clone(),
            }),
        }
    }
}