diff --git a/serde-generate/src/config.rs b/serde-generate/src/config.rs index c964bb9de..a206bd668 100644 --- a/serde-generate/src/config.rs +++ b/serde-generate/src/config.rs @@ -1,7 +1,7 @@ // Copyright (c) Facebook, Inc. and its affiliates // SPDX-License-Identifier: MIT OR Apache-2.0 -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::{BTreeMap, BTreeSet, HashMap}; /// Code generation options meant to be supported by all languages. #[derive(Clone, Debug)] @@ -12,7 +12,7 @@ pub struct CodeGeneratorConfig { pub external_definitions: ExternalDefinitions, pub comments: DocComments, pub custom_code: CustomCode, - pub c_style_enums: bool, + pub enums: EnumConfig, pub package_manifest: bool, } @@ -36,6 +36,18 @@ pub type CustomCode = std::collections::BTreeMap< /* custom code */ String, >; +/// Configure the generation style of enums. +#[derive(Clone, Debug)] +pub struct EnumConfig { + // Generate [enum] if `true` or classes if `false` + pub c_style: bool, + // Generate sealed class if `true` or abstract class if `false` + pub sealed: bool, + // If `sealed_enums` is true then the listed names will be abstract, + // if `sealed_enums` is false then the listed names will be sealed. + pub output_type: HashMap<&'static str, &'static str>, +} + /// How to copy generated source code and available runtimes for a given language. pub trait SourceInstaller { type Error; @@ -67,7 +79,11 @@ impl CodeGeneratorConfig { external_definitions: BTreeMap::new(), comments: BTreeMap::new(), custom_code: BTreeMap::new(), - c_style_enums: false, + enums: EnumConfig { + c_style: false, + sealed: false, + output_type: HashMap::new(), + }, package_manifest: true, } } @@ -116,7 +132,23 @@ impl CodeGeneratorConfig { /// Generate C-style enums (without variant data) as the target language /// native enum type in supported languages. pub fn with_c_style_enums(mut self, c_style_enums: bool) -> Self { - self.c_style_enums = c_style_enums; + self.enums.c_style = c_style_enums; + self + } + + /// For complex enums generate sealed enum classes instead of abstract classes + pub fn with_sealed_enums(mut self, sealed: bool) -> Self { + self.enums.sealed = sealed; + self + } + + /// Generate abstract or sealed classes for data enums based on `with_sealed_enums` + /// but allow item by item overrides. + pub fn with_enum_type_overrides( + mut self, + overrides: HashMap<&'static str, &'static str>, + ) -> Self { + self.enums.output_type = overrides; self } diff --git a/serde-generate/src/cpp.rs b/serde-generate/src/cpp.rs index 3bd0582ba..48763d89d 100644 --- a/serde-generate/src/cpp.rs +++ b/serde-generate/src/cpp.rs @@ -40,7 +40,7 @@ struct CppEmitter<'a, T> { impl<'a> CodeGenerator<'a> { /// Create a C++ code generator for the given config. pub fn new(config: &'a CodeGeneratorConfig) -> Self { - if config.c_style_enums { + if config.enums.c_style { panic!("C++ does not support generating c-style enums"); } let mut external_qualified_names = HashMap::new(); diff --git a/serde-generate/src/csharp.rs b/serde-generate/src/csharp.rs index ccde48d42..026cb192b 100644 --- a/serde-generate/src/csharp.rs +++ b/serde-generate/src/csharp.rs @@ -83,7 +83,7 @@ impl<'a> CodeGenerator<'a> { // When we find an enum with all Unit variants, we ser/de as a regular C# enum. // We keep track of this so we can use the enum's extension class for ser/de since enums can't have methods. let mut cstyle_enum_names = Vec::new(); - if self.config.c_style_enums { + if self.config.enums.c_style { for (name, format) in registry { if let ContainerFormat::Enum(variants) = format { if variants.values().all(|f| f.value == VariantFormat::Unit) { @@ -1214,7 +1214,7 @@ impl crate::SourceInstaller for Installer { -{} +{} "#, diff --git a/serde-generate/src/dart.rs b/serde-generate/src/dart.rs index bff5e80cd..6db29d456 100644 --- a/serde-generate/src/dart.rs +++ b/serde-generate/src/dart.rs @@ -192,7 +192,7 @@ where // in Dart enums cannot have a static method added to them // yet so we must call the extension class instead fn get_class(&self, name: &str) -> String { - if self.generator.config.c_style_enums { + if self.generator.config.enums.c_style { use ContainerFormat::Enum; match self.get_field_container_type(name) { // if we have an enum AND all of that enum's members are Unit @@ -583,12 +583,19 @@ return obj; Struct(fields) => fields.clone(), Enum(variants) => { // When we find an enum with all Unit variants, we ser/de as a regular Dart enum. - if self.generator.config.c_style_enums + if ((self.generator.config.enums.c_style + && !self.generator.config.enums.output_type.contains_key(name)) + || self.generator.config.enums.output_type.get(name) == Some(&"enum")) && variants.values().all(|f| f.value == VariantFormat::Unit) { self.output_enum_container(name, variants)?; + } else if (self.generator.config.enums.sealed + && !self.generator.config.enums.output_type.contains_key(name)) + || self.generator.config.enums.output_type.get(name) == Some(&"sealed") + { + self.output_enum_class_container(name, variants, "sealed")?; } else { - self.output_enum_class_container(name, variants)?; + self.output_enum_class_container(name, variants, "abstract")?; } return Ok(()); } @@ -1052,12 +1059,14 @@ switch (this) {{"#, &mut self, name: &str, variants: &BTreeMap>, + class_type: &str, ) -> Result<()> { writeln!(self.out)?; self.output_comment(name)?; writeln!( self.out, - "abstract class {} {{", + "{} class {} {{", + class_type, self.quote_qualified_name(name) )?; self.enter_class(name); diff --git a/serde-generate/src/golang.rs b/serde-generate/src/golang.rs index 1a9128b16..aad179f4a 100644 --- a/serde-generate/src/golang.rs +++ b/serde-generate/src/golang.rs @@ -39,7 +39,7 @@ struct GoEmitter<'a, T> { impl<'a> CodeGenerator<'a> { /// Create a Go code generator for the given config. pub fn new(config: &'a CodeGeneratorConfig) -> Self { - if config.c_style_enums { + if config.enums.c_style { panic!("Go does not support generating c-style enums"); } let mut external_qualified_names = HashMap::new(); diff --git a/serde-generate/src/java.rs b/serde-generate/src/java.rs index f800812be..b885c9b77 100644 --- a/serde-generate/src/java.rs +++ b/serde-generate/src/java.rs @@ -42,7 +42,7 @@ struct JavaEmitter<'a, T> { impl<'a> CodeGenerator<'a> { /// Create a Java code generator for the given config. pub fn new(config: &'a CodeGeneratorConfig) -> Self { - if config.c_style_enums { + if config.enums.c_style { panic!("Java does not support generating c-style enums"); } let mut external_qualified_names = HashMap::new(); diff --git a/serde-generate/src/ocaml.rs b/serde-generate/src/ocaml.rs index f3a2a2f4c..5f44c4eb7 100644 --- a/serde-generate/src/ocaml.rs +++ b/serde-generate/src/ocaml.rs @@ -30,7 +30,7 @@ struct OCamlEmitter<'a, T> { impl<'a> CodeGenerator<'a> { pub fn new(config: &'a CodeGeneratorConfig) -> Self { - if config.c_style_enums { + if config.enums.c_style { panic!("OCaml does not support generating c-style enums"); } Self { diff --git a/serde-generate/src/python3.rs b/serde-generate/src/python3.rs index af5eaf81b..abb054d30 100644 --- a/serde-generate/src/python3.rs +++ b/serde-generate/src/python3.rs @@ -37,7 +37,7 @@ struct PythonEmitter<'a, T> { impl<'a> CodeGenerator<'a> { /// Create a Python code generator for the given config. pub fn new(config: &'a CodeGeneratorConfig) -> Self { - if config.c_style_enums { + if config.enums.c_style { panic!("Python 3 does not support generating c-style enums"); } let mut external_qualified_names = HashMap::new(); diff --git a/serde-generate/src/solidity.rs b/serde-generate/src/solidity.rs index 4d18c07e3..0bbd45f5a 100644 --- a/serde-generate/src/solidity.rs +++ b/serde-generate/src/solidity.rs @@ -1455,7 +1455,7 @@ impl SolRegistry { impl<'a> CodeGenerator<'a> { /// Create a solidity code generator for the given config. pub fn new(config: &'a CodeGeneratorConfig) -> Self { - if config.c_style_enums { + if config.enums.c_style { panic!("Solidity does not support generating c-style enums"); } Self { config } diff --git a/serde-generate/src/swift.rs b/serde-generate/src/swift.rs index b80fb8154..1035e4640 100644 --- a/serde-generate/src/swift.rs +++ b/serde-generate/src/swift.rs @@ -39,7 +39,7 @@ struct SwiftEmitter<'a, T> { impl<'a> CodeGenerator<'a> { /// Create a Swift code generator for the given config. pub fn new(config: &'a CodeGeneratorConfig) -> Self { - if config.c_style_enums { + if config.enums.c_style { panic!("Swift does not support generating c-style enums"); } let mut external_qualified_names = HashMap::new(); diff --git a/serde-generate/src/typescript.rs b/serde-generate/src/typescript.rs index be8889d61..efeee7922 100644 --- a/serde-generate/src/typescript.rs +++ b/serde-generate/src/typescript.rs @@ -38,7 +38,7 @@ struct TypeScriptEmitter<'a, T> { impl<'a> CodeGenerator<'a> { /// Create a TypeScript code generator for the given config. pub fn new(config: &'a CodeGeneratorConfig) -> Self { - if config.c_style_enums { + if config.enums.c_style { panic!("TypeScript does not support generating c-style enums"); } let mut external_qualified_names = HashMap::new();