Skip to content

Commit

Permalink
[C#] Idiomatic formatting (#1083)
Browse files Browse the repository at this point in the history
* minor improvement to reduce unused using statements

* use more idiomatic c# casing and bracket style

* index on tidy-usings: f38910b use more idiomatic c# casing and bracket style

* cargo fmr

* remove TODO comment

---------

Co-authored-by: Timmy Silesmo <[email protected]>
  • Loading branch information
yowl and silesmo authored Nov 20, 2024
1 parent 1739caf commit 563956d
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 70 deletions.
188 changes: 141 additions & 47 deletions crates/csharp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ impl WorldGenerator for CSharp {
{access} readonly struct None {{}}
[StructLayout(LayoutKind.Sequential)]
{access} readonly struct Result<Ok, Err>
{access} readonly struct Result<TOk, TErr>
{{
{access} readonly byte Tag;
private readonly object value;
Expand All @@ -472,43 +472,50 @@ impl WorldGenerator for CSharp {
this.value = value;
}}
{access} static Result<Ok, Err> ok(Ok ok)
{access} static Result<TOk, TErr> Ok(TOk ok)
{{
return new Result<Ok, Err>(OK, ok!);
return new Result<TOk, TErr>(Tags.Ok, ok!);
}}
{access} static Result<Ok, Err> err(Err err)
{access} static Result<TOk, TErr> Err(TErr err)
{{
return new Result<Ok, Err>(ERR, err!);
return new Result<TOk, TErr>(Tags.Err, err!);
}}
{access} bool IsOk => Tag == OK;
{access} bool IsErr => Tag == ERR;
{access} bool IsOk => Tag == Tags.Ok;
{access} bool IsErr => Tag == Tags.Err;
{access} Ok AsOk
{access} TOk AsOk
{{
get
{{
if (Tag == OK)
return (Ok)value;
else
throw new ArgumentException("expected OK, got " + Tag);
if (Tag == Tags.Ok)
{{
return (TOk)value;
}}
throw new ArgumentException("expected k, got " + Tag);
}}
}}
{access} Err AsErr
{access} TErr AsErr
{{
get
{{
if (Tag == ERR)
return (Err)value;
else
throw new ArgumentException("expected ERR, got " + Tag);
if (Tag == Tags.Err)
{{
return (TErr)value;
}}
throw new ArgumentException("expected Err, got " + Tag);
}}
}}
{access} const byte OK = 0;
{access} const byte ERR = 1;
{access} class Tags
{{
{access} const byte Ok = 0;
{access} const byte Err = 1;
}}
}}
"#,
)
Expand Down Expand Up @@ -1144,6 +1151,8 @@ impl InterfaceGenerator<'_> {

let import_name = &func.name;

self.gen.require_using("System.Runtime.InteropServices");

