Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add C# Support #155

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions cli/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub const ARG_SCALA_PACKAGE: &str = "SCALAPACKAGE";
pub const ARG_SCALA_MODULE_NAME: &str = "SCALAMODULENAME";
#[cfg(feature = "go")]
pub const ARG_GO_PACKAGE: &str = "GOPACKAGE";
pub const ARG_CSHARP_NAMESPACE: &str = "CSHARP_NAMESPACE";
pub const ARG_CONFIG_FILE_NAME: &str = "CONFIGFILENAME";
pub const ARG_GENERATE_CONFIG: &str = "generate-config-file";
pub const ARG_OUTPUT_FILE: &str = "output-file";
Expand All @@ -20,10 +21,10 @@ pub const ARG_FOLLOW_LINKS: &str = "follow-links";
pub const ARG_TARGET_OS: &str = "target_os";

#[cfg(feature = "go")]
const AVAILABLE_LANGUAGES: [&str; 5] = ["kotlin", "scala", "swift", "typescript", "go"];
const AVAILABLE_LANGUAGES: [&str; 6] = ["kotlin", "scala", "swift", "typescript", "go", "csharp"];

#[cfg(not(feature = "go"))]
const AVAILABLE_LANGUAGES: [&str; 4] = ["kotlin", "scala", "swift", "typescript"];
const AVAILABLE_LANGUAGES: [&str; 5] = ["kotlin", "scala", "swift", "typescript", "csharp"];

/// Parse command line arguments.
pub(crate) fn build_command() -> Command<'static> {
Expand Down Expand Up @@ -97,7 +98,13 @@ pub(crate) fn build_command() -> Command<'static> {
.takes_value(true)
.required(false),
)

