Skip to content

Commit

Permalink
chore(net-client-gen): gen code w/ init props, nullable enable (#675)
Browse files Browse the repository at this point in the history
  • Loading branch information
vobradovich authored Nov 26, 2024
1 parent 7cf5719 commit 36ee739
Show file tree
Hide file tree
Showing 15 changed files with 506 additions and 496 deletions.
5 changes: 1 addition & 4 deletions net/rs/client-gen/src/ctor_generators.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
use crate::{
helpers::*,
type_generators::{primitive_type_to_dotnet, TypeDeclGenerator},
};
use crate::{helpers::*, type_decl_generators::*};
use convert_case::{Case, Casing};
use csharp::Tokens;
use genco::prelude::*;
Expand Down
3 changes: 1 addition & 2 deletions net/rs/client-gen/src/events_generator.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::helpers::*;
use crate::type_generators::{primitive_type_to_dotnet, TypeDeclGenerator};
use crate::{helpers::*, type_decl_generators::*};
use convert_case::{Case, Casing};
use csharp::Tokens;
use genco::prelude::*;
Expand Down
6 changes: 4 additions & 2 deletions net/rs/client-gen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ mod events_generator;
mod helpers;
mod root_generator;
mod service_generators;
mod type_generators;
mod tol_level_type_generators;
mod type_decl_generators;

pub struct IdlPath<'a>(&'a Path);

Expand Down Expand Up @@ -69,7 +70,8 @@ impl<'a> ClientGenerator<'a, IdlString<'a>> {
RootGenerator::new(anonymous_service_name, namespace, self.external_types);
visitor::accept_program(&program, &mut generator);
let tokens = generator.finalize();
let code = tokens.to_file_string()?;
let mut code = "// <auto-generated />\n".to_owned();
code.push_str(&tokens.to_file_string()?);
Ok(code)
}

Expand Down
7 changes: 5 additions & 2 deletions net/rs/client-gen/src/root_generator.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use crate::{ctor_generators::*, events_generator::*, service_generators::*, type_generators::*};
use crate::{
ctor_generators::*, events_generator::*, service_generators::*, tol_level_type_generators::*,
type_decl_generators::*,
};
use convert_case::{Case, Casing};
use csharp::Tokens;
use genco::{prelude::*, tokens::ItemStr};
Expand All @@ -19,7 +22,7 @@ impl<'a> RootGenerator<'a> {
external_types: HashMap<&'a str, &'a str>,
) -> Self {
let mut tokens = Tokens::new();
tokens.append(ItemStr::Static("#nullable disable"));
tokens.append(ItemStr::Static("#nullable enable"));
tokens.line();
tokens.append(ItemStr::Static(
"#pragma warning disable RCS0056 // A line is too long",
Expand Down
2 changes: 1 addition & 1 deletion net/rs/client-gen/src/service_generators.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{helpers::*, type_generators::TypeDeclGenerator};
use crate::{helpers::*, type_decl_generators::*};
use convert_case::{Case, Casing};
use csharp::Tokens;
use genco::prelude::*;
Expand Down
218 changes: 218 additions & 0 deletions net/rs/client-gen/src/tol_level_type_generators.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
use crate::{
helpers::*,
type_decl_generators::{primitive_type_to_dotnet, TypeDeclGenerator},
};
use convert_case::{Case, Casing};
use csharp::Tokens;
use genco::prelude::*;
use sails_idl_parser::{ast::visitor, ast::visitor::Visitor, ast::*};

pub(crate) struct TopLevelTypeGenerator<'a> {
type_name: &'a str,
type_generator: TypeDeclGenerator<'a>,
tokens: Tokens,
}

impl<'a> TopLevelTypeGenerator<'a> {
pub(crate) fn new(type_name: &'a str, type_generator: TypeDeclGenerator<'a>) -> Self {
Self {
type_name,
type_generator,
tokens: Tokens::new(),
}
}

pub(crate) fn finalize(self) -> Tokens {
self.tokens
}
}

impl<'a> Visitor<'a> for TopLevelTypeGenerator<'a> {
fn visit_type(&mut self, type_: &'a Type) {
self.tokens.push();
self.tokens.append(summary_comment(type_.docs()));
self.tokens.push();
visitor::accept_type(type_, self);
self.tokens.line();
}

fn visit_struct_def(&mut self, struct_def: &'a StructDef) {
let mut struct_def_generator =
StructDefGenerator::new(self.type_name, self.type_generator.clone());
struct_def_generator.visit_struct_def(struct_def);
self.tokens.extend(struct_def_generator.finalize());
}

fn visit_enum_def(&mut self, enum_def: &'a EnumDef) {
let mut enum_def_generator =
EnumDefGenerator::new(self.type_name, self.type_generator.clone());
enum_def_generator.visit_enum_def(enum_def);
self.tokens.extend(enum_def_generator.finalize());
}
}

struct StructDefGenerator<'a> {
type_name: &'a str,
type_generator: TypeDeclGenerator<'a>,
is_tuple_struct: bool,
props_tokens: Tokens,
encode_tokens: Tokens,
decode_tokens: Tokens,
}

