neuxdb 0.3.1

A super simple, embedded, encrypted database like SQLite, using pipe-separated files and age encryption.
Documentation
use crate::error::{Error, Result};
use crate::log::LogEntry;
use crate::table::TableData;
use crate::types::Value;
pub fn insert(table: &mut TableData, row: Vec<Value>, logs: &mut Vec<LogEntry>) -> Result<()> {
    table.schema.validate_row(&row)?;
    table.rows.push(row.clone());
    logs.push(LogEntry::new(
        "INSERT",
        &table.schema.name,
        Some(format!("{:?}", row)),
    ));
    Ok(())
}
pub fn select(
    table: &TableData,
    columns: Option<Vec<&str>>,
    filter: Option<&dyn Fn(&[Value]) -> bool>,
) -> Result<Vec<Vec<Value>>> {
    let indices: Option<Vec<usize>> = if let Some(cols) = &columns {
        Some(
            cols.iter()
                .map(|c| {
                    table
                        .schema
                        .columns
                        .iter()
                        .position(|col_def| col_def.name == *c)
                        .ok_or_else(|| Error::ColumnNotFound(c.to_string()))
                })
                .collect::<Result<Vec<_>>>()?,
        )
    } else {
        None
    };
    let mut result = Vec::new();
    for row in &table.rows {
        if let Some(pred) = &filter {
            if !pred(row) {
                continue;
            }
        }
        match &indices {
            Some(idxs) => {
                let projected: Vec<Value> = idxs.iter().map(|&i| row[i].clone()).collect();
                result.push(projected);
            }
            None => {
                result.push(row.clone());
            }
        }
    }
    Ok(result)
}
pub fn update(
    table: &mut TableData,
    filter: &dyn Fn(&[Value]) -> bool,
    set_col: &str,
    new_val: Value,
    logs: &mut Vec<LogEntry>,
) -> Result<usize> {
    let col_idx = table
        .schema
        .columns
        .iter()
        .position(|col_def| col_def.name == set_col)
        .ok_or_else(|| Error::ColumnNotFound(set_col.to_string()))?;
    let col_type = table.schema.columns[col_idx].col_type;
    if !col_type.validate(&new_val) {
        return Err(Error::TypeMismatch {
            column: set_col.to_string(),
            expected: col_type.name().to_string(),
            actual: match &new_val {
                Value::Int(_) => "INT",
                Value::Text(_) => "TEXT",
                Value::Bool(_) => "BOOL",
                Value::Float(_) => "FLOAT",
                Value::Null => "NULL",
            }
            .to_string(),
        });
    }
    let mut count = 0;
    for row in &mut table.rows {
        if filter(row) {
            row[col_idx] = new_val.clone();
            count += 1;
        }
    }
    if count > 0 {
        logs.push(LogEntry::new(
            "UPDATE",
            &table.schema.name,
            Some(format!("Set {} = {:?} on {} rows", set_col, new_val, count)),
        ));
    }
    Ok(count)
}
pub fn delete(
    table: &mut TableData,
    filter: &dyn Fn(&[Value]) -> bool,
    logs: &mut Vec<LogEntry>,
) -> Result<usize> {
    let original = table.rows.len();
    table.rows.retain(|r| !filter(r));
    let removed = original - table.rows.len();
    if removed > 0 {
        logs.push(LogEntry::new(
            "DELETE",
            &table.schema.name,
            Some(format!("Removed {} rows", removed)),
        ));
    }
    Ok(removed)
}