let target = if let FunctionKind::Freestanding = &func.kind {
self.require_interop_using("System.Runtime.InteropServices");
&mut self.csharp_interop_src
Expand Down Expand Up @@ -1865,7 +1874,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> {
.iter()
.map(|case| {
let case_name = case.name.to_csharp_ident();
let tag = case.name.to_shouty_snake_case();
let tag = case.name.to_csharp_ident_upper();
let (parameter, argument) = if let Some(ty) = self.non_empty_type(case.ty.as_ref())
{
(
Expand All @@ -1877,8 +1886,8 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> {
};

format!(
"{access} static {name} {case_name}({parameter}) {{
return new {name}({tag}, {argument});
"{access} static {name} {tag}({parameter}) {{
return new {name}(Tags.{tag}, {argument});
}}
"
)
Expand All @@ -1892,14 +1901,14 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> {
.filter_map(|case| {
self.non_empty_type(case.ty.as_ref()).map(|ty| {
let case_name = case.name.to_upper_camel_case();
let tag = case.name.to_shouty_snake_case();
let tag = case.name.to_csharp_ident_upper();
let ty = self.type_name(ty);
format!(
r#"{access} {ty} As{case_name}
{{
get
{{
if (Tag == {tag})
if (Tag == Tags.{tag})
return ({ty})value!;
else
throw new ArgumentException("expected {tag}, got " + Tag);
Expand All @@ -1917,7 +1926,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> {
.iter()
.enumerate()
.map(|(i, case)| {
let tag = case.name.to_shouty_snake_case();
let tag = case.name.to_csharp_ident_upper();
format!("{access} const {tag_type} {tag} = {i};")
})
.collect::<Vec<_>>()
Expand All @@ -1937,7 +1946,10 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> {
{constructors}
{accessors}
{tags}
{access} class Tags {{
{tags}
}}
}}
"
);
Expand Down Expand Up @@ -2195,7 +2207,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> {
String::new()
};

let method = case_name.to_csharp_ident();
let method = case_name.to_csharp_ident_upper();

let call = if let Some(position) = generics_position {
let (ty, generics) = ty.split_at(position);
Expand Down Expand Up @@ -2557,15 +2569,15 @@ impl Bindgen for FunctionBindgen<'_, '_> {
result,
..
} => self.lower_variant(
&[("ok", result.ok), ("err", result.err)],
&[("Ok", result.ok), ("Err", result.err)],
lowered_types,
&operands[0],
results,
),

Instruction::ResultLift { result, ty } => self.lift_variant(
&Type::Id(*ty),
&[("ok", result.ok), ("err", result.err)],
&[("Ok", result.ok), ("Err", result.err)],
&operands[0],
results,
),
Expand Down Expand Up @@ -2844,13 +2856,13 @@ impl Bindgen for FunctionBindgen<'_, '_> {
format!(
"\
case {index}: {{
ret = {head}{ty}.err(({err_ty}) e.Value){tail};
ret = {head}{ty}.Err(({err_ty}) e.Value){tail};
break;
}}
"
)
);
oks.push(format!("{ty}.ok("));
oks.push(format!("{ty}.Ok("));
payload_is_void = result.ok.is_none();
}
if !self.results.is_empty() {
Expand Down Expand Up @@ -3382,28 +3394,110 @@ fn is_primitive(ty: &Type) -> bool {
}

trait ToCSharpIdent: ToOwned {
fn csharp_keywords() -> Vec<&'static str>;
fn to_csharp_ident(&self) -> Self::Owned;
fn to_csharp_ident_upper(&self) -> Self::Owned;
}

impl ToCSharpIdent for str {
// Source: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/
fn csharp_keywords() -> Vec<&'static str> {
vec![
"abstract",
"as",
"base",
"bool",
"break",
"byte",
"case",
"catch",
"char",
"checked",
"class",
"const",
"continue",
"decimal",
"default",
"delegate",
"do",
"double",
"else",
"enum",
"event",
"explicit",
"extern",
"false",
"finally",
"fixed",
"float",
"for",
"foreach",
"goto",
"if",
"implicit",
"in",
"int",
"interface",
"internal",
"is",
"lock",
"long",
"namespace",
"new",
"null",
"object",
"operator",
"out",
"override",
"params",
"private",
"protected",
"public",
"readonly",
"ref",
"return",
"sbyte",
"sealed",
"short",
"sizeof",
"stackalloc",
"static",
"string",
"struct",
"switch",
"this",
"throw",
"true",
"try",
"typeof",
"uint",
"ulong",
"unchecked",
"unsafe",
"ushort",
"using",
"virtual",
"void",
"volatile",
"while",
]
}

fn to_csharp_ident(&self) -> String {
// Escape C# keywords
// Source: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/

//TODO: Repace with actual keywords
match self {
"abstract" | "as" | "base" | "bool" | "break" | "byte" | "case" | "catch" | "char"
| "checked" | "class" | "const" | "continue" | "decimal" | "default" | "delegate"
| "do" | "double" | "else" | "enum" | "event" | "explicit" | "extern" | "false"
| "finally" | "fixed" | "float" | "for" | "foreach" | "goto" | "if" | "implicit"
| "in" | "int" | "interface" | "internal" | "is" | "lock" | "long" | "namespace"
| "new" | "null" | "object" | "operator" | "out" | "override" | "params"
| "private" | "protected" | "public" | "readonly" | "ref" | "return" | "sbyte"
| "sealed" | "short" | "sizeof" | "stackalloc" | "static" | "string" | "struct"
| "switch" | "this" | "throw" | "true" | "try" | "typeof" | "uint" | "ulong"
| "unchecked" | "unsafe" | "ushort" | "using" | "virtual" | "void" | "volatile"
| "while" => format!("@{self}"),
_ => self.to_lower_camel_case(),
if Self::csharp_keywords().contains(&self) {
format!("@{}", self)
} else {
self.to_lower_camel_case()
}
}

fn to_csharp_ident_upper(&self) -> String {
// Escape C# keywords
if Self::csharp_keywords().contains(&self) {
format!("@{}", self)
} else {
self.to_upper_camel_case()
}
}
}
Expand Down
12 changes: 6 additions & 6 deletions tests/runtime/resource_aggregates/wasm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ public static uint Foo(
var ir3 = new Import.R3(((Thing) r3.thing1).val, ((Thing) r3.thing2).val);
var it1 = (((Thing) t1.Item1).val, new Import.R1(((Thing) t1.Item2.thing).val));
var it2 = ((Thing) t2).val;
var iv1 = Import.V1.thing(((Thing) v1.AsThing).val);
var iv2 = Import.V2.thing(((Thing) v2.AsThing).val);
var iv1 = Import.V1.Thing(((Thing) v1.AsThing).val);
var iv2 = Import.V2.Thing(((Thing) v2.AsThing).val);
var il1 = new List<Import.Thing>();
foreach (var thing in l1)
{
Expand All @@ -52,11 +52,11 @@ public static uint Foo(
? ((Thing) o2).val
: null;
var iresult1 = result1.IsOk
? Result<Import.Thing, None>.ok(((Thing) result1.AsOk).val)
: Result<Import.Thing, None>.err(new None());
? Result<Import.Thing, None>.Ok(((Thing) result1.AsOk).val)
: Result<Import.Thing, None>.Err(new None());
var iresult2 = result2.IsOk
? Result<Import.Thing, None>.ok(((Thing) result2.AsOk).val)
: Result<Import.Thing, None>.err(new None());
? Result<Import.Thing, None>.Ok(((Thing) result2.AsOk).val)
: Result<Import.Thing, None>.Err(new None());

return Host.Foo(ir1, ir2, ir3, it1, it2, iv1, iv2, il1, il2, io1, io2, iresult1, iresult2) + 4;
}
Expand Down
12 changes: 6 additions & 6 deletions tests/runtime/results/wasm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,19 @@ public static float VariantError(float a)
} catch (WitException e) {
var value = (ResultsWorld.wit.imports.test.results.ITest.E3) e.Value;
switch (value.Tag) {
case ResultsWorld.wit.imports.test.results.ITest.E3.E1:
case ResultsWorld.wit.imports.test.results.ITest.E3.Tags.E1:
switch (value.AsE1) {
case ResultsWorld.wit.imports.test.results.ITest.E.A:
throw new WitException(ITest.E3.e1(ITest.E.A), 0);
throw new WitException(ITest.E3.E1(ITest.E.A), 0);
case ResultsWorld.wit.imports.test.results.ITest.E.B:
throw new WitException(ITest.E3.e1(ITest.E.B), 0);
throw new WitException(ITest.E3.E1(ITest.E.B), 0);
case ResultsWorld.wit.imports.test.results.ITest.E.C:
throw new WitException(ITest.E3.e1(ITest.E.C), 0);
throw new WitException(ITest.E3.E1(ITest.E.C), 0);
default:
throw new Exception("unreachable");
}
case ResultsWorld.wit.imports.test.results.ITest.E3.E2: {
throw new WitException(ITest.E3.e2(new ITest.E2(value.AsE2.line, value.AsE2.column)), 0);
case ResultsWorld.wit.imports.test.results.ITest.E3.Tags.E2: {
throw new WitException(ITest.E3.E2(new ITest.E2(value.AsE2.line, value.AsE2.column)), 0);
}
default:
throw new Exception("unreachable");
Expand Down
Loading

0 comments on commit 563956d

Please sign in to comment.