diff --git a/.github/workflows/net-ci.yml b/.github/workflows/net-ci.yml index e698b6d1..d5386740 100644 --- a/.github/workflows/net-ci.yml +++ b/.github/workflows/net-ci.yml @@ -19,6 +19,8 @@ jobs: build_test: name: Build & Test runs-on: ubuntu-latest + permissions: + packages: read defaults: run: shell: bash @@ -33,7 +35,14 @@ jobs: dotnet-version: 8.0.x - name: Restore Solution - run: >- + run: | + NUGET_SOURCE_URL="https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json" + dotnet nuget remove source ${{ github.repository_owner }}-github || true + dotnet nuget add source $NUGET_SOURCE_URL \ + --name ${{ github.repository_owner }}-github \ + --username "${{ github.workflow }}" \ + --password ${{ secrets.GITHUB_TOKEN }} \ + --store-password-in-clear-text dotnet restore - name: Build Solution diff --git a/Sails.Net.sln b/Sails.Net.sln index ba363611..7cf30a1d 100644 --- a/Sails.Net.sln +++ b/Sails.Net.sln @@ -33,6 +33,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Substrate.Gear.Client.Tests EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sails.Client.Tests", "net\tests\Sails.Client.Tests\Sails.Client.Tests.csproj", "{BC248D44-14CD-4BB0-9AF9-4E0750574492}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{4D457454-ED79-4BC3-8B6B-AC6934AAF048}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sails.DemoClient", "net\examples\Sails.DemoClient\Sails.DemoClient.csproj", "{C9D87AD3-612B-43A0-89BD-9211FE2FB310}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sails.DemoClient.Tests", "net\tests\Sails.DemoClient.Tests\Sails.DemoClient.Tests.csproj", "{5ED8CDB7-F896-4ACE-8B7B-A128CCBBCFA4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -83,6 +89,14 @@ Global {BC248D44-14CD-4BB0-9AF9-4E0750574492}.Debug|Any CPU.Build.0 = Debug|Any CPU {BC248D44-14CD-4BB0-9AF9-4E0750574492}.Release|Any CPU.ActiveCfg = Release|Any CPU {BC248D44-14CD-4BB0-9AF9-4E0750574492}.Release|Any CPU.Build.0 = Release|Any CPU + {C9D87AD3-612B-43A0-89BD-9211FE2FB310}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C9D87AD3-612B-43A0-89BD-9211FE2FB310}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C9D87AD3-612B-43A0-89BD-9211FE2FB310}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C9D87AD3-612B-43A0-89BD-9211FE2FB310}.Release|Any CPU.Build.0 = Release|Any CPU + {5ED8CDB7-F896-4ACE-8B7B-A128CCBBCFA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5ED8CDB7-F896-4ACE-8B7B-A128CCBBCFA4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5ED8CDB7-F896-4ACE-8B7B-A128CCBBCFA4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5ED8CDB7-F896-4ACE-8B7B-A128CCBBCFA4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -93,6 +107,8 @@ Global {A6A2172B-8F8F-4BDC-B519-E7299FFCCA5F} = {19594BCA-94DB-44AD-ACBC-2ACFED242E9F} {42B621CE-C2B4-4911-961C-5B087A514AF5} = {19594BCA-94DB-44AD-ACBC-2ACFED242E9F} {BC248D44-14CD-4BB0-9AF9-4E0750574492} = {19594BCA-94DB-44AD-ACBC-2ACFED242E9F} + {C9D87AD3-612B-43A0-89BD-9211FE2FB310} = {4D457454-ED79-4BC3-8B6B-AC6934AAF048} + {5ED8CDB7-F896-4ACE-8B7B-A128CCBBCFA4} = {19594BCA-94DB-44AD-ACBC-2ACFED242E9F} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {0894C08B-8BB9-401D-8471-26F5EB5A4EA2} diff --git a/net/Directory.Packages.props b/net/Directory.Packages.props index 17b57a68..c5b61101 100644 --- a/net/Directory.Packages.props +++ b/net/Directory.Packages.props @@ -11,7 +11,7 @@ - + @@ -19,17 +19,21 @@ + - + - + + + + diff --git a/net/examples/Sails.DemoClient/Sails.DemoClient.csproj b/net/examples/Sails.DemoClient/Sails.DemoClient.csproj new file mode 100644 index 00000000..3e49ccb2 --- /dev/null +++ b/net/examples/Sails.DemoClient/Sails.DemoClient.csproj @@ -0,0 +1,16 @@ + + + + netstandard2.0 + + + + + + + + + + + + diff --git a/net/examples/Sails.DemoClient/demo.idl b/net/examples/Sails.DemoClient/demo.idl new file mode 100644 index 00000000..d7cc6728 --- /dev/null +++ b/net/examples/Sails.DemoClient/demo.idl @@ -0,0 +1,91 @@ +type ReferenceCount = struct { + u32, +}; + +type DoThatParam = struct { + p1: nat32, + p2: actor_id, + p3: ManyVariants, +}; + +type ManyVariants = enum { + One, + Two: u32, + Three: opt u256, + Four: struct { a: u32, b: opt u16 }, + Five: struct { str, h256 }, + Six: struct { u32 }, +}; + +type TupleStruct = struct { + bool, +}; + +constructor { + /// Program constructor (called once at the very beginning of the program lifetime) + Default : (); + /// Another program constructor (called once at the very beginning of the program lifetime) + New : (counter: opt u32, dog_position: opt struct { i32, i32 }); +}; + +service Counter { + /// Add a value to the counter + Add : (value: u32) -> u32; + /// Substract a value from the counter + Sub : (value: u32) -> u32; + /// Get the current value + query Value : () -> u32; + + events { + /// Emitted when a new value is added to the counter + Added: u32; + /// Emitted when a value is subtracted from the counter + Subtracted: u32; + } +}; + +service Dog { + MakeSound : () -> str; + Walk : (dx: i32, dy: i32) -> null; + query AvgWeight : () -> u32; + query Position : () -> struct { i32, i32 }; + + events { + Barked; + Walked: struct { from: struct { i32, i32 }, to: struct { i32, i32 } }; + } +}; + +service PingPong { + Ping : (input: str) -> result (str, str); +}; + +service References { + Add : (v: u32) -> u32; + AddByte : (byte: u8) -> vec u8; + GuessNum : (number: u8) -> result (str, str); + Incr : () -> ReferenceCount; + SetNum : (number: u8) -> result (null, str); + query Baked : () -> str; + query LastByte : () -> opt u8; + query Message : () -> opt str; +}; + +service ThisThat { + DoThat : (param: DoThatParam) -> result (struct { actor_id, nat32 }, struct { str }); + DoThis : (p1: u32, p2: str, p3: struct { opt h160, nat8 }, p4: TupleStruct) -> struct { str, u32 }; + Noop : () -> null; + query That : () -> result (str, str); + query This : () -> u32; +}; + +service ValueFee { + /// Return flag if fee taken and remain value, + /// using special type `CommandReply` + DoSomethingAndTakeFee : () -> bool; + + events { + Withheld: u128; + } +}; + diff --git a/net/rs/client-gen/src/ctor_generators.rs b/net/rs/client-gen/src/ctor_generators.rs index 690f1d84..27feaf11 100644 --- a/net/rs/client-gen/src/ctor_generators.rs +++ b/net/rs/client-gen/src/ctor_generators.rs @@ -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::*; @@ -63,19 +60,9 @@ impl<'a> Visitor<'a> for CtorFactoryGenerator<'a> { self.interface_tokens.push(); let route_bytes = &path_bytes(func.name()).0; - let args = &encoded_fn_args(func.params()); + let args = &encoded_fn_args_comma_prefixed(func.params()); let args_with_type = &self.type_generator.fn_params_with_types(func.params()); - - let type_decls = func - .params() - .iter() - .map(|p| p.type_decl()) - .collect::>(); - let tuple_arg_type = if type_decls.is_empty() { - primitive_type_to_dotnet(PrimitiveType::Null).to_string() - } else { - self.type_generator.generate_types_as_tuple(type_decls) - }; + let void_type = primitive_type_to_dotnet(PrimitiveType::Null); let activation = &csharp::import("global::Sails.Remoting.Abstractions", "IActivation"); let action = &csharp::import("global::Sails.Remoting", "RemotingAction"); @@ -88,10 +75,7 @@ impl<'a> Visitor<'a> for CtorFactoryGenerator<'a> { $(inheritdoc()) public $activation $func_name_pascal($args_with_type) { - return new $action<$(&tuple_arg_type)>( - this.remoting, - [$route_bytes], - new $(&tuple_arg_type)($args)); + return new $action<$(void_type)>(this.remoting, [$route_bytes]$args); } $['\n'] }; diff --git a/net/rs/client-gen/src/events_generator.rs b/net/rs/client-gen/src/events_generator.rs index 7b7bad38..c8fd1180 100644 --- a/net/rs/client-gen/src/events_generator.rs +++ b/net/rs/client-gen/src/events_generator.rs @@ -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::*; diff --git a/net/rs/client-gen/src/helpers.rs b/net/rs/client-gen/src/helpers.rs index 3f5f0bcf..0ccdb60c 100644 --- a/net/rs/client-gen/src/helpers.rs +++ b/net/rs/client-gen/src/helpers.rs @@ -22,12 +22,17 @@ pub(crate) fn path_bytes(path: &str) -> (String, usize) { } } -pub(crate) fn encoded_fn_args(params: &[FuncParam]) -> String { +pub(crate) fn encoded_fn_args_comma_prefixed(params: &[FuncParam]) -> String { params .iter() - .map(|p| escape_keywords(p.name().to_case(convert_case::Case::Camel))) + .map(|p| { + format!( + ", {}", + escape_keywords(p.name().to_case(convert_case::Case::Camel)) + ) + }) .collect::>() - .join(", ") + .join("") } pub fn summary_comment(comment: T) -> SummaryComment diff --git a/net/rs/client-gen/src/lib.rs b/net/rs/client-gen/src/lib.rs index 27d5a49b..bc1549e4 100644 --- a/net/rs/client-gen/src/lib.rs +++ b/net/rs/client-gen/src/lib.rs @@ -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); @@ -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 = "// \n".to_owned(); + code.push_str(&tokens.to_file_string()?); Ok(code) } diff --git a/net/rs/client-gen/src/root_generator.rs b/net/rs/client-gen/src/root_generator.rs index f0a4dc7d..cc109075 100644 --- a/net/rs/client-gen/src/root_generator.rs +++ b/net/rs/client-gen/src/root_generator.rs @@ -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}; @@ -19,6 +22,8 @@ impl<'a> RootGenerator<'a> { external_types: HashMap<&'a str, &'a str>, ) -> Self { let mut tokens = Tokens::new(); + tokens.append(ItemStr::Static("#nullable enable")); + tokens.line(); tokens.append(ItemStr::Static( "#pragma warning disable RCS0056 // A line is too long", )); diff --git a/net/rs/client-gen/src/service_generators.rs b/net/rs/client-gen/src/service_generators.rs index fac502ae..9adaeee9 100644 --- a/net/rs/client-gen/src/service_generators.rs +++ b/net/rs/client-gen/src/service_generators.rs @@ -1,11 +1,9 @@ -use crate::{helpers::*, type_generators::TypeDeclGenerator}; +use crate::{helpers::*, type_decl_generators::*}; use convert_case::{Case, Casing}; use csharp::Tokens; use genco::prelude::*; use sails_idl_parser::{ast::visitor, ast::visitor::Visitor, ast::*}; -const BASE_TUPLE_RUST: &str = "global::Substrate.NetApi.Model.Types.Base.BaseTupleRust"; - /// Generates a client that implements service trait pub(crate) struct ServiceClientGenerator<'a> { service_name: String, @@ -63,7 +61,7 @@ impl<'a> Visitor<'a> for ServiceClientGenerator<'a> { let func_route_bytes = path_bytes(func.name()).0; let route_bytes = [service_route_bytes, func_route_bytes].join(", "); - let args = encoded_fn_args(func.params()); + let args = &encoded_fn_args_comma_prefixed(func.params()); let args_with_type = &self.type_generator.fn_params_with_types(func.params()); let func_return_type = &self.type_generator.generate_type_decl(func.output()); @@ -80,11 +78,7 @@ impl<'a> Visitor<'a> for ServiceClientGenerator<'a> { $(inheritdoc()) public $return_type<$func_return_type> $func_name_pascal($args_with_type) { - return new $action<$func_return_type>( - this.remoting, - [$(&route_bytes)], - new $(BASE_TUPLE_RUST)($(&args)) - ); + return new $action<$func_return_type>(this.remoting, [$(&route_bytes)]$args); } }; } diff --git a/net/rs/client-gen/src/tol_level_type_generators.rs b/net/rs/client-gen/src/tol_level_type_generators.rs new file mode 100644 index 00000000..3901796a --- /dev/null +++ b/net/rs/client-gen/src/tol_level_type_generators.rs @@ -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();$['\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'] + } + } +} diff --git a/net/rs/client-gen/src/type_decl_generators.rs b/net/rs/client-gen/src/type_decl_generators.rs new file mode 100644 index 00000000..a238ef47 --- /dev/null +++ b/net/rs/client-gen/src/type_decl_generators.rs @@ -0,0 +1,193 @@ +use crate::helpers::*; +use convert_case::Casing; +use sails_idl_parser::{ast::visitor, ast::visitor::Visitor, ast::*}; + +macro_rules! base { + ($t: expr) => { + concat!("global::Substrate.NetApi.Model.Types.Base.", $t) + }; +} + +macro_rules! primitive { + ($t: expr) => { + concat!("global::Substrate.NetApi.Model.Types.Primitive.", $t) + }; +} + +macro_rules! gprimitives { + ($t: expr) => { + concat!( + "global::Substrate.Gear.Api.Generated.Model.gprimitives.", + $t + ) + }; +} + +macro_rules! client_base { + ($t: expr) => { + concat!("global::Substrate.Gear.Client.NetApi.Model.Types.Base.", $t) + }; +} + +macro_rules! client_primitive { + ($t: expr) => { + concat!( + "global::Substrate.Gear.Client.NetApi.Model.Types.Primitive.", + $t + ) + }; +} + +#[derive(Clone)] +pub(crate) struct TypeDeclGenerator<'a> { + code: String, + generated_types: &'a Vec<&'a Type>, +} + +impl<'a> TypeDeclGenerator<'a> { + pub(crate) fn new(generated_types: &'a Vec<&'a Type>) -> Self { + Self { + code: String::new(), + generated_types, + } + } + + pub(crate) fn generate_type_decl(&mut self, type_decl: &'a TypeDecl) -> String { + visitor::accept_type_decl(type_decl, self); + std::mem::take(&mut self.code) + } + + pub(crate) fn generate_struct_def(&mut self, struct_def: &'a StructDef) -> String { + visitor::accept_struct_def(struct_def, self); + std::mem::take(&mut self.code) + } + + fn join_type_decls>( + &mut self, + type_decls: I, + separator: &str, + ) { + let prev_code = std::mem::take(&mut self.code); + let mut type_decls_str: Vec = Vec::new(); + let iter = type_decls.into_iter(); + for type_decl in iter { + visitor::accept_type_decl(type_decl, self); + type_decls_str.push(std::mem::take(&mut self.code)); + } + _ = std::mem::replace(&mut self.code, prev_code); + self.code.push_str(type_decls_str.join(separator).as_str()); + } + + pub(crate) fn fn_params_with_types(&mut self, params: &'a [FuncParam]) -> String { + params + .iter() + .map(|p| { + format!( + "{} {}", + self.generate_type_decl(p.type_decl()), + escape_keywords(p.name().to_case(convert_case::Case::Camel)) + ) + }) + .collect::>() + .join(", ") + } +} + +impl<'a> Visitor<'a> for TypeDeclGenerator<'a> { + fn visit_optional_type_decl(&mut self, optional_type_decl: &'a TypeDecl) { + self.code.push_str(base!("BaseOpt<")); + visitor::accept_type_decl(optional_type_decl, self); + self.code.push('>'); + } + + fn visit_result_type_decl(&mut self, ok_type_decl: &'a TypeDecl, err_type_decl: &'a TypeDecl) { + self.code.push_str(client_base!("BaseResult<")); + visitor::accept_type_decl(ok_type_decl, self); + self.code.push_str(", "); + visitor::accept_type_decl(err_type_decl, self); + self.code.push('>'); + } + + fn visit_vector_type_decl(&mut self, vector_type_decl: &'a TypeDecl) { + self.code.push_str(base!("BaseVec<")); + visitor::accept_type_decl(vector_type_decl, self); + self.code.push('>'); + } + + fn visit_struct_def(&mut self, struct_def: &'a StructDef) { + if struct_def.fields().is_empty() { + return; + } + if struct_def.fields().len() == 1 { + visitor::accept_type_decl(struct_def.fields()[0].type_decl(), self); + } else { + self.code.push_str(base!("BaseTuple<")); + self.join_type_decls(struct_def.fields().iter().map(|f| f.type_decl()), ", "); + self.code.push('>'); + } + } + + fn visit_primitive_type_id(&mut self, primitive_type_id: PrimitiveType) { + self.code + .push_str(primitive_type_to_dotnet(primitive_type_id)); + } + + fn visit_user_defined_type_id(&mut self, user_defined_type_id: &'a str) { + let is_enum = self + .generated_types + .iter() + .find(|&&t| t.name() == user_defined_type_id) + .map(|&t| matches!(t.def(), TypeDef::Enum(_))) + .unwrap_or_default(); + let type_id = if is_enum { + &format!("Enum{}", user_defined_type_id) // Enum prefix + } else { + user_defined_type_id + }; + self.code.push_str(type_id); + } + + fn visit_map_type_decl(&mut self, key_type_decl: &'a TypeDecl, value_type_decl: &'a TypeDecl) { + self.code.push_str(client_base!("BaseDictionary<")); + visitor::accept_type_decl(key_type_decl, self); + self.code.push_str(", "); + visitor::accept_type_decl(value_type_decl, self); + self.code.push('>'); + } + + fn visit_array_type_decl(&mut self, item_type_decl: &'a TypeDecl, _len: u32) { + visitor::accept_type_decl(item_type_decl, self); + self.code.push_str("[]"); + } +} + +pub(crate) fn primitive_type_to_dotnet(primitive_type: PrimitiveType) -> &'static str { + match primitive_type { + PrimitiveType::U8 => primitive!("U8"), + PrimitiveType::U16 => primitive!("U16"), + PrimitiveType::U32 => primitive!("U32"), + PrimitiveType::U64 => primitive!("U64"), + PrimitiveType::U128 => primitive!("U128"), + PrimitiveType::I8 => primitive!("I8"), + PrimitiveType::I16 => primitive!("I16"), + PrimitiveType::I32 => primitive!("I32"), + PrimitiveType::I64 => primitive!("I64"), + PrimitiveType::I128 => primitive!("I128"), + PrimitiveType::Bool => primitive!("Bool"), + PrimitiveType::Str => primitive!("Str"), + PrimitiveType::Char => primitive!("PrimChar"), + PrimitiveType::Null => base!("BaseVoid"), + PrimitiveType::ActorId => gprimitives!("ActorId"), + PrimitiveType::CodeId => gprimitives!("CodeId"), + PrimitiveType::MessageId => gprimitives!("MessageId"), + PrimitiveType::H160 => client_primitive!("H160"), + PrimitiveType::H256 => "global::Substrate.Gear.Api.Generated.Model.primitive_types.H256", + PrimitiveType::U256 => primitive!("U256"), + PrimitiveType::NonZeroU8 => client_primitive!("NonZeroU8"), + PrimitiveType::NonZeroU16 => client_primitive!("NonZeroU16"), + PrimitiveType::NonZeroU32 => "global::Substrate.Gear.Api.Generated.Types.Base.NonZeroU32", + PrimitiveType::NonZeroU64 => client_primitive!("NonZeroU64"), + PrimitiveType::NonZeroU128 => client_primitive!("NonZeroU128"), + PrimitiveType::NonZeroU256 => client_primitive!("NonZeroU256"), + } +} diff --git a/net/rs/client-gen/src/type_generators.rs b/net/rs/client-gen/src/type_generators.rs deleted file mode 100644 index 027329b1..00000000 --- a/net/rs/client-gen/src/type_generators.rs +++ /dev/null @@ -1,418 +0,0 @@ -use crate::helpers::*; -use convert_case::{Case, Casing}; -use csharp::Tokens; -use genco::prelude::*; -use sails_idl_parser::{ast::visitor, ast::visitor::Visitor, ast::*}; - -macro_rules! base { - ($t: expr) => { - concat!("global::Substrate.NetApi.Model.Types.Base.", $t) - }; -} - -macro_rules! primitive { - ($t: expr) => { - concat!("global::Substrate.NetApi.Model.Types.Primitive.", $t) - }; -} - -macro_rules! gprimitives { - ($t: expr) => { - concat!( - "global::Substrate.Gear.Api.Generated.Model.gprimitives.", - $t - ) - }; -} - -macro_rules! client_base { - ($t: expr) => { - concat!("global::Substrate.Gear.Client.NetApi.Model.Types.Base.", $t) - }; -} - -macro_rules! client_primitive { - ($t: expr) => { - concat!("global::Substrate.Gear.Client.NetApi.Model.Types.Primitive.", $t) - }; -} - -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(); - $(self.encode_tokens) - return result.ToArray(); - } - - $(inheritdoc()) - public override void Decode(byte[] byteArray, ref int p) - { - var start = p; - $(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 => - [System.Diagnostics.CodeAnalysis.AllowNull]$['\r'] - public $(&value_type) Value { get; set; }$['\r'] - }; - quote_in! { self.encode_tokens => - result.AddRange(this.Value.Encode());$['\r'] - }; - quote_in! { self.decode_tokens => - this.Value = new $(&value_type)();$['\r'] - 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); - return; - } - 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 => - [System.Diagnostics.CodeAnalysis.AllowNull]$['\r'] - public $(&type_decl_code) $(&field_name_pascal) { get; set; }$['\r'] - }; - quote_in! { self.encode_tokens => - result.AddRange(this.$(&field_name_pascal).Encode());$['\r'] - }; - quote_in! { self.decode_tokens => - this.$(&field_name_pascal) = new $(&type_decl_code)();$['\r'] - 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'] - } - } -} - -#[derive(Clone)] -pub(crate) struct TypeDeclGenerator<'a> { - code: String, - generated_types: &'a Vec<&'a Type>, -} - -impl<'a> TypeDeclGenerator<'a> { - pub(crate) fn new(generated_types: &'a Vec<&'a Type>) -> Self { - Self { - code: String::new(), - generated_types, - } - } - - pub(crate) fn generate_type_decl(&mut self, type_decl: &'a TypeDecl) -> String { - visitor::accept_type_decl(type_decl, self); - std::mem::take(&mut self.code) - } - - pub(crate) fn generate_struct_def(&mut self, struct_def: &'a StructDef) -> String { - visitor::accept_struct_def(struct_def, self); - std::mem::take(&mut self.code) - } - - pub(crate) fn generate_types_as_tuple(&mut self, type_decls: Vec<&'a TypeDecl>) -> String { - if type_decls.is_empty() { - } else if type_decls.len() == 1 { - visitor::accept_type_decl(type_decls[0], self); - } else { - self.code.push_str(base!("BaseTuple<")); - self.join_type_decls(type_decls, ", "); - self.code.push('>'); - } - std::mem::take(&mut self.code) - } - - fn join_type_decls>( - &mut self, - type_decls: I, - separator: &str, - ) { - let prev_code = std::mem::take(&mut self.code); - let mut type_decls_str: Vec = Vec::new(); - let iter = type_decls.into_iter(); - for type_decl in iter { - visitor::accept_type_decl(type_decl, self); - type_decls_str.push(std::mem::take(&mut self.code)); - } - _ = std::mem::replace(&mut self.code, prev_code); - self.code.push_str(type_decls_str.join(separator).as_str()); - } - - pub(crate) fn fn_params_with_types(&mut self, params: &'a [FuncParam]) -> String { - params - .iter() - .map(|p| { - format!( - "{} {}", - self.generate_type_decl(p.type_decl()), - escape_keywords(p.name().to_case(convert_case::Case::Camel)) - ) - }) - .collect::>() - .join(", ") - } -} - -impl<'a> Visitor<'a> for TypeDeclGenerator<'a> { - fn visit_optional_type_decl(&mut self, optional_type_decl: &'a TypeDecl) { - self.code.push_str(base!("BaseOpt<")); - visitor::accept_type_decl(optional_type_decl, self); - self.code.push('>'); - } - - fn visit_result_type_decl(&mut self, ok_type_decl: &'a TypeDecl, err_type_decl: &'a TypeDecl) { - self.code.push_str(client_base!("BaseResult<")); - visitor::accept_type_decl(ok_type_decl, self); - self.code.push_str(", "); - visitor::accept_type_decl(err_type_decl, self); - self.code.push('>'); - } - - fn visit_vector_type_decl(&mut self, vector_type_decl: &'a TypeDecl) { - self.code.push_str(base!("BaseVec<")); - visitor::accept_type_decl(vector_type_decl, self); - self.code.push('>'); - } - - fn visit_struct_def(&mut self, struct_def: &'a StructDef) { - if struct_def.fields().is_empty() { - return; - } - if struct_def.fields().len() == 1 { - visitor::accept_type_decl(struct_def.fields()[0].type_decl(), self); - } else { - self.code.push_str(base!("BaseTuple<")); - self.join_type_decls(struct_def.fields().iter().map(|f| f.type_decl()), ", "); - self.code.push('>'); - } - } - - fn visit_primitive_type_id(&mut self, primitive_type_id: PrimitiveType) { - self.code - .push_str(primitive_type_to_dotnet(primitive_type_id)); - } - - fn visit_user_defined_type_id(&mut self, user_defined_type_id: &'a str) { - let is_enum = self - .generated_types - .iter() - .find(|&&t| t.name() == user_defined_type_id) - .map(|&t| matches!(t.def(), TypeDef::Enum(_))) - .unwrap_or_default(); - let type_id = if is_enum { - &format!("Enum{}", user_defined_type_id) // Enum prefix - } else { - user_defined_type_id - }; - self.code.push_str(type_id); - } - - fn visit_map_type_decl(&mut self, key_type_decl: &'a TypeDecl, value_type_decl: &'a TypeDecl) { - self.code.push_str(client_base!("BaseDictionary<")); - visitor::accept_type_decl(key_type_decl, self); - self.code.push_str(", "); - visitor::accept_type_decl(value_type_decl, self); - self.code.push('>'); - } - - fn visit_array_type_decl(&mut self, item_type_decl: &'a TypeDecl, _len: u32) { - visitor::accept_type_decl(item_type_decl, self); - self.code.push_str("[]"); - } -} - -pub(crate) fn primitive_type_to_dotnet(primitive_type: PrimitiveType) -> &'static str { - match primitive_type { - PrimitiveType::U8 => primitive!("U8"), - PrimitiveType::U16 => primitive!("U16"), - PrimitiveType::U32 => primitive!("U32"), - PrimitiveType::U64 => primitive!("U64"), - PrimitiveType::U128 => primitive!("U128"), - PrimitiveType::I8 => primitive!("I8"), - PrimitiveType::I16 => primitive!("I16"), - PrimitiveType::I32 => primitive!("I32"), - PrimitiveType::I64 => primitive!("I64"), - PrimitiveType::I128 => primitive!("I128"), - PrimitiveType::Bool => primitive!("Bool"), - PrimitiveType::Str => primitive!("Str"), - PrimitiveType::Char => primitive!("PrimChar"), - PrimitiveType::Null => base!("BaseVoid"), - PrimitiveType::ActorId => gprimitives!("ActorId"), - PrimitiveType::CodeId => gprimitives!("CodeId"), - PrimitiveType::MessageId => gprimitives!("MessageId"), - PrimitiveType::H160 => client_primitive!("H160"), - PrimitiveType::H256 => "global::Substrate.Gear.Api.Generated.Model.primitive_types.H256", - PrimitiveType::U256 => primitive!("U256"), - PrimitiveType::NonZeroU8 => client_primitive!("NonZeroU8"), - PrimitiveType::NonZeroU16 => client_primitive!("NonZeroU16"), - PrimitiveType::NonZeroU32 => "global::Substrate.Gear.Api.Generated.Types.Base.NonZeroU32", - PrimitiveType::NonZeroU64 => client_primitive!("NonZeroU64"), - PrimitiveType::NonZeroU128 => client_primitive!("NonZeroU128"), - PrimitiveType::NonZeroU256 => client_primitive!("NonZeroU256"), - } -} diff --git a/net/rs/client-gen/tests/snapshots/generator__basic_works.snap b/net/rs/client-gen/tests/snapshots/generator__basic_works.snap index a44d4456..7974674d 100644 --- a/net/rs/client-gen/tests/snapshots/generator__basic_works.snap +++ b/net/rs/client-gen/tests/snapshots/generator__basic_works.snap @@ -2,12 +2,15 @@ source: client-gen/tests/generator.rs expression: "gen(idl, \"Basic\")" --- +// using global::Sails.Remoting; using global::Sails.Remoting.Abstractions; using global::Sails.Remoting.Abstractions.Core; using global::System; using global::System.Collections.Generic; +#nullable enable + #pragma warning disable RCS0056 // A line is too long namespace Basic.Client; @@ -23,31 +26,28 @@ ICall DoThat(global::Substrat public Basic(IRemoting remoting) { this.remoting = remoting; } /// - public ICall DoThis(global::Substrate.NetApi.Model.Types.Primitive.U32 p1, MyParam p2) { return new RemotingAction( this.remoting, [20, 66, 97, 115, 105, 99, 24, 68, 111, 84, 104, 105, 115], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust(p1, p2) ); } + public ICall DoThis(global::Substrate.NetApi.Model.Types.Primitive.U32 p1, MyParam p2) { return new RemotingAction(this.remoting, [20, 66, 97, 115, 105, 99, 24, 68, 111, 84, 104, 105, 115], p1, p2); } /// - public ICall DoThat(global::Substrate.NetApi.Model.Types.Base.BaseTuple p1) { return new RemotingAction( this.remoting, [20, 66, 97, 115, 105, 99, 24, 68, 111, 84, 104, 97, 116], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust(p1) ); } } + public ICall DoThat(global::Substrate.NetApi.Model.Types.Base.BaseTuple p1) { return new RemotingAction(this.remoting, [20, 66, 97, 115, 105, 99, 24, 68, 111, 84, 104, 97, 116], p1); } } public sealed partial class MyParam : global::Substrate.NetApi.Model.Types.Base.BaseType { -[System.Diagnostics.CodeAnalysis.AllowNull] - public global::Substrate.NetApi.Model.Types.Primitive.U32 F1 { get; set; } -[System.Diagnostics.CodeAnalysis.AllowNull] - public global::Substrate.NetApi.Model.Types.Base.BaseVec F2 { get; set; } -[System.Diagnostics.CodeAnalysis.AllowNull] - public global::Substrate.NetApi.Model.Types.Base.BaseOpt> F3 { get; set; } +public global::Substrate.NetApi.Model.Types.Primitive.U32 F1 { get; init; } = new(); +public global::Substrate.NetApi.Model.Types.Base.BaseVec F2 { get; init; } = new(); +public global::Substrate.NetApi.Model.Types.Base.BaseOpt> F3 { get; init; } = new(); /// public override string TypeName() => "MyParam"; /// - public override byte[] Encode() { var result = new List(); result.AddRange(this.F1.Encode()); + public override byte[] Encode() { var result = new List(); + result.AddRange(this.F1.Encode()); result.AddRange(this.F2.Encode()); result.AddRange(this.F3.Encode()); - return result.ToArray(); } + return result.ToArray(); + } /// - public override void Decode(byte[] byteArray, ref int p) { var start = p; this.F1 = new global::Substrate.NetApi.Model.Types.Primitive.U32(); + public override void Decode(byte[] byteArray, ref int p) { var start = p; this.F1.Decode(byteArray, ref p); -this.F2 = new global::Substrate.NetApi.Model.Types.Base.BaseVec(); - this.F2.Decode(byteArray, ref p); -this.F3 = new global::Substrate.NetApi.Model.Types.Base.BaseOpt>(); - this.F3.Decode(byteArray, ref p); +this.F2.Decode(byteArray, ref p); +this.F3.Decode(byteArray, ref p); var bytesLength = p - start; this.TypeSize = bytesLength; this.Bytes = new byte[bytesLength]; Array.Copy(byteArray, start, this.Bytes, 0, bytesLength); } } public enum MyParam2 { Variant1, diff --git a/net/rs/client-gen/tests/snapshots/generator__events_works.snap b/net/rs/client-gen/tests/snapshots/generator__events_works.snap index 275b8806..277b30b4 100644 --- a/net/rs/client-gen/tests/snapshots/generator__events_works.snap +++ b/net/rs/client-gen/tests/snapshots/generator__events_works.snap @@ -2,12 +2,15 @@ source: client-gen/tests/generator.rs expression: "gen(idl, \"ServiceWithEvents\")" --- +// using global::Sails.Remoting; using global::Sails.Remoting.Abstractions; using global::Sails.Remoting.Abstractions.Core; using global::System; using global::System.Collections.Generic; +#nullable enable + #pragma warning disable RCS0056 // A line is too long namespace ServiceWithEvents.Client; @@ -22,7 +25,7 @@ public interface IServiceWithEvents public ServiceWithEvents(IRemoting remoting) { this.remoting = remoting; } /// - public ICall DoThis(global::Substrate.Gear.Client.NetApi.Model.Types.Primitive.NonZeroU256 p1, MyParam p2) { return new RemotingAction( this.remoting, [68, 83, 101, 114, 118, 105, 99, 101, 87, 105, 116, 104, 69, 118, 101, 110, 116, 115, 24, 68, 111, 84, 104, 105, 115], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust(p1, p2) ); } } + public ICall DoThis(global::Substrate.Gear.Client.NetApi.Model.Types.Primitive.NonZeroU256 p1, MyParam p2) { return new RemotingAction(this.remoting, [68, 83, 101, 114, 118, 105, 99, 101, 87, 105, 116, 104, 69, 118, 101, 110, 116, 115, 24, 68, 111, 84, 104, 105, 115], p1, p2); } } public enum ServiceWithEventsEvents { One, Two, @@ -45,24 +48,21 @@ this.AddTypeDecoder(ServiceW public async global::System.Collections.Generic.IAsyncEnumerable ListenAsync([global::System.Runtime.CompilerServices.EnumeratorCancellation] global::System.Threading.CancellationToken cancellationToken = default) { await foreach (var bytes in this.remoting.ListenAsync(cancellationToken)) { byte idx = 0; foreach (var route in EventRoutes) { if (route.Length > bytes.Length) { continue; } if (route.AsSpan().SequenceEqual(bytes.AsSpan()[..route.Length])) { var bytesLength = bytes.Length - route.Length + 1; var data = new byte[bytesLength]; data[0] = idx; Buffer.BlockCopy(bytes, route.Length, data, 1, bytes.Length - route.Length); var p = 0; EnumServiceWithEventsEvents ev = new(); ev.Decode(bytes, ref p); yield return ev; } idx++; } } } } public sealed partial class MyParam : global::Substrate.NetApi.Model.Types.Base.BaseType { -[System.Diagnostics.CodeAnalysis.AllowNull] - public global::Substrate.Gear.Client.NetApi.Model.Types.Primitive.NonZeroU256 F1 { get; set; } -[System.Diagnostics.CodeAnalysis.AllowNull] - public global::Substrate.NetApi.Model.Types.Base.BaseVec F2 { get; set; } -[System.Diagnostics.CodeAnalysis.AllowNull] - public global::Substrate.NetApi.Model.Types.Base.BaseOpt> F3 { get; set; } +public global::Substrate.Gear.Client.NetApi.Model.Types.Primitive.NonZeroU256 F1 { get; init; } = new(); +public global::Substrate.NetApi.Model.Types.Base.BaseVec F2 { get; init; } = new(); +public global::Substrate.NetApi.Model.Types.Base.BaseOpt> F3 { get; init; } = new(); /// public override string TypeName() => "MyParam"; /// - public override byte[] Encode() { var result = new List(); result.AddRange(this.F1.Encode()); + public override byte[] Encode() { var result = new List(); + result.AddRange(this.F1.Encode()); result.AddRange(this.F2.Encode()); result.AddRange(this.F3.Encode()); - return result.ToArray(); } + return result.ToArray(); + } /// - public override void Decode(byte[] byteArray, ref int p) { var start = p; this.F1 = new global::Substrate.Gear.Client.NetApi.Model.Types.Primitive.NonZeroU256(); + public override void Decode(byte[] byteArray, ref int p) { var start = p; this.F1.Decode(byteArray, ref p); -this.F2 = new global::Substrate.NetApi.Model.Types.Base.BaseVec(); - this.F2.Decode(byteArray, ref p); -this.F3 = new global::Substrate.NetApi.Model.Types.Base.BaseOpt>(); - this.F3.Decode(byteArray, ref p); +this.F2.Decode(byteArray, ref p); +this.F3.Decode(byteArray, ref p); var bytesLength = p - start; this.TypeSize = bytesLength; this.Bytes = new byte[bytesLength]; Array.Copy(byteArray, start, this.Bytes, 0, bytesLength); } } diff --git a/net/rs/client-gen/tests/snapshots/generator__full.snap b/net/rs/client-gen/tests/snapshots/generator__full.snap index 4686e229..2833129d 100644 --- a/net/rs/client-gen/tests/snapshots/generator__full.snap +++ b/net/rs/client-gen/tests/snapshots/generator__full.snap @@ -2,12 +2,15 @@ source: client-gen/tests/generator.rs expression: "gen(IDL, \"Service\")" --- +// using global::Sails.Remoting; using global::Sails.Remoting.Abstractions; using global::Sails.Remoting.Abstractions.Core; using global::System; using global::System.Collections.Generic; +#nullable enable + #pragma warning disable RCS0056 // A line is too long namespace Service.Client; @@ -29,7 +32,7 @@ IActivation New(global::Substrate.NetApi.Model.Types.Primitive.U32 a); { this.remoting = remoting; } /// - public IActivation New(global::Substrate.NetApi.Model.Types.Primitive.U32 a) { return new RemotingAction( this.remoting, [12, 78, 101, 119], new global::Substrate.NetApi.Model.Types.Primitive.U32(a)); } + public IActivation New(global::Substrate.NetApi.Model.Types.Primitive.U32 a) { return new RemotingAction(this.remoting, [12, 78, 101, 119], a); } } @@ -46,13 +49,13 @@ IQuery - public ICall> DoThis(global::Substrate.NetApi.Model.Types.Primitive.U32 p1, global::Substrate.NetApi.Model.Types.Primitive.Str p2, global::Substrate.NetApi.Model.Types.Base.BaseTuple, global::Substrate.NetApi.Model.Types.Primitive.U8> p3, ThisThatSvcAppTupleStruct p4) { return new RemotingAction>( this.remoting, [28, 83, 101, 114, 118, 105, 99, 101, 24, 68, 111, 84, 104, 105, 115], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust(p1, p2, p3, p4) ); } + public ICall> DoThis(global::Substrate.NetApi.Model.Types.Primitive.U32 p1, global::Substrate.NetApi.Model.Types.Primitive.Str p2, global::Substrate.NetApi.Model.Types.Base.BaseTuple, global::Substrate.NetApi.Model.Types.Primitive.U8> p3, ThisThatSvcAppTupleStruct p4) { return new RemotingAction>(this.remoting, [28, 83, 101, 114, 118, 105, 99, 101, 24, 68, 111, 84, 104, 105, 115], p1, p2, p3, p4); } /// - public ICall, global::Substrate.NetApi.Model.Types.Primitive.Str>> DoThat(ThisThatSvcAppDoThatParam param) { return new RemotingAction, global::Substrate.NetApi.Model.Types.Primitive.Str>>( this.remoting, [28, 83, 101, 114, 118, 105, 99, 101, 24, 68, 111, 84, 104, 97, 116], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust(param) ); } + public ICall, global::Substrate.NetApi.Model.Types.Primitive.Str>> DoThat(ThisThatSvcAppDoThatParam param) { return new RemotingAction, global::Substrate.NetApi.Model.Types.Primitive.Str>>(this.remoting, [28, 83, 101, 114, 118, 105, 99, 101, 24, 68, 111, 84, 104, 97, 116], param); } /// - public IQuery This(global::Substrate.NetApi.Model.Types.Base.BaseVec v1) { return new RemotingAction( this.remoting, [28, 83, 101, 114, 118, 105, 99, 101, 16, 84, 104, 105, 115], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust(v1) ); } + public IQuery This(global::Substrate.NetApi.Model.Types.Base.BaseVec v1) { return new RemotingAction(this.remoting, [28, 83, 101, 114, 118, 105, 99, 101, 16, 84, 104, 105, 115], v1); } /// - public IQuery> That(global::Substrate.NetApi.Model.Types.Base.BaseVoid v1) { return new RemotingAction>( this.remoting, [28, 83, 101, 114, 118, 105, 99, 101, 16, 84, 104, 97, 116], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust(v1) ); } } + public IQuery> That(global::Substrate.NetApi.Model.Types.Base.BaseVoid v1) { return new RemotingAction>(this.remoting, [28, 83, 101, 114, 118, 105, 99, 101, 16, 84, 104, 97, 116], v1); } } public enum ServiceEvents { /// @@ -80,15 +83,16 @@ this.AddTypeDecoder(ServiceE /// /// ThisThatSvcAppTupleStruct docs /// -public sealed partial class ThisThatSvcAppTupleStruct : global::Substrate.NetApi.Model.Types.Base.BaseType { [System.Diagnostics.CodeAnalysis.AllowNull] - public global::Substrate.NetApi.Model.Types.Primitive.Bool Value { get; set; } +public sealed partial class ThisThatSvcAppTupleStruct : global::Substrate.NetApi.Model.Types.Base.BaseType { public global::Substrate.NetApi.Model.Types.Primitive.Bool Value { get; init; } = new(); /// public override string TypeName() => "ThisThatSvcAppTupleStruct"; /// - public override byte[] Encode() { var result = new List(); result.AddRange(this.Value.Encode()); - return result.ToArray(); } + public override byte[] Encode() { var result = new List(); + result.AddRange(this.Value.Encode()); + return result.ToArray(); + } /// - public override void Decode(byte[] byteArray, ref int p) { var start = p; this.Value = new global::Substrate.NetApi.Model.Types.Primitive.Bool(); + public override void Decode(byte[] byteArray, ref int p) { var start = p; this.Value.Decode(byteArray, ref p); var bytesLength = p - start; this.TypeSize = bytesLength; this.Bytes = new byte[bytesLength]; Array.Copy(byteArray, start, this.Bytes, 0, bytesLength); } } @@ -99,32 +103,29 @@ public sealed partial class ThisThatSvcAppDoThatParam : global::Substrate.NetApi /// /// field `query` /// -[System.Diagnostics.CodeAnalysis.AllowNull] - public global::Substrate.NetApi.Model.Types.Primitive.U32 Query { get; set; } +public global::Substrate.NetApi.Model.Types.Primitive.U32 Query { get; init; } = new(); /// /// field `result` /// -[System.Diagnostics.CodeAnalysis.AllowNull] - public global::Substrate.NetApi.Model.Types.Primitive.Str Result { get; set; } +public global::Substrate.NetApi.Model.Types.Primitive.Str Result { get; init; } = new(); /// /// field `p3` /// -[System.Diagnostics.CodeAnalysis.AllowNull] - public EnumThisThatSvcAppManyVariants P3 { get; set; } +public EnumThisThatSvcAppManyVariants P3 { get; init; } = new(); /// public override string TypeName() => "ThisThatSvcAppDoThatParam"; /// - public override byte[] Encode() { var result = new List(); result.AddRange(this.Query.Encode()); + public override byte[] Encode() { var result = new List(); + result.AddRange(this.Query.Encode()); result.AddRange(this.Result.Encode()); result.AddRange(this.P3.Encode()); - return result.ToArray(); } + return result.ToArray(); + } /// - public override void Decode(byte[] byteArray, ref int p) { var start = p; this.Query = new global::Substrate.NetApi.Model.Types.Primitive.U32(); + public override void Decode(byte[] byteArray, ref int p) { var start = p; this.Query.Decode(byteArray, ref p); -this.Result = new global::Substrate.NetApi.Model.Types.Primitive.Str(); - this.Result.Decode(byteArray, ref p); -this.P3 = new EnumThisThatSvcAppManyVariants(); - this.P3.Decode(byteArray, ref p); +this.Result.Decode(byteArray, ref p); +this.P3.Decode(byteArray, ref p); var bytesLength = p - start; this.TypeSize = bytesLength; this.Bytes = new byte[bytesLength]; Array.Copy(byteArray, start, this.Bytes, 0, bytesLength); } } /// diff --git a/net/rs/client-gen/tests/snapshots/generator__multiple_services.snap b/net/rs/client-gen/tests/snapshots/generator__multiple_services.snap index d2185fe8..42051a25 100644 --- a/net/rs/client-gen/tests/snapshots/generator__multiple_services.snap +++ b/net/rs/client-gen/tests/snapshots/generator__multiple_services.snap @@ -2,10 +2,13 @@ source: client-gen/tests/generator.rs expression: "gen(idl, \"Multiple\")" --- +// using global::Sails.Remoting; using global::Sails.Remoting.Abstractions; using global::Sails.Remoting.Abstractions.Core; +#nullable enable + #pragma warning disable RCS0056 // A line is too long namespace Multiple.Client; @@ -21,9 +24,9 @@ ICall DoThat(global::Substrat public Multiple(IRemoting remoting) { this.remoting = remoting; } /// - public ICall DoThis(global::Substrate.NetApi.Model.Types.Primitive.U32 p1, MyParam p2) { return new RemotingAction( this.remoting, [32, 77, 117, 108, 116, 105, 112, 108, 101, 24, 68, 111, 84, 104, 105, 115], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust(p1, p2) ); } + public ICall DoThis(global::Substrate.NetApi.Model.Types.Primitive.U32 p1, MyParam p2) { return new RemotingAction(this.remoting, [32, 77, 117, 108, 116, 105, 112, 108, 101, 24, 68, 111, 84, 104, 105, 115], p1, p2); } /// - public ICall DoThat(global::Substrate.NetApi.Model.Types.Base.BaseTuple p1) { return new RemotingAction( this.remoting, [32, 77, 117, 108, 116, 105, 112, 108, 101, 24, 68, 111, 84, 104, 97, 116], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust(p1) ); } } + public ICall DoThat(global::Substrate.NetApi.Model.Types.Base.BaseTuple p1) { return new RemotingAction(this.remoting, [32, 77, 117, 108, 116, 105, 112, 108, 101, 24, 68, 111, 84, 104, 97, 116], p1); } } public interface INamed { IQuery That(global::Substrate.NetApi.Model.Types.Primitive.U32 p1); @@ -35,4 +38,4 @@ public interface INamed public Named(IRemoting remoting) { this.remoting = remoting; } /// - public IQuery That(global::Substrate.NetApi.Model.Types.Primitive.U32 p1) { return new RemotingAction( this.remoting, [20, 78, 97, 109, 101, 100, 16, 84, 104, 97, 116], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust(p1) ); } } + public IQuery That(global::Substrate.NetApi.Model.Types.Primitive.U32 p1) { return new RemotingAction(this.remoting, [20, 78, 97, 109, 101, 100, 16, 84, 104, 97, 116], p1); } } diff --git a/net/rs/client-gen/tests/snapshots/generator__nonzero_works.snap b/net/rs/client-gen/tests/snapshots/generator__nonzero_works.snap index 2a275b62..a9b2f3ab 100644 --- a/net/rs/client-gen/tests/snapshots/generator__nonzero_works.snap +++ b/net/rs/client-gen/tests/snapshots/generator__nonzero_works.snap @@ -2,12 +2,15 @@ source: client-gen/tests/generator.rs expression: "gen(idl, \"NonZeroParams\")" --- +// using global::Sails.Remoting; using global::Sails.Remoting.Abstractions; using global::Sails.Remoting.Abstractions.Core; using global::System; using global::System.Collections.Generic; +#nullable enable + #pragma warning disable RCS0056 // A line is too long namespace NonZeroParams.Client; @@ -22,27 +25,24 @@ public interface INonZeroParams public NonZeroParams(IRemoting remoting) { this.remoting = remoting; } /// - public ICall DoThis(global::Substrate.Gear.Client.NetApi.Model.Types.Primitive.NonZeroU256 p1, MyParam p2) { return new RemotingAction( this.remoting, [52, 78, 111, 110, 90, 101, 114, 111, 80, 97, 114, 97, 109, 115, 24, 68, 111, 84, 104, 105, 115], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust(p1, p2) ); } } + public ICall DoThis(global::Substrate.Gear.Client.NetApi.Model.Types.Primitive.NonZeroU256 p1, MyParam p2) { return new RemotingAction(this.remoting, [52, 78, 111, 110, 90, 101, 114, 111, 80, 97, 114, 97, 109, 115, 24, 68, 111, 84, 104, 105, 115], p1, p2); } } public sealed partial class MyParam : global::Substrate.NetApi.Model.Types.Base.BaseType { -[System.Diagnostics.CodeAnalysis.AllowNull] - public global::Substrate.Gear.Client.NetApi.Model.Types.Primitive.NonZeroU256 F1 { get; set; } -[System.Diagnostics.CodeAnalysis.AllowNull] - public global::Substrate.NetApi.Model.Types.Base.BaseVec F2 { get; set; } -[System.Diagnostics.CodeAnalysis.AllowNull] - public global::Substrate.NetApi.Model.Types.Base.BaseOpt> F3 { get; set; } +public global::Substrate.Gear.Client.NetApi.Model.Types.Primitive.NonZeroU256 F1 { get; init; } = new(); +public global::Substrate.NetApi.Model.Types.Base.BaseVec F2 { get; init; } = new(); +public global::Substrate.NetApi.Model.Types.Base.BaseOpt> F3 { get; init; } = new(); /// public override string TypeName() => "MyParam"; /// - public override byte[] Encode() { var result = new List(); result.AddRange(this.F1.Encode()); + public override byte[] Encode() { var result = new List(); + result.AddRange(this.F1.Encode()); result.AddRange(this.F2.Encode()); result.AddRange(this.F3.Encode()); - return result.ToArray(); } + return result.ToArray(); + } /// - public override void Decode(byte[] byteArray, ref int p) { var start = p; this.F1 = new global::Substrate.Gear.Client.NetApi.Model.Types.Primitive.NonZeroU256(); + public override void Decode(byte[] byteArray, ref int p) { var start = p; this.F1.Decode(byteArray, ref p); -this.F2 = new global::Substrate.NetApi.Model.Types.Base.BaseVec(); - this.F2.Decode(byteArray, ref p); -this.F3 = new global::Substrate.NetApi.Model.Types.Base.BaseOpt>(); - this.F3.Decode(byteArray, ref p); +this.F2.Decode(byteArray, ref p); +this.F3.Decode(byteArray, ref p); var bytesLength = p - start; this.TypeSize = bytesLength; this.Bytes = new byte[bytesLength]; Array.Copy(byteArray, start, this.Bytes, 0, bytesLength); } } diff --git a/net/rs/client-gen/tests/snapshots/generator__rmrk_works.snap b/net/rs/client-gen/tests/snapshots/generator__rmrk_works.snap index 18418206..6d04d28b 100644 --- a/net/rs/client-gen/tests/snapshots/generator__rmrk_works.snap +++ b/net/rs/client-gen/tests/snapshots/generator__rmrk_works.snap @@ -2,12 +2,15 @@ source: client-gen/tests/generator.rs expression: "gen(idl, \"RmrkCatalog\")" --- +// using global::Sails.Remoting; using global::Sails.Remoting.Abstractions; using global::Sails.Remoting.Abstractions.Core; using global::System; using global::System.Collections.Generic; +#nullable enable + #pragma warning disable RCS0056 // A line is too long namespace RmrkCatalog.Client; @@ -26,7 +29,7 @@ IActivation New(); { this.remoting = remoting; } /// - public IActivation New() { return new RemotingAction( this.remoting, [12, 78, 101, 119], new global::Substrate.NetApi.Model.Types.Base.BaseVoid()); } + public IActivation New() { return new RemotingAction(this.remoting, [12, 78, 101, 119]); } } @@ -47,21 +50,21 @@ IQuery> Part(global::Sub public RmrkCatalog(IRemoting remoting) { this.remoting = remoting; } /// - public ICall>, Error>> AddEquippables(global::Substrate.NetApi.Model.Types.Primitive.U32 partId, global::Substrate.NetApi.Model.Types.Base.BaseVec collectionIds) { return new RemotingAction>, Error>>( this.remoting, [44, 82, 109, 114, 107, 67, 97, 116, 97, 108, 111, 103, 56, 65, 100, 100, 69, 113, 117, 105, 112, 112, 97, 98, 108, 101, 115], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust(partId, collectionIds) ); } + public ICall>, Error>> AddEquippables(global::Substrate.NetApi.Model.Types.Primitive.U32 partId, global::Substrate.NetApi.Model.Types.Base.BaseVec collectionIds) { return new RemotingAction>, Error>>(this.remoting, [44, 82, 109, 114, 107, 67, 97, 116, 97, 108, 111, 103, 56, 65, 100, 100, 69, 113, 117, 105, 112, 112, 97, 98, 108, 101, 115], partId, collectionIds); } /// - public ICall, Error>> AddParts(global::Substrate.Gear.Client.NetApi.Model.Types.Base.BaseDictionary parts) { return new RemotingAction, Error>>( this.remoting, [44, 82, 109, 114, 107, 67, 97, 116, 97, 108, 111, 103, 32, 65, 100, 100, 80, 97, 114, 116, 115], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust(parts) ); } + public ICall, Error>> AddParts(global::Substrate.Gear.Client.NetApi.Model.Types.Base.BaseDictionary parts) { return new RemotingAction, Error>>(this.remoting, [44, 82, 109, 114, 107, 67, 97, 116, 97, 108, 111, 103, 32, 65, 100, 100, 80, 97, 114, 116, 115], parts); } /// - public ICall, Error>> RemoveEquippable(global::Substrate.NetApi.Model.Types.Primitive.U32 partId, global::Substrate.Gear.Api.Generated.Model.gprimitives.ActorId collectionId) { return new RemotingAction, Error>>( this.remoting, [44, 82, 109, 114, 107, 67, 97, 116, 97, 108, 111, 103, 64, 82, 101, 109, 111, 118, 101, 69, 113, 117, 105, 112, 112, 97, 98, 108, 101], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust(partId, collectionId) ); } + public ICall, Error>> RemoveEquippable(global::Substrate.NetApi.Model.Types.Primitive.U32 partId, global::Substrate.Gear.Api.Generated.Model.gprimitives.ActorId collectionId) { return new RemotingAction, Error>>(this.remoting, [44, 82, 109, 114, 107, 67, 97, 116, 97, 108, 111, 103, 64, 82, 101, 109, 111, 118, 101, 69, 113, 117, 105, 112, 112, 97, 98, 108, 101], partId, collectionId); } /// - public ICall, Error>> RemoveParts(global::Substrate.NetApi.Model.Types.Base.BaseVec partIds) { return new RemotingAction, Error>>( this.remoting, [44, 82, 109, 114, 107, 67, 97, 116, 97, 108, 111, 103, 44, 82, 101, 109, 111, 118, 101, 80, 97, 114, 116, 115], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust(partIds) ); } + public ICall, Error>> RemoveParts(global::Substrate.NetApi.Model.Types.Base.BaseVec partIds) { return new RemotingAction, Error>>(this.remoting, [44, 82, 109, 114, 107, 67, 97, 116, 97, 108, 111, 103, 44, 82, 101, 109, 111, 118, 101, 80, 97, 114, 116, 115], partIds); } /// - public ICall> ResetEquippables(global::Substrate.NetApi.Model.Types.Primitive.U32 partId) { return new RemotingAction>( this.remoting, [44, 82, 109, 114, 107, 67, 97, 116, 97, 108, 111, 103, 64, 82, 101, 115, 101, 116, 69, 113, 117, 105, 112, 112, 97, 98, 108, 101, 115], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust(partId) ); } + public ICall> ResetEquippables(global::Substrate.NetApi.Model.Types.Primitive.U32 partId) { return new RemotingAction>(this.remoting, [44, 82, 109, 114, 107, 67, 97, 116, 97, 108, 111, 103, 64, 82, 101, 115, 101, 116, 69, 113, 117, 105, 112, 112, 97, 98, 108, 101, 115], partId); } /// - public ICall> SetEquippablesToAll(global::Substrate.NetApi.Model.Types.Primitive.U32 partId) { return new RemotingAction>( this.remoting, [44, 82, 109, 114, 107, 67, 97, 116, 97, 108, 111, 103, 76, 83, 101, 116, 69, 113, 117, 105, 112, 112, 97, 98, 108, 101, 115, 84, 111, 65, 108, 108], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust(partId) ); } + public ICall> SetEquippablesToAll(global::Substrate.NetApi.Model.Types.Primitive.U32 partId) { return new RemotingAction>(this.remoting, [44, 82, 109, 114, 107, 67, 97, 116, 97, 108, 111, 103, 76, 83, 101, 116, 69, 113, 117, 105, 112, 112, 97, 98, 108, 101, 115, 84, 111, 65, 108, 108], partId); } /// - public IQuery> Equippable(global::Substrate.NetApi.Model.Types.Primitive.U32 partId, global::Substrate.Gear.Api.Generated.Model.gprimitives.ActorId collectionId) { return new RemotingAction>( this.remoting, [44, 82, 109, 114, 107, 67, 97, 116, 97, 108, 111, 103, 40, 69, 113, 117, 105, 112, 112, 97, 98, 108, 101], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust(partId, collectionId) ); } + public IQuery> Equippable(global::Substrate.NetApi.Model.Types.Primitive.U32 partId, global::Substrate.Gear.Api.Generated.Model.gprimitives.ActorId collectionId) { return new RemotingAction>(this.remoting, [44, 82, 109, 114, 107, 67, 97, 116, 97, 108, 111, 103, 40, 69, 113, 117, 105, 112, 112, 97, 98, 108, 101], partId, collectionId); } /// - public IQuery> Part(global::Substrate.NetApi.Model.Types.Primitive.U32 partId) { return new RemotingAction>( this.remoting, [44, 82, 109, 114, 107, 67, 97, 116, 97, 108, 111, 103, 16, 80, 97, 114, 116], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust(partId) ); } } + public IQuery> Part(global::Substrate.NetApi.Model.Types.Primitive.U32 partId) { return new RemotingAction>(this.remoting, [44, 82, 109, 114, 107, 67, 97, 116, 97, 108, 111, 103, 16, 80, 97, 114, 116], partId); } } public enum Error { PartIdCantBeZero, BadConfig, @@ -91,56 +94,52 @@ public sealed partial class FixedPart : global::Substrate.NetApi.Model.Types.Bas /// specifies the stack order of an element. /// An element with greater stack order is always in front of an element with a lower stack order. /// -[System.Diagnostics.CodeAnalysis.AllowNull] - public global::Substrate.NetApi.Model.Types.Base.BaseOpt Z { get; set; } +public global::Substrate.NetApi.Model.Types.Base.BaseOpt Z { get; init; } = new(); /// /// The metadata URI of the part. /// -[System.Diagnostics.CodeAnalysis.AllowNull] - public global::Substrate.NetApi.Model.Types.Primitive.Str MetadataUri { get; set; } +public global::Substrate.NetApi.Model.Types.Primitive.Str MetadataUri { get; init; } = new(); /// public override string TypeName() => "FixedPart"; /// - public override byte[] Encode() { var result = new List(); result.AddRange(this.Z.Encode()); + public override byte[] Encode() { var result = new List(); + result.AddRange(this.Z.Encode()); result.AddRange(this.MetadataUri.Encode()); - return result.ToArray(); } + return result.ToArray(); + } /// - public override void Decode(byte[] byteArray, ref int p) { var start = p; this.Z = new global::Substrate.NetApi.Model.Types.Base.BaseOpt(); + public override void Decode(byte[] byteArray, ref int p) { var start = p; this.Z.Decode(byteArray, ref p); -this.MetadataUri = new global::Substrate.NetApi.Model.Types.Primitive.Str(); - this.MetadataUri.Decode(byteArray, ref p); +this.MetadataUri.Decode(byteArray, ref p); var bytesLength = p - start; this.TypeSize = bytesLength; this.Bytes = new byte[bytesLength]; Array.Copy(byteArray, start, this.Bytes, 0, bytesLength); } } public sealed partial class SlotPart : global::Substrate.NetApi.Model.Types.Base.BaseType { /// /// Array of whitelisted collections that can be equipped in the given slot. Used with slot parts only. /// -[System.Diagnostics.CodeAnalysis.AllowNull] - public global::Substrate.NetApi.Model.Types.Base.BaseVec Equippable { get; set; } +public global::Substrate.NetApi.Model.Types.Base.BaseVec Equippable { get; init; } = new(); /// /// An optional zIndex of base part layer. /// specifies the stack order of an element. /// An element with greater stack order is always in front of an element with a lower stack order. /// -[System.Diagnostics.CodeAnalysis.AllowNull] - public global::Substrate.NetApi.Model.Types.Base.BaseOpt Z { get; set; } +public global::Substrate.NetApi.Model.Types.Base.BaseOpt Z { get; init; } = new(); /// /// The metadata URI of the part. /// -[System.Diagnostics.CodeAnalysis.AllowNull] - public global::Substrate.NetApi.Model.Types.Primitive.Str MetadataUri { get; set; } +public global::Substrate.NetApi.Model.Types.Primitive.Str MetadataUri { get; init; } = new(); /// public override string TypeName() => "SlotPart"; /// - public override byte[] Encode() { var result = new List(); result.AddRange(this.Equippable.Encode()); + public override byte[] Encode() { var result = new List(); + result.AddRange(this.Equippable.Encode()); result.AddRange(this.Z.Encode()); result.AddRange(this.MetadataUri.Encode()); - return result.ToArray(); } + return result.ToArray(); + } /// - public override void Decode(byte[] byteArray, ref int p) { var start = p; this.Equippable = new global::Substrate.NetApi.Model.Types.Base.BaseVec(); + public override void Decode(byte[] byteArray, ref int p) { var start = p; this.Equippable.Decode(byteArray, ref p); -this.Z = new global::Substrate.NetApi.Model.Types.Base.BaseOpt(); - this.Z.Decode(byteArray, ref p); -this.MetadataUri = new global::Substrate.NetApi.Model.Types.Primitive.Str(); - this.MetadataUri.Decode(byteArray, ref p); +this.Z.Decode(byteArray, ref p); +this.MetadataUri.Decode(byteArray, ref p); var bytesLength = p - start; this.TypeSize = bytesLength; this.Bytes = new byte[bytesLength]; Array.Copy(byteArray, start, this.Bytes, 0, bytesLength); } } diff --git a/net/src/Sails.Remoting/RemotingAction.cs b/net/src/Sails.Remoting/RemotingAction.cs index 760983b4..20dd4a68 100644 --- a/net/src/Sails.Remoting/RemotingAction.cs +++ b/net/src/Sails.Remoting/RemotingAction.cs @@ -11,11 +11,11 @@ namespace Sails.Remoting; -public sealed class RemotingAction(IRemoting remoting, byte[] route, IType args) : IActivation, IQuery, ICall +public sealed class RemotingAction(IRemoting remoting, byte[] route, params IType[] args) : IActivation, IQuery, ICall where T : IType, new() { private GasUnit? gasLimit; - private ValueUnit value = new(); + private ValueUnit value = new(0); /// public async Task> ActivateAsync( @@ -97,11 +97,13 @@ public RemotingAction WithValue(ValueUnit value) private byte[] EncodePayload() { - var encodedArgs = args.Encode(); - var payload = new byte[route.Length + encodedArgs.Length]; - Buffer.BlockCopy(route.ToArray(), 0, payload, 0, route.Length); - Buffer.BlockCopy(encodedArgs, 0, payload, route.Length, encodedArgs.Length); - return payload; + var byteList = new List(); + byteList.AddRange(route); + foreach (var arg in args) + { + byteList.AddRange(arg.Encode()); + } + return [.. byteList]; } private T DecodePayload(byte[] bytes) diff --git a/net/tests/Sails.Client.Tests/DemoClientTests.cs b/net/tests/Sails.Client.Tests/DemoClientTests.cs index 9436d718..13d5dd4c 100644 --- a/net/tests/Sails.Client.Tests/DemoClientTests.cs +++ b/net/tests/Sails.Client.Tests/DemoClientTests.cs @@ -1,6 +1,3 @@ -using Substrate.Gear.Client.NetApi.Model.Types.Base; -using Substrate.Gear.Client.NetApi.Model.Types.Primitive; - namespace Sails.Client.Tests; public class DemoClientTests diff --git a/net/tests/Sails.ClientGenerator.Tests/Snapshots/SailsClientGeneratorTests.Generate_DemoIdl#Demo.g.verified.cs b/net/tests/Sails.ClientGenerator.Tests/Snapshots/SailsClientGeneratorTests.Generate_DemoIdl#Demo.g.verified.cs index a24206f8..8cfba485 100644 --- a/net/tests/Sails.ClientGenerator.Tests/Snapshots/SailsClientGeneratorTests.Generate_DemoIdl#Demo.g.verified.cs +++ b/net/tests/Sails.ClientGenerator.Tests/Snapshots/SailsClientGeneratorTests.Generate_DemoIdl#Demo.g.verified.cs @@ -1,10 +1,12 @@ //HintName: Demo.g.cs +// using global::Sails.Remoting; using global::Sails.Remoting.Abstractions; using global::Sails.Remoting.Abstractions.Core; using global::System; using global::System.Collections.Generic; +#nullable enable #pragma warning disable RCS0056 // A line is too long namespace Sails.ClientGenerator.Tests.Demo; @@ -31,13 +33,13 @@ public DemoFactory(IRemoting remoting) /// public IActivation Default() { - return new RemotingAction(this.remoting, [28, 68, 101, 102, 97, 117, 108, 116], new global::Substrate.NetApi.Model.Types.Base.BaseVoid()); + return new RemotingAction(this.remoting, [28, 68, 101, 102, 97, 117, 108, 116]); } /// public IActivation New(global::Substrate.NetApi.Model.Types.Base.BaseOpt counter, global::Substrate.NetApi.Model.Types.Base.BaseOpt> dogPosition) { - return new RemotingAction, global::Substrate.NetApi.Model.Types.Base.BaseOpt>>>(this.remoting, [12, 78, 101, 119], new global::Substrate.NetApi.Model.Types.Base.BaseTuple, global::Substrate.NetApi.Model.Types.Base.BaseOpt>>(counter, dogPosition)); + return new RemotingAction(this.remoting, [12, 78, 101, 119], counter, dogPosition); } } @@ -59,19 +61,19 @@ public Counter(IRemoting remoting) /// public ICall Add(global::Substrate.NetApi.Model.Types.Primitive.U32 value) { - return new RemotingAction(this.remoting, [28, 67, 111, 117, 110, 116, 101, 114, 12, 65, 100, 100], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust(value)); + return new RemotingAction(this.remoting, [28, 67, 111, 117, 110, 116, 101, 114, 12, 65, 100, 100], value); } /// public ICall Sub(global::Substrate.NetApi.Model.Types.Primitive.U32 value) { - return new RemotingAction(this.remoting, [28, 67, 111, 117, 110, 116, 101, 114, 12, 83, 117, 98], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust(value)); + return new RemotingAction(this.remoting, [28, 67, 111, 117, 110, 116, 101, 114, 12, 83, 117, 98], value); } /// public IQuery Value() { - return new RemotingAction(this.remoting, [28, 67, 111, 117, 110, 116, 101, 114, 20, 86, 97, 108, 117, 101], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust()); + return new RemotingAction(this.remoting, [28, 67, 111, 117, 110, 116, 101, 114, 20, 86, 97, 108, 117, 101]); } } @@ -154,25 +156,25 @@ public Dog(IRemoting remoting) /// public ICall MakeSound() { - return new RemotingAction(this.remoting, [12, 68, 111, 103, 36, 77, 97, 107, 101, 83, 111, 117, 110, 100], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust()); + return new RemotingAction(this.remoting, [12, 68, 111, 103, 36, 77, 97, 107, 101, 83, 111, 117, 110, 100]); } /// public ICall Walk(global::Substrate.NetApi.Model.Types.Primitive.I32 dx, global::Substrate.NetApi.Model.Types.Primitive.I32 dy) { - return new RemotingAction(this.remoting, [12, 68, 111, 103, 16, 87, 97, 108, 107], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust(dx, dy)); + return new RemotingAction(this.remoting, [12, 68, 111, 103, 16, 87, 97, 108, 107], dx, dy); } /// public IQuery AvgWeight() { - return new RemotingAction(this.remoting, [12, 68, 111, 103, 36, 65, 118, 103, 87, 101, 105, 103, 104, 116], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust()); + return new RemotingAction(this.remoting, [12, 68, 111, 103, 36, 65, 118, 103, 87, 101, 105, 103, 104, 116]); } /// public IQuery> Position() { - return new RemotingAction>(this.remoting, [12, 68, 111, 103, 32, 80, 111, 115, 105, 116, 105, 111, 110], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust()); + return new RemotingAction>(this.remoting, [12, 68, 111, 103, 32, 80, 111, 115, 105, 116, 105, 111, 110]); } } @@ -246,7 +248,7 @@ public PingPong(IRemoting remoting) /// public ICall> Ping(global::Substrate.NetApi.Model.Types.Primitive.Str input) { - return new RemotingAction>(this.remoting, [32, 80, 105, 110, 103, 80, 111, 110, 103, 16, 80, 105, 110, 103], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust(input)); + return new RemotingAction>(this.remoting, [32, 80, 105, 110, 103, 80, 111, 110, 103, 16, 80, 105, 110, 103], input); } } @@ -273,49 +275,49 @@ public References(IRemoting remoting) /// public ICall Add(global::Substrate.NetApi.Model.Types.Primitive.U32 v) { - return new RemotingAction(this.remoting, [40, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 12, 65, 100, 100], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust(v)); + return new RemotingAction(this.remoting, [40, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 12, 65, 100, 100], v); } /// public ICall> AddByte(global::Substrate.NetApi.Model.Types.Primitive.U8 @byte) { - return new RemotingAction>(this.remoting, [40, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 28, 65, 100, 100, 66, 121, 116, 101], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust(@byte)); + return new RemotingAction>(this.remoting, [40, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 28, 65, 100, 100, 66, 121, 116, 101], @byte); } /// public ICall> GuessNum(global::Substrate.NetApi.Model.Types.Primitive.U8 number) { - return new RemotingAction>(this.remoting, [40, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 32, 71, 117, 101, 115, 115, 78, 117, 109], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust(number)); + return new RemotingAction>(this.remoting, [40, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 32, 71, 117, 101, 115, 115, 78, 117, 109], number); } /// public ICall Incr() { - return new RemotingAction(this.remoting, [40, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 16, 73, 110, 99, 114], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust()); + return new RemotingAction(this.remoting, [40, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 16, 73, 110, 99, 114]); } /// public ICall> SetNum(global::Substrate.NetApi.Model.Types.Primitive.U8 number) { - return new RemotingAction>(this.remoting, [40, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 24, 83, 101, 116, 78, 117, 109], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust(number)); + return new RemotingAction>(this.remoting, [40, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 24, 83, 101, 116, 78, 117, 109], number); } /// public IQuery Baked() { - return new RemotingAction(this.remoting, [40, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 20, 66, 97, 107, 101, 100], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust()); + return new RemotingAction(this.remoting, [40, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 20, 66, 97, 107, 101, 100]); } /// public IQuery> LastByte() { - return new RemotingAction>(this.remoting, [40, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 32, 76, 97, 115, 116, 66, 121, 116, 101], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust()); + return new RemotingAction>(this.remoting, [40, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 32, 76, 97, 115, 116, 66, 121, 116, 101]); } /// public IQuery> Message() { - return new RemotingAction>(this.remoting, [40, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 28, 77, 101, 115, 115, 97, 103, 101], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust()); + return new RemotingAction>(this.remoting, [40, 82, 101, 102, 101, 114, 101, 110, 99, 101, 115, 28, 77, 101, 115, 115, 97, 103, 101]); } } @@ -339,31 +341,31 @@ public ThisThat(IRemoting remoting) /// public ICall, global::Substrate.NetApi.Model.Types.Primitive.Str>> DoThat(DoThatParam param) { - return new RemotingAction, global::Substrate.NetApi.Model.Types.Primitive.Str>>(this.remoting, [32, 84, 104, 105, 115, 84, 104, 97, 116, 24, 68, 111, 84, 104, 97, 116], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust(param)); + return new RemotingAction, global::Substrate.NetApi.Model.Types.Primitive.Str>>(this.remoting, [32, 84, 104, 105, 115, 84, 104, 97, 116, 24, 68, 111, 84, 104, 97, 116], param); } /// public ICall> DoThis(global::Substrate.NetApi.Model.Types.Primitive.U32 p1, global::Substrate.NetApi.Model.Types.Primitive.Str p2, global::Substrate.NetApi.Model.Types.Base.BaseTuple, global::Substrate.Gear.Client.NetApi.Model.Types.Primitive.NonZeroU8> p3, TupleStruct p4) { - return new RemotingAction>(this.remoting, [32, 84, 104, 105, 115, 84, 104, 97, 116, 24, 68, 111, 84, 104, 105, 115], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust(p1, p2, p3, p4)); + return new RemotingAction>(this.remoting, [32, 84, 104, 105, 115, 84, 104, 97, 116, 24, 68, 111, 84, 104, 105, 115], p1, p2, p3, p4); } /// public ICall Noop() { - return new RemotingAction(this.remoting, [32, 84, 104, 105, 115, 84, 104, 97, 116, 16, 78, 111, 111, 112], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust()); + return new RemotingAction(this.remoting, [32, 84, 104, 105, 115, 84, 104, 97, 116, 16, 78, 111, 111, 112]); } /// public IQuery> That() { - return new RemotingAction>(this.remoting, [32, 84, 104, 105, 115, 84, 104, 97, 116, 16, 84, 104, 97, 116], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust()); + return new RemotingAction>(this.remoting, [32, 84, 104, 105, 115, 84, 104, 97, 116, 16, 84, 104, 97, 116]); } /// public IQuery This() { - return new RemotingAction(this.remoting, [32, 84, 104, 105, 115, 84, 104, 97, 116, 16, 84, 104, 105, 115], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust()); + return new RemotingAction(this.remoting, [32, 84, 104, 105, 115, 84, 104, 97, 116, 16, 84, 104, 105, 115]); } } @@ -383,7 +385,7 @@ public ValueFee(IRemoting remoting) /// public ICall DoSomethingAndTakeFee() { - return new RemotingAction(this.remoting, [32, 86, 97, 108, 117, 101, 70, 101, 101, 84, 68, 111, 83, 111, 109, 101, 116, 104, 105, 110, 103, 65, 110, 100, 84, 97, 107, 101, 70, 101, 101], new global::Substrate.NetApi.Model.Types.Base.BaseTupleRust()); + return new RemotingAction(this.remoting, [32, 86, 97, 108, 117, 101, 70, 101, 101, 84, 68, 111, 83, 111, 109, 101, 116, 104, 105, 110, 103, 65, 110, 100, 84, 97, 107, 101, 70, 101, 101]); } } @@ -441,8 +443,7 @@ public ValueFeeListener(global::Sails.Remoting.Abstractions.Core.IRemotingListen public sealed partial class ReferenceCount : global::Substrate.NetApi.Model.Types.Base.BaseType { - [System.Diagnostics.CodeAnalysis.AllowNull] - public global::Substrate.NetApi.Model.Types.Primitive.U32 Value { get; set; } + public global::Substrate.NetApi.Model.Types.Primitive.U32 Value { get; init; } = new(); /// public override string TypeName() => "ReferenceCount"; @@ -458,7 +459,6 @@ public override byte[] Encode() public override void Decode(byte[] byteArray, ref int p) { var start = p; - this.Value = new global::Substrate.NetApi.Model.Types.Primitive.U32(); this.Value.Decode(byteArray, ref p); var bytesLength = p - start; this.TypeSize = bytesLength; @@ -469,14 +469,9 @@ public override void Decode(byte[] byteArray, ref int p) public sealed partial class DoThatParam : global::Substrate.NetApi.Model.Types.Base.BaseType { - [System.Diagnostics.CodeAnalysis.AllowNull] - public global::Substrate.Gear.Api.Generated.Types.Base.NonZeroU32 P1 { get; set; } - - [System.Diagnostics.CodeAnalysis.AllowNull] - public global::Substrate.Gear.Api.Generated.Model.gprimitives.ActorId P2 { get; set; } - - [System.Diagnostics.CodeAnalysis.AllowNull] - public EnumManyVariants P3 { get; set; } + public global::Substrate.Gear.Api.Generated.Types.Base.NonZeroU32 P1 { get; init; } = new(); + public global::Substrate.Gear.Api.Generated.Model.gprimitives.ActorId P2 { get; init; } = new(); + public EnumManyVariants P3 { get; init; } = new(); /// public override string TypeName() => "DoThatParam"; @@ -494,11 +489,8 @@ public override byte[] Encode() public override void Decode(byte[] byteArray, ref int p) { var start = p; - this.P1 = new global::Substrate.Gear.Api.Generated.Types.Base.NonZeroU32(); this.P1.Decode(byteArray, ref p); - this.P2 = new global::Substrate.Gear.Api.Generated.Model.gprimitives.ActorId(); this.P2.Decode(byteArray, ref p); - this.P3 = new EnumManyVariants(); this.P3.Decode(byteArray, ref p); var bytesLength = p - start; this.TypeSize = bytesLength; @@ -532,8 +524,7 @@ public EnumManyVariants() public sealed partial class TupleStruct : global::Substrate.NetApi.Model.Types.Base.BaseType { - [System.Diagnostics.CodeAnalysis.AllowNull] - public global::Substrate.NetApi.Model.Types.Primitive.Bool Value { get; set; } + public global::Substrate.NetApi.Model.Types.Primitive.Bool Value { get; init; } = new(); /// public override string TypeName() => "TupleStruct"; @@ -549,11 +540,10 @@ public override byte[] Encode() public override void Decode(byte[] byteArray, ref int p) { var start = p; - this.Value = new global::Substrate.NetApi.Model.Types.Primitive.Bool(); this.Value.Decode(byteArray, ref p); var bytesLength = p - start; this.TypeSize = bytesLength; this.Bytes = new byte[bytesLength]; Array.Copy(byteArray, start, this.Bytes, 0, bytesLength); } -} +} \ No newline at end of file diff --git a/net/tests/Sails.DemoClient.Tests/AssemblyAttributes.cs b/net/tests/Sails.DemoClient.Tests/AssemblyAttributes.cs new file mode 100644 index 00000000..991f08f6 --- /dev/null +++ b/net/tests/Sails.DemoClient.Tests/AssemblyAttributes.cs @@ -0,0 +1,6 @@ +[assembly: TestFramework( + "Sails.Tests.Shared.XUnit.TestFramework", + "Sails.Tests.Shared")] + +[assembly: AssemblyFixture( + typeof(Sails.DemoClient.Tests._Infra.XUnit.Fixtures.SailsFixture))] diff --git a/net/tests/Sails.DemoClient.Tests/DemoFactoryTests.cs b/net/tests/Sails.DemoClient.Tests/DemoFactoryTests.cs new file mode 100644 index 00000000..60f4f229 --- /dev/null +++ b/net/tests/Sails.DemoClient.Tests/DemoFactoryTests.cs @@ -0,0 +1,21 @@ +using Sails.DemoClient.Tests._Infra.XUnit.Fixtures; + +namespace Sails.DemoClient.Tests; + +public sealed class DemoFactoryTests : IAssemblyFixture +{ + public DemoFactoryTests(SailsFixture fixture) + { + this.sailsFixture = fixture; + // Assert that IDL file from the Sails.DemoClient project is the same as the one + // from the SailsFixture + } + + private readonly SailsFixture sailsFixture; + + [Fact] + public async Task Test1() + { + var demoContractCodeId = await this.sailsFixture.GetDemoContractCodeIdAsync(); + } +} diff --git a/net/tests/Sails.DemoClient.Tests/GlobalUsings.cs b/net/tests/Sails.DemoClient.Tests/GlobalUsings.cs new file mode 100644 index 00000000..8f37676d --- /dev/null +++ b/net/tests/Sails.DemoClient.Tests/GlobalUsings.cs @@ -0,0 +1,3 @@ +global using System.Threading.Tasks; +global using Sails.Tests.Shared.XUnit; +global using Xunit; diff --git a/net/tests/Sails.DemoClient.Tests/Sails.DemoClient.Tests.csproj b/net/tests/Sails.DemoClient.Tests/Sails.DemoClient.Tests.csproj new file mode 100644 index 00000000..eeb8d95e --- /dev/null +++ b/net/tests/Sails.DemoClient.Tests/Sails.DemoClient.Tests.csproj @@ -0,0 +1,22 @@ + + + + net8.0 + false + true + $(NoWarn);xUnit1041 + + + + + + + + + + + + + + + diff --git a/net/tests/Sails.DemoClient.Tests/_Infra/XUnit/Fixtures/SailsFixture.cs b/net/tests/Sails.DemoClient.Tests/_Infra/XUnit/Fixtures/SailsFixture.cs new file mode 100644 index 00000000..6ec86d5e --- /dev/null +++ b/net/tests/Sails.DemoClient.Tests/_Infra/XUnit/Fixtures/SailsFixture.cs @@ -0,0 +1,9 @@ +namespace Sails.DemoClient.Tests._Infra.XUnit.Fixtures; + +public sealed class SailsFixture : Sails.Tests.Shared.XUnit.Fixtures.SailsFixture +{ + public SailsFixture() + : base("demo-client-tests") + { + } +} diff --git a/net/tests/Sails.Remoting.Tests/AssemblyAttributes.cs b/net/tests/Sails.Remoting.Tests/AssemblyAttributes.cs new file mode 100644 index 00000000..32f26ce1 --- /dev/null +++ b/net/tests/Sails.Remoting.Tests/AssemblyAttributes.cs @@ -0,0 +1,6 @@ +[assembly: TestFramework( + "Sails.Tests.Shared.XUnit.TestFramework", + "Sails.Tests.Shared")] + +[assembly: AssemblyFixture( + typeof(Sails.Remoting.Tests._Infra.XUnit.Fixtures.SailsFixture))] diff --git a/net/tests/Sails.Remoting.Tests/Core/RemotingViaNodeClientTests.cs b/net/tests/Sails.Remoting.Tests/Core/RemotingViaNodeClientTests.cs index 3f603faf..1024a7c7 100644 --- a/net/tests/Sails.Remoting.Tests/Core/RemotingViaNodeClientTests.cs +++ b/net/tests/Sails.Remoting.Tests/Core/RemotingViaNodeClientTests.cs @@ -1,14 +1,6 @@ using Sails.Remoting.Tests._Infra.XUnit.Fixtures; -using Substrate.Gear.Api.Generated; -using Substrate.Gear.Client; -using Substrate.Gear.Client.Extensions; using Substrate.Gear.Client.GearApi.Model.gprimitives; -using Substrate.NET.Schnorrkel.Keys; -using Substrate.NetApi; -using Substrate.NetApi.Model.Extrinsics; -using Substrate.NetApi.Model.Types; using Substrate.NetApi.Model.Types.Primitive; -using CodeId = Substrate.Gear.Api.Generated.Model.gprimitives.CodeId; namespace Sails.Remoting.Tests.Core; @@ -25,18 +17,9 @@ public RemotingViaNodeClientTests(SailsFixture sailsFixture) }); var serviceProvider = serviceCollection.BuildServiceProvider(); this.remotingProvider = serviceProvider.GetRequiredService(); - this.remoting = this.remotingProvider.CreateRemoting(AliceAccount); + this.remoting = this.remotingProvider.CreateRemoting(SailsFixture.AliceAccount); } - private static readonly MiniSecret AliceMiniSecret - = new( - Utils.HexToByteArray("0xe5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a"), - ExpandMode.Ed25519); - private static readonly Account AliceAccount - = Account.Build( - KeyType.Sr25519, - AliceMiniSecret.ExpandToSecret().ToEd25519Bytes(), - AliceMiniSecret.GetPair().Public.Key); private static readonly Random Random = new((int)DateTime.UtcNow.Ticks); private readonly SailsFixture sailsFixture; @@ -51,8 +34,7 @@ public void Service_Provider_Resolves_Expected_Implementation() public async Task Program_Activation_Works() { // Arrange - var codeBytes = await this.sailsFixture.GetNoSvcsProgContractWasmAsync(); - var codeId = await this.UploadCodeAsync(codeBytes.AsReadOnlyCollection()); + var codeId = await this.sailsFixture.GetDemoContractCodeIdAsync(); // Act var encodedPayload = new Str("Default").Encode(); @@ -63,25 +45,24 @@ public async Task Program_Activation_Works() CancellationToken.None); // Assert - var activationResult = await activationReply.ReadAsync(CancellationToken.None); + var (programId, payload) = await activationReply.ReadAsync(CancellationToken.None); - var programIdStr = activationResult.ProgramId.ToHexString(); // Should be asserted against logs produced by node + var programIdStr = programId.ToHexString(); // Should be asserted against logs produced by node - activationResult.Payload.Should().BeEquivalentTo(encodedPayload, options => options.WithStrictOrdering()); + payload.Should().BeEquivalentTo(encodedPayload, options => options.WithStrictOrdering()); } [Fact] public async Task Sending_Message_To_Program_Works() { // Arrange - var codeBytes = await this.sailsFixture.GetDemoContractWasmAsync(); - var codeId = await this.UploadCodeAsync(codeBytes.AsReadOnlyCollection()); + var codeId = await this.sailsFixture.GetDemoContractCodeIdAsync(); var activationReply = await this.remoting.ActivateAsync( codeId, salt: BitConverter.GetBytes(Random.NextInt64()), new Str("Default").Encode(), CancellationToken.None); - var activationResult = await activationReply.ReadAsync(CancellationToken.None); + var (programId, _) = await activationReply.ReadAsync(CancellationToken.None); // Act var encodedPayload = new Str("Counter").Encode() @@ -89,7 +70,7 @@ public async Task Sending_Message_To_Program_Works() .Concat(new U32(42).Encode()) .ToArray(); var messageReply = await this.remoting.MessageAsync( - activationResult.ProgramId, + programId, encodedPayload, CancellationToken.None); @@ -105,16 +86,15 @@ public async Task Sending_Message_To_Program_Works() public async Task Querying_Program_State_Works() { // Arrange - var codeBytes = await this.sailsFixture.GetDemoContractWasmAsync(); - var codeId = await this.UploadCodeAsync(codeBytes.AsReadOnlyCollection()); + var codeId = await this.sailsFixture.GetDemoContractCodeIdAsync(); var activationReply = await this.remoting.ActivateAsync( codeId, salt: BitConverter.GetBytes(Random.NextInt64()), new Str("Default").Encode(), CancellationToken.None); - var activationResult = await activationReply.ReadAsync(CancellationToken.None); + var (programId, _) = await activationReply.ReadAsync(CancellationToken.None); var messageReply = await this.remoting.MessageAsync( - activationResult.ProgramId, + programId, encodedPayload: new Str("Counter").Encode() .Concat(new Str("Add").Encode()) .Concat(new U32(42).Encode()) @@ -127,28 +107,13 @@ public async Task Querying_Program_State_Works() .Concat(new Str("Value").Encode()) .ToArray(); var queryResult = await this.remoting.QueryAsync( - activationResult.ProgramId, + programId, encodedPayload, CancellationToken.None); // Assert queryResult.Should().BeEquivalentTo( - encodedPayload.Concat(new U32(42).Encode()).ToArray(), + [.. encodedPayload, .. new U32(42).Encode()], options => options.WithStrictOrdering()); } - - private async Task UploadCodeAsync(IReadOnlyCollection codeBytes) - { - using (var nodeClient = new SubstrateClientExt( - this.sailsFixture.GearNodeWsUrl, - ChargeTransactionPayment.Default())) - { - await nodeClient.ConnectAsync(); - - return await nodeClient.UploadCodeAsync( - AliceAccount, - codeBytes, - CancellationToken.None); - } - } } diff --git a/net/tests/Sails.Remoting.Tests/GlobalUsings.cs b/net/tests/Sails.Remoting.Tests/GlobalUsings.cs index d22948a8..8310e6fc 100644 --- a/net/tests/Sails.Remoting.Tests/GlobalUsings.cs +++ b/net/tests/Sails.Remoting.Tests/GlobalUsings.cs @@ -1,19 +1,12 @@ global using System; -global using System.Collections.Generic; -global using System.IO; global using System.Linq; -global using System.Text.RegularExpressions; global using System.Threading; global using System.Threading.Tasks; -global using EnsureThat; global using FluentAssertions; global using Microsoft.Extensions.DependencyInjection; global using Sails.Remoting.Abstractions.Core; global using Sails.Remoting.Core; global using Sails.Remoting.DependencyInjection; global using Sails.Remoting.Options; -global using Sails.Tests.Shared.Containers; -global using Sails.Tests.Shared.Git; global using Sails.Tests.Shared.XUnit; global using Xunit; -global using Xunit.Abstractions; diff --git a/net/tests/Sails.Remoting.Tests/Sails.Remoting.Tests.csproj b/net/tests/Sails.Remoting.Tests/Sails.Remoting.Tests.csproj index bf619d93..a940f93f 100644 --- a/net/tests/Sails.Remoting.Tests/Sails.Remoting.Tests.csproj +++ b/net/tests/Sails.Remoting.Tests/Sails.Remoting.Tests.csproj @@ -15,7 +15,6 @@ - all @@ -24,11 +23,6 @@ - - diff --git a/net/tests/Sails.Remoting.Tests/_Infra/XUnit/Fixtures/SailsFixture.cs b/net/tests/Sails.Remoting.Tests/_Infra/XUnit/Fixtures/SailsFixture.cs index 0fa13585..aba7cea8 100644 --- a/net/tests/Sails.Remoting.Tests/_Infra/XUnit/Fixtures/SailsFixture.cs +++ b/net/tests/Sails.Remoting.Tests/_Infra/XUnit/Fixtures/SailsFixture.cs @@ -1,138 +1,9 @@ -using Nito.AsyncEx; -using Sails.Remoting.Tests._Infra.XUnit.Fixtures; +namespace Sails.Remoting.Tests._Infra.XUnit.Fixtures; -[assembly: AssemblyFixture(typeof(SailsFixture))] - -namespace Sails.Remoting.Tests._Infra.XUnit.Fixtures; - -public sealed partial class SailsFixture : IAsyncLifetime +public sealed class SailsFixture : Sails.Tests.Shared.XUnit.Fixtures.SailsFixture { public SailsFixture() - : this(sailsRsVersion: "0.6.3") - { - } - - public SailsFixture(string sailsRsVersion) - { - EnsureArg.IsNotNullOrWhiteSpace(sailsRsVersion, nameof(sailsRsVersion)); - - this.sailsRsReleaseTag = $"rs/v{sailsRsVersion}"; - this.demoContractIdl = new AsyncLazy( - () => this.DownloadStringAsset("demo.idl"), - AsyncLazyFlags.RetryOnFailure); - this.demoContractWasm = new AsyncLazy( - () => this.DownloadOctetAsset("demo.wasm"), - AsyncLazyFlags.RetryOnFailure); - this.noSvcsProgContractIdl = new AsyncLazy( - () => this.DownloadStringAsset("no-svcs-prog.idl"), - AsyncLazyFlags.RetryOnFailure); - this.noSvcsProgContractWasm = new AsyncLazy( - () => this.DownloadOctetAsset("no_svcs_prog.wasm"), - AsyncLazyFlags.RetryOnFailure); - this.gearNodeContainer = null; - } - - private static readonly GithubDownloader GithubDownloader = new("gear-tech", "sails"); - - private readonly string sailsRsReleaseTag; - private readonly AsyncLazy demoContractIdl; - private readonly AsyncLazy demoContractWasm; - private readonly AsyncLazy noSvcsProgContractIdl; - private readonly AsyncLazy noSvcsProgContractWasm; - private GearNodeContainer? gearNodeContainer; - - public Uri GearNodeWsUrl => this.gearNodeContainer?.WsUrl - ?? throw new InvalidOperationException("Gear node container is not initialized."); - - public async Task DisposeAsync() + : base("remoting-tests") { - if (this.gearNodeContainer is not null) - { - await this.gearNodeContainer.DisposeAsync(); - this.gearNodeContainer = null; - } - if (this.demoContractWasm.IsStarted) - { - await (await this.demoContractWasm).DisposeAsync(); - } - if (this.noSvcsProgContractWasm.IsStarted) - { - await (await this.noSvcsProgContractWasm).DisposeAsync(); - } } - - public async Task InitializeAsync() - { - var sailsRsCargoToml = await this.DownloadSailsRsCargoTomlAsync(); - - var matchResult = GStdDependencyRegex().Match(sailsRsCargoToml); - if (!matchResult.Success) - { - throw new InvalidOperationException( - $"Failed to find gstd dependency in Cargo.toml by the '{this.sailsRsReleaseTag}' tag."); - } - var gearNodeVersion = matchResult.Groups[1].Value; - - // The `reuse` parameter can be made configurable if needed - this.gearNodeContainer = new GearNodeContainer(gearNodeVersion, reuse: true); - await this.gearNodeContainer.StartAsync(); - } - - public Task GetDemoContractIdlAsync() - => this.demoContractIdl.Task; - - public async Task> GetDemoContractWasmAsync() - { - var byteStream = await this.demoContractWasm; - Ensure.Comparable.IsLte(byteStream.Length, int.MaxValue); - return new ReadOnlyMemory(byteStream.GetBuffer(), start: 0, length: (int)byteStream.Length); - } - - public Task GetNoSvcsProgContractIdlAsync() - => this.noSvcsProgContractIdl.Task; - - public async Task> GetNoSvcsProgContractWasmAsync() - { - var byteStream = await this.noSvcsProgContractWasm; - Ensure.Comparable.IsLte(byteStream.Length, int.MaxValue); - return new ReadOnlyMemory(byteStream.GetBuffer(), start: 0, length: (int)byteStream.Length); - } - - private async Task DownloadStringAsset(string assetName) - { - var downloadStream = await GithubDownloader.DownloadReleaseAssetAsync( - this.sailsRsReleaseTag, - assetName, - CancellationToken.None); - using (var reader = new StreamReader(downloadStream, leaveOpen: false)) - { - return await reader.ReadToEndAsync(CancellationToken.None); - } - } - - private async Task DownloadOctetAsset(string assetName) - { - var downloadStream = await GithubDownloader.DownloadReleaseAssetAsync( - this.sailsRsReleaseTag, - assetName, - CancellationToken.None); - var memoryStream = new MemoryStream(); - await downloadStream.CopyToAsync(memoryStream); - return memoryStream; - } - - private async Task DownloadSailsRsCargoTomlAsync() - { - var downloadStream = await GithubDownloader.DownloadFileFromTagAsync( - this.sailsRsReleaseTag, - "Cargo.toml", - CancellationToken.None); - using (var reader = new StreamReader(downloadStream, leaveOpen: false)) - { - return await reader.ReadToEndAsync(CancellationToken.None); - } - } - - [GeneratedRegex(@"gstd\s*=\s*""=?(\d+\.\d+\.\d+)""")] - private static partial Regex GStdDependencyRegex(); } diff --git a/net/tests/Sails.Remoting.Tests/_Infra/XUnit/TestFramework.cs b/net/tests/Sails.Remoting.Tests/_Infra/XUnit/TestFramework.cs deleted file mode 100644 index 3580ce24..00000000 --- a/net/tests/Sails.Remoting.Tests/_Infra/XUnit/TestFramework.cs +++ /dev/null @@ -1,13 +0,0 @@ -[assembly: TestFramework( - "Sails.Remoting.Tests._Infra.XUnit.TestFramework", - "Sails.Remoting.Tests")] - -namespace Sails.Remoting.Tests._Infra.XUnit; - -internal sealed class TestFramework : Sails.Tests.Shared.XUnit.TestFramework -{ - public TestFramework(IMessageSink messageSink) - : base(messageSink) - { - } -} diff --git a/net/tests/Sails.Tests.Shared/Containers/GearNodeContainer.cs b/net/tests/Sails.Tests.Shared/Containers/GearNodeContainer.cs index 8205d792..12fbc477 100644 --- a/net/tests/Sails.Tests.Shared/Containers/GearNodeContainer.cs +++ b/net/tests/Sails.Tests.Shared/Containers/GearNodeContainer.cs @@ -16,15 +16,16 @@ public sealed class GearNodeContainer : IAsyncDisposable // TODO: Consider making 'Version' as an optional parameter. // By default the latest version should be taken which can be determined // from the downloaded 'Cargo.toml' file. - public GearNodeContainer(string gearNodeVersion, bool reuse) + public GearNodeContainer(string consumerName, string gearNodeVersion, bool reuse) { + EnsureArg.IsNotNullOrWhiteSpace(consumerName, nameof(consumerName)); EnsureArg.IsNotNullOrWhiteSpace(gearNodeVersion, nameof(gearNodeVersion)); this.nodeInitializationDetector = new NodeInitializationDetector(); this.container = new ContainerBuilder() - .WithName("gear-node-for-tests") + .WithName($"gear-node-for-{consumerName.ToLower()}") .WithImage($"ghcr.io/gear-tech/node:v{gearNodeVersion}") - .WithPortBinding(RpcPort, RpcPort) // Use WithPortBinding(RpcPort, true) if random host port is required + .WithPortBinding(RpcPort, true) .WithEntrypoint("gear") .WithCommand( "--rpc-external", // --rpc-external is required for listening on all interfaces @@ -38,13 +39,13 @@ public GearNodeContainer(string gearNodeVersion, bool reuse) } private const ushort RpcPort = 9944; - private static readonly TimeSpan NodeInitializationTimeout = TimeSpan.FromSeconds(10); + private static readonly TimeSpan NodeInitializationTimeout = TimeSpan.FromSeconds(30); private readonly NodeInitializationDetector nodeInitializationDetector; private readonly IContainer container; private readonly bool reuse; - public Uri WsUrl => new($"ws://localhost:{this.container.GetMappedPublicPort(9944)}"); + public Uri WsUrl => new($"ws://localhost:{this.container.GetMappedPublicPort(RpcPort)}"); public ValueTask DisposeAsync() // Do not dispose container if it is reused otherwise it will be stopped @@ -57,8 +58,8 @@ public ValueTask DisposeAsync() public async Task StartAsync() { - await this.container.StartAsync(); - await this.nodeInitializationDetector.IsInitializedAsync(NodeInitializationTimeout); + await this.container.StartAsync().ConfigureAwait(false); + await this.nodeInitializationDetector.IsInitializedAsync(NodeInitializationTimeout).ConfigureAwait(false); } private sealed class NodeInitializationDetector : IOutputConsumer @@ -81,12 +82,12 @@ public NodeInitializationDetector() public async Task IsInitializedAsync(TimeSpan maxWaitTime) { var timeoutTask = Task.Delay(maxWaitTime); - var completedTask = await Task.WhenAny(this.isNodeInitialized.Task, timeoutTask); + var completedTask = await Task.WhenAny(this.isNodeInitialized.Task, timeoutTask).ConfigureAwait(false); if (completedTask == timeoutTask) { this.isNodeInitialized.SetException( new TimeoutException($"Node initialization timed out after {maxWaitTime}.")); - await this.isNodeInitialized.Task; + await this.isNodeInitialized.Task.ConfigureAwait(false); } } diff --git a/net/tests/Sails.Tests.Shared/Sails.Tests.Shared.csproj b/net/tests/Sails.Tests.Shared/Sails.Tests.Shared.csproj index bf5ab517..2df2bbc5 100644 --- a/net/tests/Sails.Tests.Shared/Sails.Tests.Shared.csproj +++ b/net/tests/Sails.Tests.Shared/Sails.Tests.Shared.csproj @@ -8,8 +8,14 @@ + + + + + + diff --git a/net/tests/Sails.Tests.Shared/XUnit/Fixtures/SailsFixture.cs b/net/tests/Sails.Tests.Shared/XUnit/Fixtures/SailsFixture.cs new file mode 100644 index 00000000..c027a967 --- /dev/null +++ b/net/tests/Sails.Tests.Shared/XUnit/Fixtures/SailsFixture.cs @@ -0,0 +1,229 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using EnsureThat; +using Nito.AsyncEx; +using Polly; +using Polly.Retry; +using Sails.Tests.Shared.Containers; +using Sails.Tests.Shared.Git; +using Substrate.Gear.Api.Generated; +using Substrate.Gear.Api.Generated.Model.gprimitives; +using Substrate.Gear.Client; +using Substrate.NET.Schnorrkel.Keys; +using Substrate.NetApi; +using Substrate.NetApi.Model.Extrinsics; +using Substrate.NetApi.Model.Types; +using Xunit; + +namespace Sails.Tests.Shared.XUnit.Fixtures; + +public partial class SailsFixture : IAsyncLifetime +{ + public SailsFixture(string consumerName) + : this(consumerName, sailsRsVersion: "0.6.3") + { + } + + public SailsFixture(string consumerName, string sailsRsVersion) + { + EnsureArg.IsNotNullOrWhiteSpace(consumerName, nameof(consumerName)); + EnsureArg.IsNotNullOrWhiteSpace(sailsRsVersion, nameof(sailsRsVersion)); + + this.consumerName = consumerName; + this.sailsRsReleaseTag = $"rs/v{sailsRsVersion}"; + this.demoContractIdl = new AsyncLazy( + () => this.DownloadStringAssetAsync("demo.idl"), + AsyncLazyFlags.RetryOnFailure); + this.demoContractWasm = new AsyncLazy( + () => this.DownloadOctetAssetAsync("demo.wasm"), + AsyncLazyFlags.RetryOnFailure); + this.demoContractCodeId = new AsyncLazy( + async () => + { + var codeBytes = await this.GetDemoContractWasmAsync().ConfigureAwait(false); + return await UploadCodeRetryPolicy.ExecuteAsync( + () => this.UploadCodeAsync(codeBytes.ToArray())) + .ConfigureAwait(false); + }, + AsyncLazyFlags.RetryOnFailure); + this.noSvcsProgContractIdl = new AsyncLazy( + () => this.DownloadStringAssetAsync("no-svcs-prog.idl"), + AsyncLazyFlags.RetryOnFailure); + this.noSvcsProgContractWasm = new AsyncLazy( + () => this.DownloadOctetAssetAsync("no_svcs_prog.wasm"), + AsyncLazyFlags.RetryOnFailure); + this.noSvcsProgContractCodeId = new AsyncLazy( + async () => + { + var codeBytes = await this.GetNoSvcsProgContractWasmAsync().ConfigureAwait(false); + return await UploadCodeRetryPolicy.ExecuteAsync( + () => this.UploadCodeAsync(codeBytes.ToArray())) + .ConfigureAwait(false); + }, + AsyncLazyFlags.RetryOnFailure); + this.gearNodeContainer = null; + } + + private static readonly GithubDownloader GithubDownloader = new("gear-tech", "sails"); + private static readonly AsyncRetryPolicy UploadCodeRetryPolicy = Policy.Handle( + exception => + exception.ErrorCode == 1014 && exception.Message.StartsWith("Priority is too low")) + .WaitAndRetryAsync( + 10, + retry => retry * TimeSpan.FromSeconds(1)); + + private readonly string consumerName; + private readonly string sailsRsReleaseTag; + private readonly AsyncLazy demoContractIdl; + private readonly AsyncLazy demoContractWasm; + private readonly AsyncLazy demoContractCodeId; + private readonly AsyncLazy noSvcsProgContractIdl; + private readonly AsyncLazy noSvcsProgContractWasm; + private readonly AsyncLazy noSvcsProgContractCodeId; + private GearNodeContainer? gearNodeContainer; + + public static MiniSecret AliceMiniSecret { get; } + = new( + // Taken from 'gear key inspect //Alice' output + Utils.HexToByteArray("0xe5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a"), + ExpandMode.Ed25519); + public static Account AliceAccount { get; } + = Account.Build( + KeyType.Sr25519, + AliceMiniSecret.ExpandToSecret().ToEd25519Bytes(), + AliceMiniSecret.GetPair().Public.Key); + public static MiniSecret BobMiniSecret { get; } + = new( + // Taken from 'gear key inspect //Bob' output + Utils.HexToByteArray("0x398f0c28f98885e046333d4a41c19cee4c37368a9832c6502f6cfd182e2aef89"), + ExpandMode.Ed25519); + public static Account BobAccount { get; } + = Account.Build( + KeyType.Sr25519, + BobMiniSecret.ExpandToSecret().ToEd25519Bytes(), + BobMiniSecret.GetPair().Public.Key); + + public Uri GearNodeWsUrl => this.gearNodeContainer?.WsUrl + ?? throw new InvalidOperationException("Gear node container is not initialized."); + + public async Task DisposeAsync() + { + if (this.gearNodeContainer is not null) + { + await this.gearNodeContainer.DisposeAsync().ConfigureAwait(false); + this.gearNodeContainer = null; + } + if (this.demoContractWasm.IsStarted) + { + await (await this.demoContractWasm).DisposeAsync().ConfigureAwait(false); + } + if (this.noSvcsProgContractWasm.IsStarted) + { + await (await this.noSvcsProgContractWasm).DisposeAsync().ConfigureAwait(false); + } + } + + public async Task InitializeAsync() + { + var sailsRsCargoToml = await this.DownloadSailsRsCargoTomlAsync().ConfigureAwait(false); + + var matchResult = GStdDependencyRegex().Match(sailsRsCargoToml); + if (!matchResult.Success) + { + throw new InvalidOperationException( + $"Failed to find gstd dependency in Cargo.toml by the '{this.sailsRsReleaseTag}' tag."); + } + var gearNodeVersion = matchResult.Groups[1].Value; + + // The `reuse` parameter can be made configurable if needed + this.gearNodeContainer = new GearNodeContainer(this.consumerName, gearNodeVersion, reuse: true); + await this.gearNodeContainer.StartAsync().ConfigureAwait(false); + } + + public Task GetDemoContractIdlAsync() + => this.demoContractIdl.Task; + + public async Task> GetDemoContractWasmAsync() + { + var byteStream = await this.demoContractWasm; + Ensure.Comparable.IsLte(byteStream.Length, int.MaxValue); + return new ReadOnlyMemory(byteStream.GetBuffer(), start: 0, length: (int)byteStream.Length); + } + + public Task GetDemoContractCodeIdAsync() + => this.demoContractCodeId.Task; + + public Task GetNoSvcsProgContractIdlAsync() + => this.noSvcsProgContractIdl.Task; + + public async Task> GetNoSvcsProgContractWasmAsync() + { + var byteStream = await this.noSvcsProgContractWasm; + Ensure.Comparable.IsLte(byteStream.Length, int.MaxValue); + return new ReadOnlyMemory(byteStream.GetBuffer(), start: 0, length: (int)byteStream.Length); + } + + public Task GetNoSvcsProgContractCodeIdAsync() + => this.noSvcsProgContractCodeId.Task; + + private async Task DownloadStringAssetAsync(string assetName) + { + var downloadStream = await GithubDownloader.DownloadReleaseAssetAsync( + this.sailsRsReleaseTag, + assetName, + CancellationToken.None) + .ConfigureAwait(false); + using (var reader = new StreamReader(downloadStream, leaveOpen: false)) + { + return await reader.ReadToEndAsync(CancellationToken.None).ConfigureAwait(false); + } + } + + private async Task DownloadOctetAssetAsync(string assetName) + { + var downloadStream = await GithubDownloader.DownloadReleaseAssetAsync( + this.sailsRsReleaseTag, + assetName, + CancellationToken.None) + .ConfigureAwait(false); + var memoryStream = new MemoryStream(); + await downloadStream.CopyToAsync(memoryStream).ConfigureAwait(false); + return memoryStream; + } + + private async Task DownloadSailsRsCargoTomlAsync() + { + var downloadStream = await GithubDownloader.DownloadFileFromTagAsync( + this.sailsRsReleaseTag, + "Cargo.toml", + CancellationToken.None) + .ConfigureAwait(false); + using (var reader = new StreamReader(downloadStream, leaveOpen: false)) + { + return await reader.ReadToEndAsync(CancellationToken.None).ConfigureAwait(false); + } + } + + private async Task UploadCodeAsync(IReadOnlyCollection codeBytes) + { + using (var nodeClient = new SubstrateClientExt( + this.GearNodeWsUrl, + ChargeTransactionPayment.Default())) + { + await nodeClient.ConnectAsync().ConfigureAwait(false); + + return await nodeClient.UploadCodeAsync( + AliceAccount, + codeBytes, + CancellationToken.None) + .ConfigureAwait(false); + } + } + + [GeneratedRegex(@"gstd\s*=\s*""=?(\d+\.\d+\.\d+)""")] + private static partial Regex GStdDependencyRegex(); +} diff --git a/net/tests/Sails.Tests.Shared/XUnit/TestAssemblyRunner.cs b/net/tests/Sails.Tests.Shared/XUnit/TestAssemblyRunner.cs index 90f4521a..90abc75b 100644 --- a/net/tests/Sails.Tests.Shared/XUnit/TestAssemblyRunner.cs +++ b/net/tests/Sails.Tests.Shared/XUnit/TestAssemblyRunner.cs @@ -30,7 +30,7 @@ public TestAssemblyRunner( protected override async Task AfterTestAssemblyStartingAsync() { - await base.AfterTestAssemblyStartingAsync(); + await base.AfterTestAssemblyStartingAsync().ConfigureAwait(false); var requiredFixtureTypes = new HashSet(); @@ -71,7 +71,7 @@ protected override async Task AfterTestAssemblyStartingAsync() foreach (var initializable in this.assemblyFixtureMappings.Values.OfType()) { - await this.Aggregator.RunAsync(initializable.InitializeAsync); + await this.Aggregator.RunAsync(initializable.InitializeAsync).ConfigureAwait(false); } } diff --git a/net/tests/Sails.Tests.Shared/XUnit/TestFramework.cs b/net/tests/Sails.Tests.Shared/XUnit/TestFramework.cs index 18820c25..13956cfb 100644 --- a/net/tests/Sails.Tests.Shared/XUnit/TestFramework.cs +++ b/net/tests/Sails.Tests.Shared/XUnit/TestFramework.cs @@ -4,9 +4,9 @@ namespace Sails.Tests.Shared.XUnit; -public abstract class TestFramework : XunitTestFramework +public class TestFramework : XunitTestFramework { - protected TestFramework(IMessageSink messageSink) + public TestFramework(IMessageSink messageSink) : base(messageSink) { } diff --git a/net/tests/Sails.Tests.Shared/XUnit/TestFrameworkExecutor.cs b/net/tests/Sails.Tests.Shared/XUnit/TestFrameworkExecutor.cs index be9079e6..55a424ce 100644 --- a/net/tests/Sails.Tests.Shared/XUnit/TestFrameworkExecutor.cs +++ b/net/tests/Sails.Tests.Shared/XUnit/TestFrameworkExecutor.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using Xunit.Abstractions; using Xunit.Sdk; @@ -15,6 +16,9 @@ public TestFrameworkExecutor( { } + [SuppressMessage( + "Usage", "VSTHRD100:Avoid async void methods", + Justification = "All exceptions should be added into the aggregator")] protected override async void RunTestCases( IEnumerable testCases, IMessageSink executionMessageSink, @@ -27,7 +31,7 @@ protected override async void RunTestCases( executionMessageSink, executionOptions)) { - await assemblyRunner.RunAsync(); + await assemblyRunner.RunAsync().ConfigureAwait(false); } } } diff --git a/nuget.config b/nuget.config new file mode 100644 index 00000000..67eb9d4d --- /dev/null +++ b/nuget.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + +