tier 0.1.17

Rust configuration library for layered TOML, env, and CLI settings
Documentation
use serde_json::Value;

use crate::ConfigMetadata;

use super::comments::{
    render_toml_array_table_item_comments, render_toml_comments,
    render_toml_inline_array_item_comments,
};
use super::path::{
    join_metadata_path, resolve_toml_array_item_metadata_path,
    resolve_toml_object_child_metadata_path,
};
use super::value::{
    is_nested_toml_value, start_toml_section, toml_inline_value, toml_key, toml_table_name,
};

pub(super) fn render_example_toml(value: &Value, metadata: &ConfigMetadata) -> String {
    let mut output = String::new();
    render_toml_root_comments(metadata, &mut output);
    match value {
        Value::Object(root) => render_toml_table("", "", root, metadata, &mut output),
        other => render_toml_root_value(other, metadata, &mut output),
    }

    if !output.ends_with('\n') {
        output.push('\n');
    }

    output
}

fn render_toml_root_value(value: &Value, metadata: &ConfigMetadata, output: &mut String) {
    if matches!(value, Value::Array(_)) {
        render_toml_inline_array_item_comments("", metadata, output);
    }

    output.push_str(
        "# root values are rendered as comments because TOML documents require a table at the top level\n",
    );
    output.push_str("# ");
    output.push_str(&toml_inline_value(value));
    output.push('\n');
}

fn render_toml_root_comments(metadata: &ConfigMetadata, output: &mut String) {
    if metadata.checks().is_empty() {
        return;
    }

    for check in metadata.checks() {
        output.push_str("# validate: ");
        output.push_str(&check.to_string());
        output.push('\n');
    }

    output.push('\n');
}

fn render_toml_table(
    display_path: &str,
    metadata_path: &str,
    table: &serde_json::Map<String, Value>,
    metadata: &ConfigMetadata,
    output: &mut String,
) {
    let mut nested = Vec::new();

    for (key, value) in table {
        if value.is_null() {
            continue;
        }

        if is_nested_toml_value(value) {
            nested.push((key.as_str(), value));
            continue;
        }

        let metadata_child_path =
            resolve_toml_object_child_metadata_path(metadata_path, key, metadata);
        render_toml_comments(&metadata_child_path, metadata, output);
        if matches!(value, Value::Array(_)) {
            render_toml_inline_array_item_comments(&metadata_child_path, metadata, output);
        }
        output.push_str(&toml_key(key));
        output.push_str(" = ");
        output.push_str(&toml_inline_value(value));
        output.push('\n');
    }

    let nested_count = nested.len();
    for (index, (key, value)) in nested.into_iter().enumerate() {
        let display_child_path = join_metadata_path(display_path, key);
        let metadata_child_path =
            resolve_toml_object_child_metadata_path(metadata_path, key, metadata);
        match value {
            Value::Object(child) => {
                start_toml_section(output);
                render_toml_comments(&metadata_child_path, metadata, output);
                output.push('[');
                output.push_str(&toml_table_name(&display_child_path));
                output.push_str("]\n");
                render_toml_table(
                    &display_child_path,
                    &metadata_child_path,
                    child,
                    metadata,
                    output,
                );
            }
            Value::Array(items) => {
                for (item_index, item) in items.iter().enumerate() {
                    let Some(child) = item.as_object() else {
                        continue;
                    };
                    let item_metadata_path = resolve_toml_array_item_metadata_path(
                        &metadata_child_path,
                        item_index,
                        metadata,
                    );
                    start_toml_section(output);
                    if item_index == 0 {
                        render_toml_comments(&metadata_child_path, metadata, output);
                    }
                    render_toml_array_table_item_comments(&item_metadata_path, metadata, output);
                    output.push_str("[[");
                    output.push_str(&toml_table_name(&display_child_path));
                    output.push_str("]]\n");
                    render_toml_table(
                        &display_child_path,
                        &item_metadata_path,
                        child,
                        metadata,
                        output,
                    );
                }
            }
            _ => {}
        }

        if index + 1 < nested_count && !output.ends_with("\n\n") {
            output.push('\n');
        }
    }
}