use std::path::{Path, PathBuf};
use schemars::JsonSchema;
use crate::{
config::{ConfigResult, ConfigSchema},
config_format::ConfigFormat,
config_output::write_template,
config_schema::{
generate::root_config_schema,
paths::{
env_only_field_paths, nested_section_paths, split_section_paths,
transparent_array_section_paths,
},
},
path::absolutize_lexical,
template_tree::{collect_template_targets, select_template_source},
};
mod binding;
mod fields;
mod includes;
mod json5;
mod render;
pub(crate) mod section;
mod target;
mod toml;
mod transparent_yaml;
mod yaml;
use binding::{schema_path_for_template_target, template_with_schema_directive};
use includes::{
append_missing_include_paths, default_child_include_paths, retain_split_include_paths,
template_source_include_paths,
};
use render::template_for_target;
use section::section_path_for_target;
use transparent_yaml::{is_transparent_split_section, normalize_transparent_split_template};
pub use render::template_for_path;
pub use target::ConfigTemplateTarget;
pub fn template_targets_for_paths<S>(
config_path: impl AsRef<Path>,
output_path: impl AsRef<Path>,
) -> ConfigResult<Vec<ConfigTemplateTarget>>
where
S: ConfigSchema + JsonSchema,
{
let output_path = output_path.as_ref();
let source_path = select_template_source(config_path, output_path);
let root_source_path = absolutize_lexical(source_path)?;
let output_base_dir = output_path.parent().unwrap_or_else(|| Path::new("."));
let full_schema = root_config_schema::<S>()?;
let all_section_paths = nested_section_paths(&S::META);
let split_paths = split_section_paths::<S>(&full_schema);
let transparent_paths = transparent_array_section_paths::<S>(&full_schema);
let env_only_paths = env_only_field_paths::<S>(&full_schema);
let template_targets = collect_template_targets(
&root_source_path,
output_path,
|node_source_path| -> ConfigResult<Vec<PathBuf>> {
let include_paths = template_source_include_paths::<S>(node_source_path)?;
let mut include_paths = retain_split_include_paths::<S>(
&root_source_path,
node_source_path,
include_paths,
&all_section_paths,
&split_paths,
);
append_missing_include_paths(
&mut include_paths,
default_child_include_paths::<S>(&root_source_path, node_source_path, &split_paths),
);
Ok(include_paths)
},
)?;
let split_paths = template_targets
.iter()
.filter_map(|target| {
section_path_for_target::<S>(output_base_dir, target.target_path(), &split_paths)
.filter(|section_path| !section_path.is_empty())
})
.collect::<Vec<_>>();
template_targets
.into_iter()
.map(|target| {
let (_, target_path, include_paths) = target.into_parts();
let section_path =
section_path_for_target::<S>(output_base_dir, &target_path, &split_paths)
.unwrap_or_default();
let mut content = template_for_target::<S>(
&target_path,
&include_paths,
§ion_path,
&split_paths,
&env_only_paths,
)?;
if ConfigFormat::from_path(&target_path) == ConfigFormat::Yaml
&& is_transparent_split_section(§ion_path, &transparent_paths)
{
let section_name = section_path.last().copied().unwrap_or("");
let inner_field = crate::config_schema::paths::inner_field_for_section(
&full_schema,
§ion_path,
);
content =
normalize_transparent_split_template(&content, section_name, &inner_field);
}
Ok(ConfigTemplateTarget {
content,
path: target_path,
})
})
.collect()
}
pub fn template_targets_for_paths_with_schema<S>(
config_path: impl AsRef<Path>,
output_path: impl AsRef<Path>,
schema_path: impl AsRef<Path>,
) -> ConfigResult<Vec<ConfigTemplateTarget>>
where
S: ConfigSchema + JsonSchema,
{
let output_path = output_path.as_ref();
let output_base_dir = output_path.parent().unwrap_or_else(|| Path::new("."));
let schema_path = schema_path.as_ref();
let full_schema = root_config_schema::<S>()?;
let split_paths = split_section_paths::<S>(&full_schema);
template_targets_for_paths::<S>(config_path, output_path)?
.into_iter()
.map(|mut target| {
let schema_path = schema_path_for_template_target::<S>(
output_base_dir,
&target.path,
schema_path,
&split_paths,
);
target.content =
template_with_schema_directive(&target.path, &schema_path, &target.content)?;
Ok(target)
})
.collect()
}
pub fn write_config_templates<S>(
config_path: impl AsRef<Path>,
output_path: impl AsRef<Path>,
) -> ConfigResult<()>
where
S: ConfigSchema + JsonSchema,
{
for target in template_targets_for_paths::<S>(config_path, output_path)? {
write_template(&target.path, &target.content)?;
}
Ok(())
}
pub fn write_config_templates_with_schema<S>(
config_path: impl AsRef<Path>,
output_path: impl AsRef<Path>,
schema_path: impl AsRef<Path>,
) -> ConfigResult<()>
where
S: ConfigSchema + JsonSchema,
{
for target in
template_targets_for_paths_with_schema::<S>(config_path, output_path, schema_path)?
{
write_template(&target.path, &target.content)?;
}
Ok(())
}