indexlake 0.4.0

An experimental table format with extensible index support.
Documentation
use std::collections::HashMap;

use derive_visitor::{Drive, Visitor, VisitorMut};
use uuid::Uuid;

use crate::{catalog::INTERNAL_ROW_ID_FIELD_NAME, expr::Expr};

#[derive(Debug, Default, Visitor)]
#[visitor(Expr(enter))]
pub struct ColumnRecorder {
    columns: Vec<String>,
}

impl ColumnRecorder {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn enter_expr(&mut self, expr: &Expr) {
        if let Expr::Column(name) = expr {
            self.columns.push(name.clone());
        }
    }
}

pub fn visited_columns(expr: &Expr) -> Vec<String> {
    let mut recorder = ColumnRecorder::new();
    expr.drive(&mut recorder);
    recorder.columns
}

#[derive(Debug, VisitorMut)]
#[visitor(Expr(enter))]
pub struct ColumnReplacer<'a> {
    pub field_name_id_map: &'a HashMap<String, Uuid>,
    pub fail_col: Option<String>,
}

impl<'a> ColumnReplacer<'a> {
    pub fn new(field_name_id_map: &'a HashMap<String, Uuid>) -> Self {
        Self {
            field_name_id_map,
            fail_col: None,
        }
    }

    pub fn enter_expr(&mut self, expr: &mut Expr) {
        if let Expr::Column(name) = expr
            && name != INTERNAL_ROW_ID_FIELD_NAME
        {
            if let Some(field_id) = self.field_name_id_map.get(name) {
                *name = hex::encode(field_id)
            } else {
                self.fail_col = Some(name.clone());
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::expr::{col, lit};

    use super::*;

    #[test]
    fn test_visited_columns() {
        let expr = col("a").eq(col("b").plus(lit(1)));
        let columns = visited_columns(&expr);
        assert_eq!(columns, vec!["a", "b"]);
    }
}