#![allow(dead_code)]
#[derive(Debug, Clone)]
pub struct RenderSettingsExportConfig {
pub pretty: bool,
pub validate: bool,
}
#[derive(Debug, Clone)]
pub struct RenderSetting {
pub key: String,
pub value: String,
pub category: String,
}
#[derive(Debug, Clone)]
pub struct RenderSettingsExport {
pub settings: Vec<RenderSetting>,
pub total_bytes: usize,
}
pub fn default_render_settings_export_config() -> RenderSettingsExportConfig {
RenderSettingsExportConfig {
pretty: true,
validate: true,
}
}
pub fn new_render_settings_export() -> RenderSettingsExport {
RenderSettingsExport {
settings: Vec::new(),
total_bytes: 0,
}
}
pub fn rse_add_setting(export: &mut RenderSettingsExport, setting: RenderSetting) {
if let Some(existing) = export.settings.iter_mut().find(|s| s.key == setting.key) {
*existing = setting;
} else {
export.settings.push(setting);
}
}
pub fn rse_to_json(export: &mut RenderSettingsExport, cfg: &RenderSettingsExportConfig) -> String {
let indent = if cfg.pretty { " " } else { "" };
let nl = if cfg.pretty { "\n" } else { "" };
let mut out = format!("{{{nl}{indent}\"settings\":[{nl}");
for (i, s) in export.settings.iter().enumerate() {
let comma = if i + 1 < export.settings.len() { "," } else { "" };
out.push_str(&format!(
"{indent}{indent}{{\"key\":\"{}\",\"value\":\"{}\",\"category\":\"{}\"}}{}{nl}",
s.key, s.value, s.category, comma
));
}
out.push_str(&format!("{indent}]{nl}}}"));
export.total_bytes = out.len();
out
}
pub fn rse_setting_count(export: &RenderSettingsExport) -> usize {
export.settings.len()
}
pub fn rse_get_setting<'a>(export: &'a RenderSettingsExport, key: &str) -> Option<&'a RenderSetting> {
export.settings.iter().find(|s| s.key == key)
}
pub fn rse_write_to_file(
export: &mut RenderSettingsExport,
cfg: &RenderSettingsExportConfig,
_path: &str,
) -> usize {
let json = rse_to_json(export, cfg);
export.total_bytes = json.len();
export.total_bytes
}
pub fn rse_clear(export: &mut RenderSettingsExport) {
export.settings.clear();
export.total_bytes = 0;
}
pub fn rse_total_bytes(export: &RenderSettingsExport) -> usize {
export.total_bytes
}
pub fn rse_validate(export: &RenderSettingsExport, cfg: &RenderSettingsExportConfig) -> Vec<String> {
let mut errors: Vec<String> = Vec::new();
if !cfg.validate {
return errors;
}
for s in &export.settings {
if s.key.is_empty() {
errors.push("setting with empty key".to_string());
}
if s.value.is_empty() {
errors.push(format!("setting '{}' has empty value", s.key));
}
}
errors
}
fn make_setting(key: &str, value: &str, category: &str) -> RenderSetting {
RenderSetting {
key: key.to_string(),
value: value.to_string(),
category: category.to_string(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_config_values() {
let cfg = default_render_settings_export_config();
assert!(cfg.pretty);
assert!(cfg.validate);
}
#[test]
fn new_export_is_empty() {
let e = new_render_settings_export();
assert_eq!(rse_setting_count(&e), 0);
assert_eq!(rse_total_bytes(&e), 0);
}
#[test]
fn add_setting_increments_count() {
let mut e = new_render_settings_export();
rse_add_setting(&mut e, make_setting("resolution_width", "1920", "output"));
assert_eq!(rse_setting_count(&e), 1);
}
#[test]
fn add_duplicate_key_overwrites() {
let mut e = new_render_settings_export();
rse_add_setting(&mut e, make_setting("aa_samples", "4", "anti-aliasing"));
rse_add_setting(&mut e, make_setting("aa_samples", "8", "anti-aliasing"));
assert_eq!(rse_setting_count(&e), 1);
assert_eq!(rse_get_setting(&e, "aa_samples").expect("should succeed").value, "8");
}
#[test]
fn get_setting_returns_correct_value() {
let mut e = new_render_settings_export();
rse_add_setting(&mut e, make_setting("shadow_res", "2048", "shadow"));
let s = rse_get_setting(&e, "shadow_res");
assert!(s.is_some());
assert_eq!(s.expect("should succeed").value, "2048");
}
#[test]
fn get_setting_missing_returns_none() {
let e = new_render_settings_export();
assert!(rse_get_setting(&e, "nonexistent").is_none());
}
#[test]
fn json_contains_settings_key() {
let mut e = new_render_settings_export();
rse_add_setting(&mut e, make_setting("ao_radius", "0.5", "ambient-occlusion"));
let cfg = default_render_settings_export_config();
let json = rse_to_json(&mut e, &cfg);
assert!(json.contains("\"settings\""));
assert!(json.contains("ao_radius"));
assert!(json.contains("ambient-occlusion"));
}
#[test]
fn validate_catches_empty_value() {
let mut e = new_render_settings_export();
rse_add_setting(&mut e, make_setting("shadow_res", "", "shadow"));
let cfg = default_render_settings_export_config();
let errs = rse_validate(&e, &cfg);
assert!(!errs.is_empty());
}
#[test]
fn validate_ok_when_all_valid() {
let mut e = new_render_settings_export();
rse_add_setting(&mut e, make_setting("resolution_width", "1920", "output"));
let cfg = default_render_settings_export_config();
let errs = rse_validate(&e, &cfg);
assert!(errs.is_empty());
}
#[test]
fn write_to_file_sets_total_bytes() {
let mut e = new_render_settings_export();
rse_add_setting(&mut e, make_setting("resolution_width", "1920", "output"));
let cfg = default_render_settings_export_config();
let n = rse_write_to_file(&mut e, &cfg, "/tmp/render_settings.json");
assert!(n > 0);
assert_eq!(rse_total_bytes(&e), n);
}
#[test]
fn clear_resets_state() {
let mut e = new_render_settings_export();
rse_add_setting(&mut e, make_setting("resolution_width", "1920", "output"));
let cfg = default_render_settings_export_config();
rse_write_to_file(&mut e, &cfg, "/tmp/render_settings.json");
rse_clear(&mut e);
assert_eq!(rse_setting_count(&e), 0);
assert_eq!(rse_total_bytes(&e), 0);
}
}