use crate::Lang;
use crate::TypeGenConfig;
use crate::docs::DocFormat;
use crate::docs::render_docs;
use crate::types::EnumDecl;
use crate::types::EnumVariant;
use crate::types::PrimitiveTypeRef;
use crate::types::StructDecl;
use crate::types::TupleStructDecl;
use crate::types::TypeGenDecl;
use crate::types::TypeGenGeneratedType;
use crate::types::TypeRef;
pub struct FlowGenerator;
impl FlowGenerator {
pub fn generate_flow(config: &TypeGenConfig, generated_type: &TypeGenGeneratedType) -> String {
if let Some(ref overrides) = generated_type.overrides {
if let Some(ref flow_override) = overrides.flow {
let type_name =
config.get_type_name(&generated_type.original_type_name, Lang::Flow);
let docs = render_docs(&generated_type.docs, DocFormat::TwoSlash, 0);
return format!("{}export type {} = {};\n", docs, type_name, flow_override);
}
}
let type_name = config.get_type_name(&generated_type.original_type_name, Lang::Flow);
let mut imports = std::collections::HashSet::new();
let type_code = match &generated_type.declaration {
TypeGenDecl::StructDecl(struct_decl) => Self::generate_struct_flow(
&type_name,
&generated_type.docs,
struct_decl,
&mut imports,
),
TypeGenDecl::TupleStructDecl(tuple_struct_decl) => Self::generate_tuple_struct_flow(
&type_name,
&generated_type.docs,
tuple_struct_decl,
&mut imports,
),
TypeGenDecl::EnumDecl(enum_decl) => {
Self::generate_enum_flow(&type_name, &generated_type.docs, enum_decl, &mut imports)
}
TypeGenDecl::Null => Self::generate_null_flow(&type_name, &generated_type.docs),
};
let mut result = String::new();
if !imports.is_empty() {
let mut sorted_imports: Vec<_> = imports.into_iter().collect();
sorted_imports.sort();
for import_original_type_name in sorted_imports {
result.push_str(&format!(
"import type {{ {} }} from './{}';\n",
config.get_type_name(&import_original_type_name, Lang::Flow),
config
.make_file_name(&import_original_type_name, Lang::Flow)
.display()
));
}
result.push('\n');
}
result.push_str(&type_code);
result
}
fn generate_struct_flow(
type_name: &str,
docs: &Option<String>,
struct_decl: &StructDecl,
imports: &mut std::collections::HashSet<String>,
) -> String {
let mut result = String::new();
result.push_str(&render_docs(docs, DocFormat::Block, 0));
result.push_str(&format!("export type {} = {{\n", type_name));
for field in &struct_decl.fields {
result.push_str(&render_docs(&field.docs, DocFormat::Block, 2));
let field_name = &field.field_name;
let question_mark = matches!(field.type_ref, TypeRef::Option(_))
.then(|| "?")
.unwrap_or_default();
let flow_type = Self::resolve_flow_type(&field.type_ref, imports);
result.push_str(&format!(
" {}{}: {},\n",
field_name, question_mark, flow_type
));
}
result.push_str("};");
result
}
fn generate_tuple_struct_flow(
type_name: &str,
docs: &Option<String>,
tuple_struct_decl: &TupleStructDecl,
imports: &mut std::collections::HashSet<String>,
) -> String {
let mut result = String::new();
result.push_str(&render_docs(docs, DocFormat::Block, 0));
if tuple_struct_decl.fields.len() == 1 {
let inner_type = Self::resolve_flow_type(&tuple_struct_decl.fields[0], imports);
result.push_str(&format!("export type {} = {};", type_name, inner_type));
} else {
let types: Vec<String> = tuple_struct_decl
.fields
.iter()
.map(|type_ref| Self::resolve_flow_type(type_ref, imports))
.collect();
result.push_str(&format!(
"export type {} = [{}];",
type_name,
types.join(", ")
));
}
result
}
fn generate_enum_flow(
type_name: &str,
docs: &Option<String>,
enum_decl: &EnumDecl,
imports: &mut std::collections::HashSet<String>,
) -> String {
let mut result = String::new();
result.push_str(&render_docs(docs, DocFormat::Block, 0));
let is_simple_enum = enum_decl
.variants
.iter()
.all(|variant| matches!(variant, EnumVariant::Unit { .. }));
if is_simple_enum {
let variants: Vec<String> = enum_decl
.variants
.iter()
.map(|variant| match variant {
EnumVariant::Unit { name, .. } => format!("\"{}\"", name),
_ => unreachable!(), })
.collect();
result.push_str(&format!(
"export type {} = {};",
type_name,
variants.join(" | ")
));
} else {
let variants: Vec<String> = enum_decl
.variants
.iter()
.map(|variant| match variant {
EnumVariant::Unit { name, docs } => {
let mut variant_result = String::new();
variant_result.push_str(&render_docs(docs, DocFormat::Block, 2));
variant_result.push_str(&format!(" \"{}\"", name));
variant_result
}
EnumVariant::Newtype {
name,
docs,
field_type,
} => {
let mut variant_result = String::new();
variant_result.push_str(&render_docs(docs, DocFormat::Block, 2));
let flow_type = Self::resolve_flow_type(field_type, imports);
variant_result.push_str(&format!(" {{ \"{}\": {} }}", name, flow_type));
variant_result
}
EnumVariant::Tuple { name, docs, fields } => {
let mut variant_result = String::new();
variant_result.push_str(&render_docs(docs, DocFormat::Block, 2));
let field_types: Vec<String> = fields
.iter()
.map(|field_type| Self::resolve_flow_type(field_type, imports))
.collect();
variant_result.push_str(&format!(
" {{ \"{}\": [{}] }}",
name,
field_types.join(", ")
));
variant_result
}
EnumVariant::Struct { name, docs, fields } => {
let mut variant_result = String::new();
variant_result.push_str(&render_docs(docs, DocFormat::Block, 2));
let struct_fields: Vec<String> = fields
.iter()
.map(|field| {
let field_name = &field.field_name;
let flow_type = Self::resolve_flow_type(&field.type_ref, imports);
format!("{}: {}", field_name, flow_type)
})
.collect();
variant_result.push_str(&format!(
" {{ \"{}\": {{ {} }} }}",
name,
struct_fields.join(", ")
));
variant_result
}
})
.collect();
result.push_str(&format!(
"export type {} =\n{};",
type_name,
variants.join(" |\n")
));
}
result
}
fn resolve_flow_type(
type_ref: &TypeRef,
imports: &mut std::collections::HashSet<String>,
) -> String {
match type_ref {
TypeRef::Primitive(prim) => match prim {
PrimitiveTypeRef::String => "string".to_string(),
PrimitiveTypeRef::Bool => "boolean".to_string(),
PrimitiveTypeRef::I8
| PrimitiveTypeRef::I16
| PrimitiveTypeRef::I32
| PrimitiveTypeRef::I64
| PrimitiveTypeRef::I128
| PrimitiveTypeRef::ISize
| PrimitiveTypeRef::U8
| PrimitiveTypeRef::U16
| PrimitiveTypeRef::U32
| PrimitiveTypeRef::U64
| PrimitiveTypeRef::U128
| PrimitiveTypeRef::USize
| PrimitiveTypeRef::F32
| PrimitiveTypeRef::F64 => "number".to_string(),
},
TypeRef::TypeReference(name) => {
imports.insert(name.clone());
name.clone()
}
TypeRef::Option(inner) => {
format!("?{}", Self::resolve_flow_type(inner, imports))
}
TypeRef::Vec(inner) | TypeRef::Set(inner) => {
format!("Array<{}>", Self::resolve_flow_type(inner, imports))
}
TypeRef::Array { element_type, size } => {
let element_flow = Self::resolve_flow_type(element_type, imports);
let elements = (0..*size).map(|_| element_flow.clone()).collect::<Vec<_>>();
format!("[{}]", elements.join(", "))
}
TypeRef::Map { key, value } => {
format!(
"{{ [key: {}]: {} }}",
Self::resolve_flow_type(key, imports),
Self::resolve_flow_type(value, imports)
)
}
}
}
fn generate_null_flow(type_name: &str, docs: &Option<String>) -> String {
let mut result = String::new();
result.push_str(&render_docs(docs, DocFormat::Block, 0));
result.push_str(&format!("export type {} = null;", type_name));
result
}
}