1use super::docs::{format_docstring, CommentStyle};
2use super::shared;
3use super::Codegen;
4use crate::prelude::*;
5use anyhow::Result;
6use convert_case::{Case, Casing};
7
8pub struct HackCodegen {
9 namespace: &'static str,
10}
11
12impl Codegen for HackCodegen {
13 fn gen_declarations(declarations: &Declarations) -> Result<String> {
14 let mut rc = HackCodegen { namespace: "" };
15
16 let mut declarations_code = String::from("<?hh\n");
17
18 for config in &declarations.config {
19 match config {
20 DeclarationsConfig::HackNamespace(namespace) => rc.namespace = namespace,
21 DeclarationsConfig::FileHeader(header) => {
22 declarations_code.push_str(&format!("{}\n", header));
23 }
24 }
25 }
26
27 for declaration in &declarations.declarations {
28 declarations_code.push('\n');
29 declarations_code.push_str(&rc.gen_declaration(declaration)?);
30 declarations_code.push('\n');
31 }
32
33 Ok(declarations_code)
34 }
35}
36
37impl HackCodegen {
38 fn gen_declaration(&self, declaration: &TypeDeclaration) -> Result<String> {
39 let name = self.gen_namespaced_name(declaration.name);
40 let mut r = match &declaration.value {
41 DeclarationValue::TPrimitive(p) => {
42 format!(
43 "type {}{} = {};",
44 name,
45 shared::generic_params(&declaration.generic_params, |g| self.gen_generic(g)),
46 self.gen_primitive_type(p)
47 )
48 }
49 DeclarationValue::TMap(m) => {
50 format!(
51 "type {}{} = {};",
52 name,
53 shared::generic_params(&declaration.generic_params, |g| self.gen_generic(g)),
54 self.gen_map(m)
55 )
56 }
57 DeclarationValue::TVec(v) => {
58 format!(
59 "type {}{} = {};",
60 name,
61 shared::generic_params(&declaration.generic_params, |g| self.gen_generic(g)),
62 self.gen_vec(v)
63 )
64 }
65 DeclarationValue::TOption(o) => {
66 format!(
67 "type {}{} = {};",
68 name,
69 shared::generic_params(&declaration.generic_params, |g| self.gen_generic(g)),
70 self.gen_option(o)
71 )
72 }
73 DeclarationValue::TTuple(t) => {
74 format!(
75 "type {}{} = {};",
76 name,
77 shared::generic_params(&declaration.generic_params, |g| self.gen_generic(g)),
78 self.gen_tuple(t)
79 )
80 }
81 DeclarationValue::TStruct(s) => {
82 format!(
83 "type {}{} = {};",
84 name,
85 shared::generic_params(&declaration.generic_params, |g| self.gen_generic(g)),
86 self.gen_struct(s, 0)
87 )
88 }
89 DeclarationValue::TEnum(e) => self.gen_enum(&name, &declaration.generic_params, e),
90 DeclarationValue::TSimpleEnum(e) => self.gen_simple_enum(&name, &e.variants),
91 DeclarationValue::Docs => String::new(),
92 DeclarationValue::CodeBlock(b) => self.gen_code_block(b),
93 };
94
95 if let Some(doc) = format_docstring(declaration.docs, CommentStyle::DoubleSlash, 0) {
96 r = format!("{}\n{}", doc, r);
97 }
98
99 Ok(r)
100 }
101
102 fn gen_map(&self, m: &TMap) -> String {
103 let value = match &m.value {
104 TMapValue::TPrimitive(p) => self.gen_primitive_type(p),
105 TMapValue::TSet(s) => self.gen_set(s),
106 };
107
108 format!("dict<{}, {}>", self.gen_primitive_type(&m.key), value)
109 }
110
111 fn gen_vec(&self, v: &TVec) -> String {
112 let value = match &v {
113 TVec::TPrimitive(p) => self.gen_primitive_type(p),
114 };
115 format!("vec<{}>", value)
116 }
117
118 fn gen_set(&self, s: &TSet) -> String {
119 let value = match &s {
120 TSet::TPrimitive(p) => self.gen_primitive_type(p),
121 };
122
123 format!("keyset<{}>", value)
124 }
125
126 fn gen_option(&self, o: &TOption) -> String {
127 format!("?{}", self.gen_option_value(o))
128 }
129
130 fn gen_option_value(&self, o: &TOption) -> String {
131 match &o {
132 TOption::TPrimitive(p) => self.gen_primitive_type(&p),
133 TOption::TMap(m) => self.gen_map(m),
134 TOption::TVec(v) => self.gen_vec(v),
135 TOption::TSet(s) => self.gen_set(s),
136 TOption::TTuple(t) => self.gen_tuple(t),
137 }
138 }
139
140 fn gen_struct(&self, s: &TStruct, indent: usize) -> String {
141 let mut fields = String::new();
142
143 let prefix = " ".repeat(indent);
144
145 let mut is_option = "";
146
147 for field in &s.fields {
148 let mut field_type = match &field.field_type {
149 StructFieldType::TMap(m) => self.gen_map(m),
150 StructFieldType::TSet(s) => self.gen_set(s),
151 StructFieldType::TPrimitive(p) => self.gen_primitive_type(&p),
152 StructFieldType::TTuple(t) => self.gen_tuple(t),
153 StructFieldType::TVec(v) => self.gen_vec(v),
154 StructFieldType::TOption(o) => {
155 is_option = "?";
156 self.gen_option_value(o)
157 }
158 };
159
160 field_type = format!(
161 "\n {}{}'{}' => {},",
162 &prefix, is_option, field.name, field_type
163 );
164 is_option = "";
166
167 if let Some(doc) = format_docstring(field.docs, CommentStyle::DoubleSlash, indent + 4) {
168 field_type = format!("\n{}{}", doc, field_type);
169 }
170
171 fields.push_str(&field_type);
172 }
173
174 format!("shape({}\n{})", fields, prefix)
175 }
176
177 fn gen_simple_enum(&self, name: &str, variants: &[&str]) -> String {
178 let mut variant_lines = vec![];
179
180 for name in variants {
181 variant_lines.push(format!(
182 r#" {} = "{}";{}"#,
183 name.to_case(Case::ScreamingSnake),
184 name,
185 "\n"
186 ));
187 }
188
189 format!(
190 "enum {}: string as string {{\n{}}}",
191 name,
192 variant_lines.join("")
193 )
194 }
195
196 fn gen_enum(&self, name: &str, generic_params: &[TGeneric], e: &TEnum) -> String {
197 let variant_type_enum_name = format!("{}Type", name);
198
199 let variant_names = e.variants.iter().map(|v| v.name).collect::<Vec<_>>();
200 let simple_enum = self.gen_simple_enum(&variant_type_enum_name, &variant_names);
201
202 let mut variants = String::new();
203
204 for variant in &e.variants {
205 let mut variant_type = match &variant.variant_type {
206 EnumVariantType::TStruct(s) => format!(" {}", self.gen_struct(s, 4)),
207 EnumVariantType::TPrimitive(p) => self.gen_primitive_type(p),
208 };
209
210 variant_type = format!("\n ?'{}' => {},", variant.name, variant_type);
211
212 if let Some(doc) = format_docstring(variant.docs, CommentStyle::DoubleSlash, 4) {
213 variant_type = format!("\n{}{}", doc, variant_type);
214 }
215
216 variants.push_str(&variant_type);
217 }
218
219 format!(
220 "
221{}
222
223type {}{} = shape({}\n);",
224 simple_enum,
225 name,
226 shared::generic_params(generic_params, |g| self.gen_generic(g)),
227 variants
228 )
229 }
230
231 fn gen_tuple(&self, t: &TTuple) -> String {
232 let mut values = String::new();
233
234 for (n, item) in t.items.iter().enumerate() {
235 let is_last = n == t.items.len() - 1;
236
237 let value = match item {
238 TupleItem::TPrimitive(p) => self.gen_primitive_type(p).to_string(),
239 TupleItem::TOption(o) => self.gen_option(o),
240 };
241
242 values.push_str(&value);
243 if !is_last {
244 values.push_str(", ");
245 }
246 }
247
248 format!("({})", values)
249 }
250
251 fn gen_primitive_type(&self, ty: &TPrimitive) -> String {
252 match ty {
253 TPrimitive::String => "string".to_string(),
254 TPrimitive::Tbool => "bool".to_string(),
255 TPrimitive::Ti64 => "int".to_string(),
256 TPrimitive::Tf64 => "float".to_string(),
257 TPrimitive::Ti32 => "int".to_string(),
258 TPrimitive::Tu32 => "int".to_string(),
259 TPrimitive::Tusize => "int".to_string(),
260 TPrimitive::THardcoded(s) => s.to_string(),
261 TPrimitive::TVec(v) => self.gen_vec(v),
262 TPrimitive::TMap(m) => self.gen_map(m),
263 TPrimitive::TOption(o) => self.gen_option(o),
264 TPrimitive::TDifferentPerLanguage { hack, .. } => self.gen_primitive_type(&hack),
265 TPrimitive::TGeneric(g) => self.gen_generic(g),
266 TPrimitive::TReference(r) => {
267 format!(
268 "{}{}",
269 self.gen_namespaced_name(r.get_name()),
270 shared::generic_params(&r.generic_params, |g| self.gen_generic(g))
271 )
272 }
273 }
274 }
275
276 fn gen_namespaced_name(&self, name: &str) -> String {
278 format!("{}{}", self.namespace, name)
279 }
280
281 fn gen_generic(&self, g: &TGeneric) -> String {
282 match g {
283 TGeneric::TDefinition { name, .. } => name.to_string(),
284 TGeneric::TReference(r, ..) => {
285 self.gen_primitive_type(&TPrimitive::TReference(r.clone()))
286 }
287 }
288 }
289
290 fn gen_code_block(&self, b: &CodeBlock) -> String {
291 match b {
292 CodeBlock::Rust(_) => String::new(),
293 CodeBlock::Flow(_) => String::new(),
294 CodeBlock::Hack(lines) => lines
295 .iter()
296 .map(|s| s.to_string())
297 .collect::<Vec<_>>()
298 .join("\n"),
299 }
300 }
301}