.arg(
Arg::new(ARG_CSHARP_NAMESPACE)
.long("namespace")
.help("C# namespace")
.takes_value(true)
.required(false)
)
.arg(
Arg::new(ARG_CONFIG_FILE_NAME)
.short('c')
Expand Down
9 changes: 9 additions & 0 deletions cli/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ pub struct GoParams {
pub uppercase_acronyms: Vec<String>,
}

#[derive(Default, Serialize, Deserialize, PartialEq, Eq, Debug)]
#[serde(default)]
pub struct CSharpParams {
pub type_mappings: HashMap<String, String>,
pub namespace: String,
pub without_csharp_naming_convention: bool,
}

/// The paramters that are used to configure the behaviour of typeshare
/// from the configuration file `typeshare.toml`
#[derive(Serialize, Deserialize, Default, Debug, PartialEq)]
Expand All @@ -66,6 +74,7 @@ pub(crate) struct Config {
pub go: GoParams,
#[serde(skip)]
pub target_os: Option<String>,
pub csharp: CSharpParams,
}

pub(crate) fn store_config(config: &Config, file_path: Option<&str>) -> anyhow::Result<()> {
Expand Down
18 changes: 14 additions & 4 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

use anyhow::{anyhow, Context};
use args::{
build_command, ARG_CONFIG_FILE_NAME, ARG_FOLLOW_LINKS, ARG_GENERATE_CONFIG, ARG_JAVA_PACKAGE,
ARG_KOTLIN_PREFIX, ARG_MODULE_NAME, ARG_OUTPUT_FOLDER, ARG_SCALA_MODULE_NAME,
ARG_SCALA_PACKAGE, ARG_SWIFT_PREFIX, ARG_TARGET_OS, ARG_TYPE,
build_command, ARG_CONFIG_FILE_NAME, ARG_CSHARP_NAMESPACE, ARG_FOLLOW_LINKS,
ARG_GENERATE_CONFIG, ARG_JAVA_PACKAGE, ARG_KOTLIN_PREFIX, ARG_MODULE_NAME, ARG_OUTPUT_FOLDER,
ARG_SCALA_MODULE_NAME, ARG_SCALA_PACKAGE, ARG_SWIFT_PREFIX, ARG_TARGET_OS, ARG_TYPE,
};
use clap::ArgMatches;
use config::Config;
Expand All @@ -17,7 +17,7 @@ use std::collections::{BTreeMap, HashMap};
use typeshare_core::language::Go;
use typeshare_core::{
language::{
CrateName, GenericConstraints, Kotlin, Language, Scala, SupportedLanguage, Swift,
CSharp, CrateName, GenericConstraints, Kotlin, Language, Scala, SupportedLanguage, Swift,
TypeScript,
},
parser::ParsedData,
Expand Down Expand Up @@ -175,6 +175,12 @@ fn language(
type_mappings: config.typescript.type_mappings,
..Default::default()
}),
SupportedLanguage::CSharp => Box::new(CSharp {
namespace: config.csharp.namespace,
type_mappings: config.csharp.type_mappings,
without_csharp_naming_convention: config.csharp.without_csharp_naming_convention,
..Default::default()
}),
#[cfg(feature = "go")]
SupportedLanguage::Go => Box::new(Go {
package: config.go.package,
Expand Down Expand Up @@ -215,6 +221,10 @@ fn override_configuration(mut config: Config, options: &ArgMatches) -> Config {
config.scala.module_name = scala_module_name.to_string();
}

if let Some(csharp_namespace) = options.value_of(ARG_CSHARP_NAMESPACE) {
config.csharp.namespace = csharp_namespace.to_string();
}

#[cfg(feature = "go")]
if let Some(go_package) = options.value_of(args::ARG_GO_PACKAGE) {
config.go.package = go_package.to_string();
Expand Down
1 change: 1 addition & 0 deletions cli/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ fn output_file_name(language_type: SupportedLanguage, crate_name: &CrateName) ->
SupportedLanguage::Scala => snake_case(),
SupportedLanguage::Swift => pascal_case(),
SupportedLanguage::TypeScript => snake_case(),
SupportedLanguage::CSharp => pascal_case(),
}
}

Expand Down
45 changes: 45 additions & 0 deletions core/data/tests/anonymous_struct_with_rename/output.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#nullable enable

using System.Reflection;
using JsonSubTypes;
using Newtonsoft.Json;
using System.Runtime.Serialization;

/** Generated type representing the anonymous struct variant `List` of the `AnonymousStructWithRename` Rust enum */
public class AnonymousStructWithRenameListInner {
[JsonProperty(Required = Required.Always)]
public IEnumerable<string> list { get; set; }
}

/** Generated type representing the anonymous struct variant `LongFieldNames` of the `AnonymousStructWithRename` Rust enum */
public class AnonymousStructWithRenameLongFieldNamesInner {
[JsonProperty(Required = Required.Always)]
public string some_long_field_name { get; set; }
[JsonProperty(Required = Required.Always)]
public bool and { get; set; }
[JsonProperty(Required = Required.Always)]
public IEnumerable<string> but_one_more { get; set; }
}

/** Generated type representing the anonymous struct variant `KebabCase` of the `AnonymousStructWithRename` Rust enum */
public class AnonymousStructWithRenameKebabCaseInner {
[JsonProperty(Required = Required.Always)]
public IEnumerable<string> another-list { get; set; }
[JsonProperty(Required = Required.Always)]
public string camelCaseStringField { get; set; }
[JsonProperty(Required = Required.Always)]
public bool something-else { get; set; }
}

[JsonConverter(typeof(JsonSubtypes), "type")]
[JsonSubtypes.KnownSubType(typeof(List), "list")]
[JsonSubtypes.KnownSubType(typeof(LongFieldNames), "longFieldNames")]
[JsonSubtypes.KnownSubType(typeof(KebabCase), "kebabCase")]
public abstract record AnonymousStructWithRename
{
public record list(AnonymousStructWithRenameListInner Content): AnonymousStructWithRename();
public record longFieldNames(AnonymousStructWithRenameLongFieldNamesInner Content): AnonymousStructWithRename();
public record kebabCase(AnonymousStructWithRenameKebabCaseInner Content): AnonymousStructWithRename();
}


30 changes: 30 additions & 0 deletions core/data/tests/can_apply_prefix_correctly/output.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#nullable enable

using System.Reflection;
using JsonSubTypes;
using Newtonsoft.Json;
using System.Runtime.Serialization;

public class ItemDetailsFieldValue {
[JsonProperty(Required = Required.Always)]
public string Hello { get; set; }
}

[JsonConverter(typeof(JsonSubtypes), "t")]
[JsonSubtypes.KnownSubType(typeof(String), "String")]
[JsonSubtypes.KnownSubType(typeof(Number), "Number")]
[JsonSubtypes.KnownSubType(typeof(NumberArray), "NumberArray")]
[JsonSubtypes.KnownSubType(typeof(ReallyCoolType), "ReallyCoolType")]
[JsonSubtypes.KnownSubType(typeof(ArrayReallyCoolType), "ArrayReallyCoolType")]
[JsonSubtypes.KnownSubType(typeof(DictionaryReallyCoolType), "DictionaryReallyCoolType")]
public abstract record AdvancedColors
{
public record String(string C) : AdvancedColors();
public record Number(int C) : AdvancedColors();
public record NumberArray(IEnumerable<int> C) : AdvancedColors();
public record ReallyCoolType(ItemDetailsFieldValue C) : AdvancedColors();
public record ArrayReallyCoolType(IEnumerable<ItemDetailsFieldValue> C) : AdvancedColors();
public record DictionaryReallyCoolType(IDictionary<string, ItemDetailsFieldValue> C) : AdvancedColors();
}


48 changes: 48 additions & 0 deletions core/data/tests/can_generate_algebraic_enum/output.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#nullable enable

using System.Reflection;
using JsonSubTypes;
using Newtonsoft.Json;
using System.Runtime.Serialization;

namespace Company.Domain.Models;

/** Struct comment */
public class ItemDetailsFieldValue {
}

/** Enum comment */
[JsonConverter(typeof(JsonSubtypes), "type")]
[JsonSubtypes.KnownSubType(typeof(String), "String")]
[JsonSubtypes.KnownSubType(typeof(Number), "Number")]
[JsonSubtypes.KnownSubType(typeof(UnsignedNumber), "UnsignedNumber")]
[JsonSubtypes.KnownSubType(typeof(NumberArray), "NumberArray")]
[JsonSubtypes.KnownSubType(typeof(ReallyCoolType), "ReallyCoolType")]
public abstract record AdvancedColors
{
/** This is a case comment */
public record String(string Content) : AdvancedColors();
public record Number(int Content) : AdvancedColors();
public record UnsignedNumber(uint Content) : AdvancedColors();
public record NumberArray(IEnumerable<int> Content) : AdvancedColors();
/** Comment on the last element */
public record ReallyCoolType(ItemDetailsFieldValue Content) : AdvancedColors();
}


[JsonConverter(typeof(JsonSubtypes), "type")]
[JsonSubtypes.KnownSubType(typeof(String), "string")]
[JsonSubtypes.KnownSubType(typeof(Number), "number")]
[JsonSubtypes.KnownSubType(typeof(NumberArray), "number-array")]
[JsonSubtypes.KnownSubType(typeof(ReallyCoolType), "really-cool-type")]
public abstract record AdvancedColors2
{
/** This is a case comment */
public record String(string Content) : AdvancedColors2();
public record Number(int Content) : AdvancedColors2();
public record NumberArray(IEnumerable<int> Content) : AdvancedColors2();
/** Comment on the last element */
public record ReallyCoolType(ItemDetailsFieldValue Content) : AdvancedColors2();
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#nullable enable

using System.Reflection;
using JsonSubTypes;
using Newtonsoft.Json;
using System.Runtime.Serialization;

[JsonConverter(typeof(JsonSubtypes), "type")]
[JsonSubtypes.KnownSubType(typeof(A), "A")]
[JsonSubtypes.KnownSubType(typeof(C), "C")]
public abstract record SomeEnum
{
public record A(): SomeEnum();
public record C(int Content) : SomeEnum();
}


18 changes: 18 additions & 0 deletions core/data/tests/can_generate_bare_string_enum/output.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#nullable enable

using System.Reflection;
using JsonSubTypes;
using Newtonsoft.Json;
using System.Runtime.Serialization;

/** This is a comment. */
public enum Colors
{
Red,

Blue,

Green,

}

20 changes: 20 additions & 0 deletions core/data/tests/can_generate_empty_algebraic_enum/output.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#nullable enable

using System.Reflection;
using JsonSubTypes;
using Newtonsoft.Json;
using System.Runtime.Serialization;

public class AddressDetails {
}

[JsonConverter(typeof(JsonSubtypes), "type")]
[JsonSubtypes.KnownSubType(typeof(FixedAddress), "FixedAddress")]
[JsonSubtypes.KnownSubType(typeof(NoFixedAddress), "NoFixedAddress")]
public abstract record Address
{
public record FixedAddress(AddressDetails Content) : Address();
public record NoFixedAddress(): Address();
}


76 changes: 76 additions & 0 deletions core/data/tests/can_generate_generic_enum/output.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#nullable enable

using System.Reflection;
using JsonSubTypes;
using Newtonsoft.Json;
using System.Runtime.Serialization;

[JsonConverter(typeof(JsonSubtypes), "type")]
[JsonSubtypes.KnownSubType(typeof(VariantA), "VariantA")]
[JsonSubtypes.KnownSubType(typeof(VariantB), "VariantB")]
public abstract record GenericEnum<TA, TB>
{
public record VariantA(TA Content) : GenericEnum<TA, TB>();
public record VariantB(TB Content) : GenericEnum<TA, TB>();
}


public class StructUsingGenericEnum {
[JsonProperty(Required = Required.Always)]
public GenericEnum<string, short> EnumField { get; set; }
}

[JsonConverter(typeof(JsonSubtypes), "type")]
[JsonSubtypes.KnownSubType(typeof(VariantC), "VariantC")]
[JsonSubtypes.KnownSubType(typeof(VariantD), "VariantD")]
[JsonSubtypes.KnownSubType(typeof(VariantE), "VariantE")]
public abstract record GenericEnumUsingGenericEnum<T>
{
public record VariantC(GenericEnum<T, T> Content) : GenericEnumUsingGenericEnum<T>();
public record VariantD(GenericEnum<string, IDictionary<string, T>> Content) : GenericEnumUsingGenericEnum<T>();
public record VariantE(GenericEnum<string, uint> Content) : GenericEnumUsingGenericEnum<T>();
}


/** Generated type representing the anonymous struct variant `VariantF` of the `GenericEnumsUsingStructVariants` Rust enum */
public class GenericEnumsUsingStructVariantsVariantFInner<T> {
[JsonProperty(Required = Required.Always)]
public T Action { get; set; }
}

/** Generated type representing the anonymous struct variant `VariantG` of the `GenericEnumsUsingStructVariants` Rust enum */
public class GenericEnumsUsingStructVariantsVariantGInner<T, TU> {
[JsonProperty(Required = Required.Always)]
public T Action { get; set; }
[JsonProperty(Required = Required.Always)]
public TU Response { get; set; }
}

/** Generated type representing the anonymous struct variant `VariantH` of the `GenericEnumsUsingStructVariants` Rust enum */
public class GenericEnumsUsingStructVariantsVariantHInner {
[JsonProperty(Required = Required.Always)]
public int NonGeneric { get; set; }
}

/** Generated type representing the anonymous struct variant `VariantI` of the `GenericEnumsUsingStructVariants` Rust enum */
public class GenericEnumsUsingStructVariantsVariantIInner<T, TU> {
[JsonProperty(Required = Required.Always)]
public IEnumerable<T> Vec { get; set; }
[JsonProperty(Required = Required.Always)]
public MyType<T, TU> Action { get; set; }
}

[JsonConverter(typeof(JsonSubtypes), "type")]
[JsonSubtypes.KnownSubType(typeof(VariantF), "VariantF")]
[JsonSubtypes.KnownSubType(typeof(VariantG), "VariantG")]
[JsonSubtypes.KnownSubType(typeof(VariantH), "VariantH")]
[JsonSubtypes.KnownSubType(typeof(VariantI), "VariantI")]
public abstract record GenericEnumsUsingStructVariants<T, TU>
{
public record VariantF(GenericEnumsUsingStructVariantsVariantFInner<T> Content): GenericEnumsUsingStructVariants<T, TU>();
public record VariantG(GenericEnumsUsingStructVariantsVariantGInner<T, TU> Content): GenericEnumsUsingStructVariants<T, TU>();
public record VariantH(GenericEnumsUsingStructVariantsVariantHInner Content): GenericEnumsUsingStructVariants<T, TU>();
public record VariantI(GenericEnumsUsingStructVariantsVariantIInner<T, TU> Content): GenericEnumsUsingStructVariants<T, TU>();
}


12 changes: 12 additions & 0 deletions core/data/tests/can_generate_readonly_fields/output.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#nullable enable

using System.Reflection;
using JsonSubTypes;
using Newtonsoft.Json;
using System.Runtime.Serialization;

public class SomeStruct {
[JsonProperty(Required = Required.Always)]
public uint FieldA { get; set; }
}

Loading