rototo 0.1.0-alpha.3

Control plane for runtime configuration of your application.
Documentation
use toml_span::Value as TomlValue;
use toml_span::value::Table;

use super::super::index::*;
use super::super::source::SourceDocument;
use super::super::syntax::{ParsedToml, item_location, table_location};
use super::fields::{
    integer_field, optional_string_field, predicate_op_field, project_bucket_range,
    project_value_shape, string_field,
};

pub(crate) fn project_qualifier(
    document: &SourceDocument,
    toml: &ParsedToml,
    id: &str,
) -> QualifierNode {
    let root = toml.root_table();
    let location = document.document_location();
    let schema_version = root
        .map(|root| integer_field(document, root, "schema_version", location.clone()))
        .unwrap_or_else(|| ProjectField::Missing {
            location: location.clone(),
        });
    let description = root.and_then(|root| optional_string_field(document, root, "description"));
    let predicates = project_predicates(document, root);

    QualifierNode {
        doc: document.id,
        id: id.to_owned(),
        location,
        schema_version,
        description,
        predicates,
    }
}

fn project_predicates(document: &SourceDocument, root: Option<&Table<'_>>) -> PredicateCollection {
    let Some(root) = root else {
        return PredicateCollection::Missing {
            location: document.document_location(),
        };
    };
    let Some(item) = root.get("predicate") else {
        return PredicateCollection::Missing {
            location: document.document_location(),
        };
    };
    let Some(predicates) = item.as_array() else {
        return PredicateCollection::Invalid {
            location: item_location(document, item),
        };
    };

    PredicateCollection::Predicates(
        predicates
            .iter()
            .enumerate()
            .map(|(index, value)| project_predicate(document, index, value))
            .collect(),
    )
}

fn project_predicate(
    document: &SourceDocument,
    index: usize,
    value: &TomlValue<'_>,
) -> PredicateNode {
    let location = table_location(document, value);
    let Some(table) = value.as_table() else {
        return PredicateNode {
            index,
            location: location.clone(),
            attribute: ProjectField::Invalid {
                location: location.clone(),
            },
            op: ProjectField::Invalid {
                location: location.clone(),
            },
            value: None,
            salt: None,
            range: None,
            has_bucket_value: false,
        };
    };
    let attribute = string_field(document, table, "attribute", location.clone());
    let op = predicate_op_field(document, table, location.clone());
    let value = table
        .get("value")
        .map(|item| project_value_shape(document, item));
    let salt = table
        .get("salt")
        .map(|_| string_field(document, table, "salt", location.clone()));
    let range = table
        .get("range")
        .map(|item| project_bucket_range(document, item));
    let has_bucket_value = table.contains_key("value");

    PredicateNode {
        index,
        location,
        attribute,
        op,
        value,
        salt,
        range,
        has_bucket_value,
    }
}