reddb-io-server 1.2.0

RedDB server-side engine: storage, runtime, replication, MCP, AI, and the gRPC/HTTP/RedWire/PG-wire dispatchers. Re-exported by the umbrella `reddb` crate.
Documentation
use crate::json::{Map, Value as JsonValue};
use crate::storage::query::unified::UnifiedRecord;
use crate::storage::schema::Value;

pub(crate) fn search_selection_json(
    entity_types: &Option<Vec<String>>,
    capabilities: &Option<Vec<String>>,
) -> JsonValue {
    let entity_types = entity_types.as_ref().filter(|value| !value.is_empty());
    let capabilities = capabilities.as_ref().filter(|value| !value.is_empty());
    let has_filter = entity_types.is_some() || capabilities.is_some();

    let mut object = Map::new();
    object.insert(
        "scope".to_string(),
        if has_filter {
            JsonValue::String("filtered".to_string())
        } else {
            JsonValue::String("any".to_string())
        },
    );
    if let Some(entity_types) = entity_types {
        object.insert(
            "entity_types".to_string(),
            JsonValue::Array(
                entity_types
                    .iter()
                    .cloned()
                    .map(JsonValue::String)
                    .collect(),
            ),
        );
    }
    if let Some(capabilities) = capabilities {
        object.insert(
            "capabilities".to_string(),
            JsonValue::Array(
                capabilities
                    .iter()
                    .cloned()
                    .map(JsonValue::String)
                    .collect(),
            ),
        );
    }
    JsonValue::Object(object)
}

pub(crate) fn filter_query_records(
    records: &[UnifiedRecord],
    entity_types: &Option<Vec<String>>,
    capabilities: &Option<Vec<String>>,
) -> Vec<UnifiedRecord> {
    let entity_types = entity_types
        .as_ref()
        .filter(|values| !values.is_empty())
        .map(|values| {
            values
                .iter()
                .map(|value| value.to_lowercase())
                .collect::<Vec<_>>()
        });
    let capabilities = capabilities
        .as_ref()
        .filter(|values| !values.is_empty())
        .map(|values| {
            values
                .iter()
                .map(|value| value.to_lowercase())
                .collect::<Vec<_>>()
        });

    if entity_types.is_none() && capabilities.is_none() {
        return records.to_vec();
    }

    let entity_types = entity_types.as_deref();
    let capabilities = capabilities.as_deref();

    records
        .iter()
        .filter(|record| {
            let entity_type = record
                .get("red_entity_type")
                .and_then(Value::as_text)
                .unwrap_or_else(|| {
                    record
                        .get("red_kind")
                        .and_then(Value::as_text)
                        .unwrap_or("")
                })
                .to_lowercase();

            let entity_capabilities = record
                .get("red_capabilities")
                .and_then(Value::as_text)
                .map(record_capability_tokens)
                .unwrap_or_default();

            let entity_type_ok = entity_types.is_none_or(|types| {
                !types.is_empty() && types.iter().any(|expected| expected == &entity_type)
            });
            let capability_ok = capabilities.is_none_or(|required| {
                required
                    .iter()
                    .any(|target| entity_capabilities.iter().any(|value| value == target))
            });

            entity_type_ok && capability_ok
        })
        .cloned()
        .collect()
}

fn record_capability_tokens(text: &str) -> Vec<String> {
    text.split(',')
        .map(|value| value.trim().to_lowercase())
        .filter(|value| !value.is_empty())
        .collect()
}