relay-knowledge 1.1.14

Graph-database-based knowledge graph project.
Documentation
use rusqlite::types::Value;

use crate::domain::{CodebaseViewKind, CodebaseViewRequest};

use super::{escape_like, normalized_path_filter, push_unique_path};

pub(super) fn append_file_focus(
    sql: &mut String,
    values: &mut Vec<Value>,
    request: &CodebaseViewRequest,
) {
    if request.view_kind != CodebaseViewKind::AffectedScope {
        return;
    }
    let focus = file_focus_paths(&request.changed_paths);
    if focus.paths.is_empty() && !focus.include_root_files {
        return;
    }
    sql.push_str(" AND (");
    let mut has_filter = false;
    for path in &focus.paths {
        append_or(sql, &mut has_filter);
        sql.push_str("path = ? OR path LIKE ? ESCAPE '\\'");
        values.push(Value::Text(path.clone()));
        values.push(Value::Text(format!("{}/%", escape_like(path))));
    }
    if focus.include_root_files {
        append_or(sql, &mut has_filter);
        sql.push_str("path NOT LIKE ?");
        values.push(Value::Text("%/%".to_owned()));
    }
    sql.push(')');
}

struct FileFocus {
    paths: Vec<String>,
    include_root_files: bool,
}

fn file_focus_paths(changed_paths: &[String]) -> FileFocus {
    let mut focus = FileFocus {
        paths: Vec::new(),
        include_root_files: false,
    };
    for path in changed_paths
        .iter()
        .filter_map(|path| normalized_path_filter(path))
    {
        push_unique_path(&mut focus.paths, path.clone());
        if let Some(parent) = changed_file_parent_focus(&path) {
            if parent.is_empty() {
                focus.include_root_files = true;
            } else {
                push_unique_path(&mut focus.paths, parent.to_owned());
            }
        }
    }
    focus
}

fn changed_file_parent_focus(path: &str) -> Option<&str> {
    let (parent, file_name) = path.rsplit_once('/').unwrap_or(("", path));
    let uppercase = file_name.bytes().any(|byte| byte.is_ascii_uppercase());
    (file_name.contains('.') || uppercase).then_some(parent)
}

fn append_or(sql: &mut String, has_filter: &mut bool) {
    if *has_filter {
        sql.push_str(" OR ");
    }
    *has_filter = true;
}

#[cfg(test)]
#[path = "affected_tests.rs"]
mod tests;