1use crate::catalog::DispatcherEntry;
20
21#[must_use]
35pub fn to_helpers_skeleton(entry: &DispatcherEntry) -> String {
36 let variant_fields = (entry.variant_fields)();
37 let mut out = String::with_capacity(512);
38 out.push_str(&format!(
39 "# quirk-apply.nix — mechanically emitted skeleton for the\n"
40 ));
41 out.push_str(&format!(
42 "# `{}` dispatcher (gen_platform::DispatcherCatalog).\n",
43 entry.label
44 ));
45 out.push_str("# Each helpers arm calls a class-helper function — fill the\n");
46 out.push_str("# function bodies in the `let` block below to drive the\n");
47 out.push_str("# matching builder.\n");
48 out.push_str("#\n");
49 out.push_str(
50 "# Refresh via:\n# gen dispatchers helpers-skeleton \\\n# --label ",
51 );
52 out.push_str(entry.label);
53 out.push_str(" --format nix\n");
54 out.push_str("{ lib }:\nlet\n");
55
56 for (kind, fields) in &variant_fields {
59 let helper_name = format!("{}Apply", kebab_to_camel(kind));
60 if fields.is_empty() {
61 out.push_str(&format!(" # Unit variant — no payload.\n"));
62 out.push_str(&format!(" {helper_name} = _: _: {{}};\n\n"));
63 } else if fields.len() == 1 {
64 let field = &fields[0];
65 out.push_str(&format!(
66 " # Single-field variant — receives the field's value.\n"
67 ));
68 out.push_str(&format!(" {helper_name} = {field}: _: {{}};\n\n"));
69 } else {
70 let inherit_str = fields.join(" ");
71 out.push_str(&format!(
72 " # Multi-field variant — destructure the typed payload.\n"
73 ));
74 out.push_str(&format!(
75 " {helper_name} = {{ {} }}: _: {{}};\n\n",
76 inherit_str.replace(' ', ", ")
77 ));
78 }
79 }
80
81 out.push_str("in\nimport ../shared/mk-typed-dispatcher.nix {\n");
82 out.push_str(" inherit lib;\n");
83 out.push_str(" helpers = {\n");
84
85 for (kind, fields) in &variant_fields {
86 let helper_name = format!("{}Apply", kebab_to_camel(kind));
87 if fields.is_empty() {
88 out.push_str(&format!(" \"{kind}\" = quirk: {helper_name} quirk;\n"));
89 } else if fields.len() == 1 {
90 let field = &fields[0];
91 out.push_str(&format!(
92 " \"{kind}\" = quirk: {helper_name} quirk.{field};\n"
93 ));
94 } else {
95 let inherit_str = fields.join(" ");
96 out.push_str(&format!(
97 " \"{kind}\" = quirk: {helper_name} {{\n"
98 ));
99 out.push_str(&format!(
100 " inherit (quirk) {inherit_str};\n"
101 ));
102 out.push_str(" };\n");
103 }
104 }
105
106 out.push_str(" };\n}\n");
107 out
108}
109
110fn kebab_to_camel(s: &str) -> String {
112 let mut out = String::with_capacity(s.len());
113 let mut upper_next = false;
114 for ch in s.chars() {
115 if ch == '-' {
116 upper_next = true;
117 } else if upper_next {
118 for u in ch.to_uppercase() {
119 out.push(u);
120 }
121 upper_next = false;
122 } else {
123 out.push(ch);
124 }
125 }
126 out
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132
133 #[test]
134 fn kebab_to_camel_handles_simple_cases() {
135 assert_eq!(kebab_to_camel("force-cfg"), "forceCfg");
136 assert_eq!(kebab_to_camel("substitute-source"), "substituteSource");
137 assert_eq!(kebab_to_camel("ldflag"), "ldflag");
138 assert_eq!(kebab_to_camel("pin-toolchain"), "pinToolchain");
139 assert_eq!(
140 kebab_to_camel("fold-normal-into-build"),
141 "foldNormalIntoBuild"
142 );
143 }
144}