tier 0.1.17

Rust configuration library for layered TOML, env, and CLI settings
Documentation
use std::collections::BTreeSet;

use serde_json::Value;

use super::array::{
    additional_example_item_count, array_requires_unique_items, available_additional_array_slots,
    build_repeated_example_values, count_matching_example_items, required_contains_item_count,
};
use super::{build_example_value, legacy_additional_items_for_schema, merge_example_value};

pub(super) fn merge_array_examples(
    object: &serde_json::Map<String, Value>,
    root: &Value,
    visited_refs: &mut BTreeSet<String>,
    merged: &mut Option<Value>,
) {
    merge_fixed_array_examples("prefixItems", object, root, visited_refs, merged);
    merge_fixed_array_examples("items", object, root, visited_refs, merged);
    merge_homogeneous_array_examples(
        object.get("items").filter(|value| !value.is_array()),
        object,
        root,
        visited_refs,
        merged,
    );
    merge_homogeneous_array_examples(
        legacy_additional_items_for_schema(object).filter(|value| !value.is_array()),
        object,
        root,
        visited_refs,
        merged,
    );
}

fn merge_fixed_array_examples(
    keyword: &str,
    object: &serde_json::Map<String, Value>,
    root: &Value,
    visited_refs: &mut BTreeSet<String>,
    merged: &mut Option<Value>,
) {
    let Some(items) = object.get(keyword).and_then(Value::as_array) else {
        return;
    };

    let rendered = items
        .iter()
        .map(|child| build_example_value(child, root, visited_refs, None).unwrap_or(Value::Null))
        .collect::<Vec<_>>();
    merge_example_value(merged, Value::Array(rendered));
}

fn merge_homogeneous_array_examples(
    item_schema: Option<&Value>,
    object: &serde_json::Map<String, Value>,
    root: &Value,
    visited_refs: &mut BTreeSet<String>,
    merged: &mut Option<Value>,
) {
    let Some(item_schema) = item_schema else {
        return;
    };

    let existing_len = merged
        .as_ref()
        .and_then(Value::as_array)
        .map_or(0, Vec::len);
    let additional_items = additional_example_item_count(object, existing_len);
    if additional_items == 0 {
        return;
    }

    match build_example_value(item_schema, root, visited_refs, None) {
        Some(example) => match merged {
            Some(Value::Array(existing)) => {
                existing.extend(std::iter::repeat_n(example, additional_items));
            }
            _ => {
                merge_example_value(
                    merged,
                    Value::Array(std::iter::repeat_n(example, additional_items).collect()),
                );
            }
        },
        None => {
            if merged.is_none() {
                merge_example_value(merged, Value::Array(Vec::new()));
            }
        }
    }
}

pub(super) fn merge_contains_array_examples(
    object: &serde_json::Map<String, Value>,
    root: &Value,
    visited_refs: &mut BTreeSet<String>,
    merged: &mut Option<Value>,
) {
    let Some(contains) = object.get("contains") else {
        return;
    };
    let required_matches = required_contains_item_count(object);
    if required_matches == 0 {
        return;
    }

    let unique_items = array_requires_unique_items(object);
    let existing_matches = merged
        .as_ref()
        .and_then(Value::as_array)
        .map_or(0, |values| {
            count_matching_example_items(values, contains, root, unique_items)
        });
    let missing = required_matches.saturating_sub(existing_matches);
    let available_slots = available_additional_array_slots(
        object,
        merged
            .as_ref()
            .and_then(Value::as_array)
            .map_or(0, Vec::len),
    );
    let additional_items = missing.min(available_slots);
    if additional_items == 0 {
        return;
    }

    match build_example_value(contains, root, visited_refs, None) {
        Some(example) => match merged {
            Some(Value::Array(existing)) => {
                let additions = build_repeated_example_values(
                    example,
                    contains,
                    root,
                    additional_items,
                    unique_items,
                    existing,
                );
                existing.extend(additions);
            }
            _ => {
                let additions = build_repeated_example_values(
                    example,
                    contains,
                    root,
                    additional_items,
                    unique_items,
                    &[],
                );
                merge_example_value(merged, Value::Array(additions));
            }
        },
        None => {
            if merged.is_none() {
                merge_example_value(merged, Value::Array(Vec::new()));
            }
        }
    }
}