use serde::Serialize;
use ts_rs::TS;
use crate::role::{HEIGHT_GROUP, TOP_GROUP};
#[derive(Debug, Clone, Copy, PartialEq, Serialize, TS)]
#[ts(export)]
pub struct CalibrationLimits {
pub rc_lo: (f64, f64),
pub rc_hi: (f64, f64),
pub rc_band: (f64, f64),
pub rc_bands_max: (f64, f64),
pub volume: (f64, f64),
pub dim: (f64, f64),
pub dim_valid: (f64, f64),
pub boost: (f64, f64),
pub ultra_ms: (f64, f64),
pub xo: (f64, f64),
pub xo_peq: (f64, f64),
pub infra_hz: (f64, f64),
pub infra_boost: (f64, f64),
pub main_delay: (f64, f64),
pub rear_trim: (f64, f64),
pub fsiso: (f64, f64),
pub mimo_low: (f64, f64),
pub mimo_max: (f64, f64),
}
pub const RC_LO_READ: (f64, f64) = (10.0, 200.0);
pub const CALIBRATION_LIMITS: CalibrationLimits = CalibrationLimits {
rc_lo: (20.0, 200.0),
rc_hi: (100.0, 20000.0),
rc_band: (20.0, 20000.0),
rc_bands_max: (0.0, 4.0),
volume: (0.0, 100000.0),
dim: (0.0, 100.0),
dim_valid: (0.5, 100.0),
boost: (0.0, 10.0),
ultra_ms: (30.0, 200.0),
xo: (40.0, 200.0),
xo_peq: (0.0, 1000.0),
infra_hz: (10.0, 300.0),
infra_boost: (0.0, 24.0),
main_delay: (0.0, 100.0),
rear_trim: (-20.0, 20.0),
fsiso: (50.0, 150.0),
mimo_low: (15.0, 150.0),
mimo_max: (0.0, 12.0),
};
pub const SWEEP_LENGTHS: [&str; 7] = ["64k", "128k", "256k", "512k", "1M", "2M", "4M"];
pub const START_DELAYS: [&str; 4] = ["끄기", "5 초", "10 초", "15 초"];
pub const RECENT_PROJECTS_MAX: usize = 10;
pub const CONFIG_GROUPS: [(&str, &str); 9] = [
("프론트", "front"),
("센터", "center"),
("서브", "sub"),
("서라운드", "surround"),
("백", "back"),
("와이드", "wide"),
(TOP_GROUP, "top"),
(HEIGHT_GROUP, "height"),
("기타", "other"),
];
pub const ARRANGEMENT_COUNTS: [usize; 4] = [1, 9, 13, 17];
pub const JOB_HISTORY_MAX: usize = 32;
#[cfg(test)]
mod typescript {
use serde::Serialize;
use serde_json::Value;
use super::*;
const HEADER: &str = "// This file was generated by `cargo test -p secs-types` from crates/secs-types/src/rules.rs. Do not edit this file manually.\n";
fn literal(value: &Value) -> String {
match value {
Value::Null => "null".into(),
Value::Bool(value) => value.to_string(),
Value::Number(number) => match (number.as_i64(), number.as_f64()) {
(Some(int), _) => int.to_string(),
(None, Some(float)) => float.to_string(),
(None, None) => number.to_string(),
},
Value::String(_) => value.to_string(),
Value::Array(items) => {
format!(
"[{}]",
items.iter().map(literal).collect::<Vec<_>>().join(", ")
)
}
Value::Object(fields) => format!(
"{{\n{}}}",
fields
.iter()
.map(|(key, value)| format!(" {key}: {},\n", literal(value)))
.collect::<String>()
),
}
}
pub(super) fn constant(name: &str, ty: Option<&str>, value: &impl Serialize) -> String {
let value = serde_json::to_value(value).expect("rule constants serialize");
let annotation = ty.map(|ty| format!(": {ty}")).unwrap_or_default();
let suffix = if value.is_array() || value.is_object() {
" as const"
} else {
""
};
format!(
"export const {name}{annotation} = {}{suffix};\n",
literal(&value)
)
}
pub(super) fn rules_ts() -> String {
[
HEADER.to_owned(),
"import type { CalibrationLimits } from \"./CalibrationLimits\";\n\n".to_owned(),
constant(
"CALIBRATION_LIMITS",
Some("CalibrationLimits"),
&CALIBRATION_LIMITS,
),
constant("SWEEP_LENGTHS", None, &SWEEP_LENGTHS),
constant("START_DELAYS", None, &START_DELAYS),
constant("RECENT_PROJECTS_MAX", None, &RECENT_PROJECTS_MAX),
constant("CONFIG_GROUPS", None, &CONFIG_GROUPS),
constant("ARRANGEMENT_COUNTS", None, &ARRANGEMENT_COUNTS),
constant("JOB_HISTORY_MAX", None, &JOB_HISTORY_MAX),
]
.concat()
}
}
#[cfg(test)]
mod tests {
use super::typescript::{constant, rules_ts};
use super::*;
#[test]
fn renders_constants_as_typescript() {
assert_eq!(
constant("RECENT_PROJECTS_MAX", None, &RECENT_PROJECTS_MAX),
"export const RECENT_PROJECTS_MAX = 10;\n"
);
assert_eq!(
constant("EDGES", None, &[(0.5, 100.0), (-20.0, 0.71)]),
"export const EDGES = [[0.5, 100], [-20, 0.71]] as const;\n"
);
assert_eq!(
constant("GROUPS", None, &[("탑", "top")]),
"export const GROUPS = [[\"탑\", \"top\"]] as const;\n"
);
let limits = constant("LIMITS", Some("CalibrationLimits"), &CALIBRATION_LIMITS);
assert!(
limits.starts_with(
"export const LIMITS: CalibrationLimits = {\n rc_lo: [20, 200],\n rc_hi: [100, 20000],\n rc_band: [20, 20000],\n rc_bands_max: [0, 4],\n"
),
"{limits}"
);
assert!(
limits.ends_with(" mimo_max: [0, 12],\n} as const;\n"),
"{limits}"
);
}
#[test]
fn export_rules_ts() {
let dir = ts_rs::Config::from_env().out_dir().to_path_buf();
std::fs::create_dir_all(&dir).unwrap();
std::fs::write(dir.join("rules.ts"), rules_ts()).unwrap();
}
}