tier 0.1.17

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

use serde::de::DeserializeOwned;
use serde_json::Value;

use crate::error::ConfigError;
use crate::metadata::ConfigMetadata;
use crate::report::{ConfigReport, ConfigWarning};

use crate::loader::UnknownFieldPolicy;
use crate::loader::unknown::{
    collect_unknown_fields, collect_unknown_fields_best_effort,
    collect_unknown_fields_from_metadata_scope, deserialize_error_scope, error_path_for_scope,
};

pub(super) fn pre_deserialize_unknown_fields_error<T>(
    policy: UnknownFieldPolicy,
    merged: &Value,
    metadata: &ConfigMetadata,
    suggestion_paths: &BTreeMap<String, String>,
    report: &ConfigReport,
    string_coercion_paths: &BTreeSet<String>,
    error: &ConfigError,
) -> Option<ConfigError>
where
    T: DeserializeOwned,
{
    if matches!(policy, UnknownFieldPolicy::Allow) {
        return None;
    }

    let mut unknown_fields = collect_unknown_fields_best_effort::<T>(
        merged,
        suggestion_paths,
        report,
        string_coercion_paths,
    );
    if unknown_fields.is_empty() && !metadata.fields().is_empty() {
        unknown_fields = collect_unknown_fields_from_metadata_scope(
            merged,
            metadata,
            suggestion_paths,
            report,
            deserialize_error_scope(error_path_for_scope(error)),
        );
    }

    (!unknown_fields.is_empty()).then_some(ConfigError::UnknownFields {
        fields: unknown_fields,
    })
}

pub(super) fn apply_unknown_field_policy<T>(
    policy: UnknownFieldPolicy,
    merged: &Value,
    suggestion_paths: &BTreeMap<String, String>,
    report: &mut ConfigReport,
    string_coercion_paths: &BTreeSet<String>,
) -> Result<(), ConfigError>
where
    T: DeserializeOwned,
{
    if matches!(policy, UnknownFieldPolicy::Allow) {
        return Ok(());
    }

    let unknown_fields =
        collect_unknown_fields::<T>(merged, suggestion_paths, report, string_coercion_paths)?;
    if unknown_fields.is_empty() {
        return Ok(());
    }

    match policy {
        UnknownFieldPolicy::Allow => Ok(()),
        UnknownFieldPolicy::Warn => {
            for field in unknown_fields {
                report.record_warning(ConfigWarning::UnknownField(field));
            }
            Ok(())
        }
        UnknownFieldPolicy::Deny => Err(ConfigError::UnknownFields {
            fields: unknown_fields,
        }),
    }
}