impl<'a> StructDefGenerator<'a> {
fn new(type_name: &'a str, type_generator: TypeDeclGenerator<'a>) -> Self {
Self {
type_name,
type_generator,
is_tuple_struct: false,
props_tokens: Tokens::new(),
encode_tokens: Tokens::new(),
decode_tokens: Tokens::new(),
}
}

pub(crate) fn finalize(self) -> Tokens {
let system_array = &csharp::import("global::System", "Array");
let generic_list = &csharp::import("global::System.Collections.Generic", "List");

quote! {
public sealed partial class $(self.type_name) : global::Substrate.NetApi.Model.Types.Base.BaseType
{
$(self.props_tokens)

$(inheritdoc())
public override string TypeName() => $(quoted(self.type_name));

$(inheritdoc())
public override byte[] Encode()
{
var result = new $generic_list<byte>();$['\r']
$(self.encode_tokens)
return result.ToArray();$['\r']
}

$(inheritdoc())
public override void Decode(byte[] byteArray, ref int p)
{
var start = p;$['\r']
$(self.decode_tokens)
var bytesLength = p - start;
this.TypeSize = bytesLength;
this.Bytes = new byte[bytesLength];
$system_array.Copy(byteArray, start, this.Bytes, 0, bytesLength);
}
}
}
}

fn tuple_struct(&mut self, struct_def: &'a StructDef) {
if struct_def.fields().is_empty() {
return;
}
let value_type = &self.type_generator.generate_struct_def(struct_def);
quote_in! { self.props_tokens =>
public $value_type Value { get; init; } = new();$['\r']
};
quote_in! { self.encode_tokens =>
result.AddRange(this.Value.Encode());$['\r']
};
quote_in! { self.decode_tokens =>
this.Value.Decode(byteArray, ref p);$['\r']
};
}
}

impl<'a> Visitor<'a> for StructDefGenerator<'a> {
fn visit_struct_def(&mut self, struct_def: &'a StructDef) {
let is_regular_struct = struct_def.fields().iter().all(|f| f.name().is_some());
let is_tuple_struct = struct_def.fields().iter().all(|f| f.name().is_none());
if !is_regular_struct && !is_tuple_struct {
panic!("Struct must be either regular or tuple");
}
self.is_tuple_struct = is_tuple_struct;

if is_tuple_struct {
self.tuple_struct(struct_def);
} else {
visitor::accept_struct_def(struct_def, self);
}
}

fn visit_struct_field(&mut self, struct_field: &'a StructField) {
let type_decl_code = &self
.type_generator
.generate_type_decl(struct_field.type_decl());

self.props_tokens.push();
self.props_tokens
.append(summary_comment(struct_field.docs()));
self.props_tokens.push();
if let Some(field_name) = struct_field.name() {
let field_name_pascal = &field_name.to_case(Case::Pascal);
quote_in! { self.props_tokens =>
public $type_decl_code $field_name_pascal { get; init; } = new();$['\r']
};
quote_in! { self.encode_tokens =>
result.AddRange(this.$field_name_pascal.Encode());$['\r']
};
quote_in! { self.decode_tokens =>
this.$field_name_pascal.Decode(byteArray, ref p);$['\r']
};
}
}
}

struct EnumDefGenerator<'a> {
type_name: &'a str,
type_generator: TypeDeclGenerator<'a>,
enum_tokens: Tokens,
class_tokens: Tokens,
}

impl<'a> EnumDefGenerator<'a> {
pub(crate) fn new(type_name: &'a str, type_generator: TypeDeclGenerator<'a>) -> Self {
Self {
type_name,
type_generator,
enum_tokens: Tokens::new(),
class_tokens: Tokens::new(),
}
}

pub(crate) fn finalize(self) -> Tokens {
let class_name = &format!("Enum{}", self.type_name);
quote!(
public enum $(self.type_name)
{
$(self.enum_tokens)
}

public sealed partial class $class_name : global::Substrate.NetApi.Model.Types.Base.BaseEnumRust<$(self.type_name)>
{
public $class_name()
{
$(self.class_tokens)
}
}
)
}
}

impl<'ast> Visitor<'ast> for EnumDefGenerator<'ast> {
fn visit_enum_variant(&mut self, enum_variant: &'ast EnumVariant) {
quote_in! { self.enum_tokens =>
$(summary_comment(enum_variant.docs()))
$(enum_variant.name()),$['\r']
};

let type_decl_code = if let Some(type_decl) = enum_variant.type_decl().as_ref() {
self.type_generator.generate_type_decl(type_decl)
} else {
primitive_type_to_dotnet(PrimitiveType::Null).into()
};
quote_in! { self.class_tokens =>
this.AddTypeDecoder<$(type_decl_code)>($(self.type_name).$(enum_variant.name()));$['\r']
}
}
}
Loading

0 comments on commit 36ee739

Please sign in to comment.