diff --git a/extension/generator/generator.go b/extension/generator/generator.go index e0044b3a..4d794970 100644 --- a/extension/generator/generator.go +++ b/extension/generator/generator.go @@ -53,6 +53,12 @@ type Options struct { GolangPackageImportPath string GolangPackageName string GolangPackageVersion string + + RustPackageName string + RustPackageVersion string + + TypescriptPackageName string + TypescriptPackageVersion string } func GenerateGuestLocal(options *Options) (*GuestLocalPackage, error) { diff --git a/extension/generator/golang/generator.go b/extension/generator/golang/generator.go index 5a8f747f..2ce81a21 100644 --- a/extension/generator/golang/generator.go +++ b/extension/generator/golang/generator.go @@ -24,7 +24,7 @@ import ( scaleVersion "github.com/loopholelabs/scale/version" - "github.com/loopholelabs/scale/extension/generator/templates" + "github.com/loopholelabs/scale/extension/generator/golang/templates" "github.com/loopholelabs/scale/signature/generator/utils" ) diff --git a/extension/generator/templates/arrays.go.templ b/extension/generator/golang/templates/arrays.go.templ similarity index 100% rename from extension/generator/templates/arrays.go.templ rename to extension/generator/golang/templates/arrays.go.templ diff --git a/extension/generator/templates/enumarrays.go.templ b/extension/generator/golang/templates/enumarrays.go.templ similarity index 100% rename from extension/generator/templates/enumarrays.go.templ rename to extension/generator/golang/templates/enumarrays.go.templ diff --git a/extension/generator/templates/enummaps.go.templ b/extension/generator/golang/templates/enummaps.go.templ similarity index 100% rename from extension/generator/templates/enummaps.go.templ rename to extension/generator/golang/templates/enummaps.go.templ diff --git a/extension/generator/templates/enums.go.templ b/extension/generator/golang/templates/enums.go.templ similarity index 100% rename from extension/generator/templates/enums.go.templ rename to extension/generator/golang/templates/enums.go.templ diff --git a/extension/generator/templates/guest.go.templ b/extension/generator/golang/templates/guest.go.templ similarity index 100% rename from extension/generator/templates/guest.go.templ rename to extension/generator/golang/templates/guest.go.templ diff --git a/extension/generator/templates/host.go.templ b/extension/generator/golang/templates/host.go.templ similarity index 100% rename from extension/generator/templates/host.go.templ rename to extension/generator/golang/templates/host.go.templ diff --git a/extension/generator/templates/interfaces.go.templ b/extension/generator/golang/templates/interfaces.go.templ similarity index 100% rename from extension/generator/templates/interfaces.go.templ rename to extension/generator/golang/templates/interfaces.go.templ diff --git a/extension/generator/templates/maps.go.templ b/extension/generator/golang/templates/maps.go.templ similarity index 100% rename from extension/generator/templates/maps.go.templ rename to extension/generator/golang/templates/maps.go.templ diff --git a/extension/generator/templates/mod.go.templ b/extension/generator/golang/templates/mod.go.templ similarity index 100% rename from extension/generator/templates/mod.go.templ rename to extension/generator/golang/templates/mod.go.templ diff --git a/extension/generator/templates/modelarrays.go.templ b/extension/generator/golang/templates/modelarrays.go.templ similarity index 100% rename from extension/generator/templates/modelarrays.go.templ rename to extension/generator/golang/templates/modelarrays.go.templ diff --git a/extension/generator/templates/models.go.templ b/extension/generator/golang/templates/models.go.templ similarity index 100% rename from extension/generator/templates/models.go.templ rename to extension/generator/golang/templates/models.go.templ diff --git a/extension/generator/templates/primitives.go.templ b/extension/generator/golang/templates/primitives.go.templ similarity index 100% rename from extension/generator/templates/primitives.go.templ rename to extension/generator/golang/templates/primitives.go.templ diff --git a/extension/generator/templates/templates.go b/extension/generator/golang/templates/templates.go similarity index 100% rename from extension/generator/templates/templates.go rename to extension/generator/golang/templates/templates.go diff --git a/extension/generator/templates/types.go.templ b/extension/generator/golang/templates/types.go.templ similarity index 100% rename from extension/generator/templates/types.go.templ rename to extension/generator/golang/templates/types.go.templ diff --git a/extension/generator/templates/validators.go.templ b/extension/generator/golang/templates/validators.go.templ similarity index 100% rename from extension/generator/templates/validators.go.templ rename to extension/generator/golang/templates/validators.go.templ diff --git a/extension/generator/rust/generator.go b/extension/generator/rust/generator.go new file mode 100644 index 00000000..79118f4c --- /dev/null +++ b/extension/generator/rust/generator.go @@ -0,0 +1,279 @@ +/* + Copyright 2023 Loophole Labs + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package rust + +import ( + "bytes" + "context" + "strings" + "text/template" + + interfacesVersion "github.com/loopholelabs/scale-extension-interfaces/version" + + polyglotVersion "github.com/loopholelabs/polyglot/version" + + "github.com/loopholelabs/scale/signature" + scaleVersion "github.com/loopholelabs/scale/version" + + polyglotUtils "github.com/loopholelabs/polyglot/utils" + + "github.com/loopholelabs/scale/extension" + "github.com/loopholelabs/scale/extension/generator/rust/templates" + "github.com/loopholelabs/scale/signature/generator/rust/format" + "github.com/loopholelabs/scale/signature/generator/utils" +) + +const ( + defaultPackageName = "types" +) + +var generator *Generator + +// GenerateTypes generates the types for the extension +func GenerateTypes(extensionSchema *extension.Schema, packageName string) ([]byte, error) { + return generator.GenerateTypes(extensionSchema, packageName) +} + +// GenerateCargofile generates the cargo.toml file for the extension +func GenerateCargofile(packageName string, packageVersion string) ([]byte, error) { + return generator.GenerateCargofile(packageName, packageVersion) +} + +func GenerateGuest(extensionSchema *extension.Schema, extensionHash string, packageName string) ([]byte, error) { + return generator.GenerateGuest(extensionSchema, extensionHash, packageName) +} + +func init() { + var err error + generator, err = New() + if err != nil { + panic(err) + } +} + +// Generator is the rust generator +type Generator struct { + templ *template.Template + formatter *format.Formatter +} + +// New creates a new rust generator +func New() (*Generator, error) { + templ, err := template.New("").Funcs(templateFunctions()).ParseFS(templates.FS, "*.rs.templ") + if err != nil { + return nil, err + } + + formatter, err := format.New() + if err != nil { + return nil, err + } + + return &Generator{ + templ: templ, + formatter: formatter, + }, nil +} + +// GenerateTypes generates the types for the extension +func (g *Generator) GenerateTypes(extensionSchema *extension.Schema, packageName string) ([]byte, error) { + if packageName == "" { + packageName = defaultPackageName + } + + buf := new(bytes.Buffer) + err := g.templ.ExecuteTemplate(buf, "types.rs.templ", map[string]any{ + "signature_schema": extensionSchema, + "package_name": packageName, + }) + if err != nil { + return nil, err + } + + formatted, err := g.formatter.Format(context.Background(), buf.String()) + if err != nil { + return nil, err + } + + buf.Reset() + err = g.templ.ExecuteTemplate(buf, "header.rs.templ", map[string]any{ + "generator_version": strings.Trim(scaleVersion.Version(), "v"), + "package_name": packageName, + }) + if err != nil { + return nil, err + } + return []byte(buf.String() + "\n\n" + formatted), nil +} + +// GenerateCargofile generates the cargofile for the signature +func (g *Generator) GenerateCargofile(packageName string, packageVersion string) ([]byte, error) { + buf := new(bytes.Buffer) + err := g.templ.ExecuteTemplate(buf, "cargo.rs.templ", map[string]any{ + "polyglot_version": strings.TrimPrefix(polyglotVersion.Version(), "v"), + "scale_extension_interfaces_version": strings.TrimPrefix(interfacesVersion.Version(), "v"), + "package_name": packageName, + "package_version": strings.TrimPrefix(packageVersion, "v"), + }) + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + +// GenerateGuest generates the guest bindings +func (g *Generator) GenerateGuest(extensionSchema *extension.Schema, extensionHash string, packageName string) ([]byte, error) { + if packageName == "" { + packageName = defaultPackageName + } + + buf := new(bytes.Buffer) + err := g.templ.ExecuteTemplate(buf, "guest.rs.templ", map[string]any{ + "extension_schema": extensionSchema, + "extension_hash": extensionHash, + }) + if err != nil { + return nil, err + } + + formatted, err := g.formatter.Format(context.Background(), buf.String()) + if err != nil { + return nil, err + } + + buf.Reset() + err = g.templ.ExecuteTemplate(buf, "header.rs.templ", map[string]any{ + "generator_version": strings.TrimPrefix(scaleVersion.Version(), "v"), + "package_name": packageName, + }) + if err != nil { + return nil, err + } + return []byte(buf.String() + "\n\n" + formatted), nil +} + +func templateFunctions() template.FuncMap { + return template.FuncMap{ + "Primitive": primitive, + "IsPrimitive": signature.ValidPrimitiveType, + "PolyglotPrimitive": polyglotPrimitive, + "PolyglotPrimitiveEncode": polyglotPrimitiveEncode, + "PolyglotPrimitiveDecode": polyglotPrimitiveDecode, + "Deref": func(i *bool) bool { return *i }, + "LowerFirst": func(s string) string { return string(s[0]+32) + s[1:] }, + "SnakeCase": polyglotUtils.SnakeCase, + "Params": utils.Params, + } +} + +func primitive(t string) string { + switch t { + case "string": + return "String" + case "int32": + return "i32" + case "int64": + return "i64" + case "uint32": + return "u32" + case "uint64": + return "u64" + case "float32": + return "f32" + case "float64": + return "f64" + case "bool": + return "bool" + case "bytes": + return "Vec" + default: + return t + } +} + +func polyglotPrimitive(t string) string { + switch t { + case "string": + return "Kind::String" + case "int32": + return "Kind::I32" + case "int64": + return "Kind::I64" + case "uint32": + return "Kind::U32" + case "uint64": + return "Kind::U64" + case "float32": + return "Kind::F32" + case "float64": + return "Kind::F64" + case "bool": + return "Kind::Bool" + case "bytes": + return "Kind::Bytes" + default: + return "Kind::Any" + } +} + +func polyglotPrimitiveEncode(t string) string { + switch t { + case "string": + return "encode_string" + case "int32": + return "encode_i32" + case "int64": + return "encode_i64" + case "uint32": + return "encode_u32" + case "uint64": + return "encode_u64" + case "float32": + return "encode_f32" + case "float64": + return "encode_f64" + case "bool": + return "encode_bool" + case "bytes": + return "encode_bytes" + default: + return t + } +} + +func polyglotPrimitiveDecode(t string) string { + switch t { + case "string": + return "decode_string" + case "int32": + return "decode_i32" + case "int64": + return "decode_i64" + case "uint32": + return "decode_u32" + case "uint64": + return "decode_u64" + case "float32": + return "decode_f32" + case "float64": + return "decode_f64" + case "bool": + return "decode_bool" + case "bytes": + return "decode_bytes" + default: + return "" + } +} diff --git a/extension/generator/rust/generator_test.go b/extension/generator/rust/generator_test.go new file mode 100644 index 00000000..1ff0dcf8 --- /dev/null +++ b/extension/generator/rust/generator_test.go @@ -0,0 +1,43 @@ +//go:build !integration && !generate + +/* + Copyright 2023 Loophole Labs + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package rust + +import ( + "os" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/loopholelabs/scale/extension" +) + +func TestGenerator(t *testing.T) { + s := new(extension.Schema) + err := s.Decode([]byte(extension.MasterTestingSchema)) + require.NoError(t, err) + + formatted, err := GenerateTypes(s, "types") + require.NoError(t, err) + + os.WriteFile("./generated.txt", formatted, 0644) + /* + master, err := os.ReadFile("./generated.txt") + require.NoError(t, err) + require.Equal(t, string(master), string(formatted)) + */ + t.Log(string(formatted)) + +} diff --git a/extension/generator/rust/templates/arrays.rs.templ b/extension/generator/rust/templates/arrays.rs.templ new file mode 100644 index 00000000..2d15e2ad --- /dev/null +++ b/extension/generator/rust/templates/arrays.rs.templ @@ -0,0 +1,37 @@ +{{ define "rs_arrays_struct_reference" }} + {{ $type := .Type }} + {{- range .Entries }} + {{- if (Deref .Accessor) }} + {{ SnakeCase .Name }}: Vec<{{ Primitive $type }}>, + {{- else }} + pub {{ SnakeCase .Name }}: Vec<{{ Primitive $type }}>, + {{- end -}} + {{- end }} +{{ end }} + +{{ define "rs_arrays_new_struct_reference" }} + {{ $type := .Type }} + {{- range .Entries }} + {{ SnakeCase .Name }}: Vec::with_capacity({{ .InitialSize }}), + {{- end }} +{{ end }} + +{{ define "rs_arrays_encode" }} + {{ $type := .Type }} + {{- range .Entries }} + e.encode_array(self.{{ SnakeCase .Name }}.len(), {{ PolyglotPrimitive $type }})?; + for a in &self.{{ SnakeCase .Name }} { + e.{{ PolyglotPrimitiveEncode $type }}(*a)?; + } + {{- end }} +{{ end }} + +{{ define "rs_arrays_decode" }} + {{ $type := .Type }} + {{- range .Entries }} + let size_{{ SnakeCase .Name }} = d.decode_array({{ PolyglotPrimitive $type }})?; + for _ in 0..size_{{ SnakeCase .Name }} { + x.{{ SnakeCase .Name }}.push(d.{{ PolyglotPrimitiveDecode $type }}()?); + } + {{- end }} +{{ end }} \ No newline at end of file diff --git a/extension/generator/rust/templates/cargo.rs.templ b/extension/generator/rust/templates/cargo.rs.templ new file mode 100644 index 00000000..efb7fa5f --- /dev/null +++ b/extension/generator/rust/templates/cargo.rs.templ @@ -0,0 +1,24 @@ +[package] +edition = "2021" +name = "{{ .package_name }}" +version = "{{ .package_version }}" + +[profile.release] +opt-level = 3 +lto = true +codegen-units = 1 + +[lib] +path = "guest.rs" + +[dependencies.num_enum] +version = "0.7.0" + +[dependencies.regex] +version = "1.9.4" + +[dependencies.scale_signature_interfaces] +version = "{{ .scale_signature_interfaces_version }}" + +[dependencies.polyglot_rs] +version = "{{ .polyglot_version }}" diff --git a/extension/generator/rust/templates/enumarrays.rs.templ b/extension/generator/rust/templates/enumarrays.rs.templ new file mode 100644 index 00000000..bdd88db9 --- /dev/null +++ b/extension/generator/rust/templates/enumarrays.rs.templ @@ -0,0 +1,36 @@ +{{ define "rs_enumarrays_struct_reference" }} + {{ $current_model := . }} + {{- range .EnumArrays }} + {{- if (Deref .Accessor) }} + {{ SnakeCase .Name }}: Vec<{{ .Reference }}>, + {{- else }} + pub {{ SnakeCase .Name }}: Vec<{{ .Reference }}>, + {{- end -}} + {{- end }} +{{ end }} + +{{ define "rs_enumarrays_new_struct_reference" }} + {{ $current_model := . }} + {{- range .EnumArrays }} + {{ SnakeCase .Name }}: Vec::with_capacity({{ .InitialSize }}), + {{- end }} +{{ end }} + +{{ define "rs_enumarrays_encode" }} + {{- range .EnumArrays }} + e.encode_array(self.{{ SnakeCase .Name}}.len(), Kind::U32)?; + for a in &self.{{ SnakeCase .Name}} { + e.encode_u32(*a as u32)?; + } + {{- end }} +{{ end }} + +{{ define "rs_enumarrays_decode" }} + {{ $current_model := . }} + {{- range .EnumArrays }} + let size_{{ SnakeCase .Name }} = d.decode_array(Kind::U32)?; + for _ in 0..size_{{ SnakeCase .Name }} { + x.{{ SnakeCase .Name }}.push({{ .Reference }}::try_from(d.decode_u32()?)?); + } + {{- end }} +{{ end }} \ No newline at end of file diff --git a/extension/generator/rust/templates/enummaps.rs.templ b/extension/generator/rust/templates/enummaps.rs.templ new file mode 100644 index 00000000..ace8824b --- /dev/null +++ b/extension/generator/rust/templates/enummaps.rs.templ @@ -0,0 +1,70 @@ +{{ define "rs_enummaps_struct_reference" }} + {{ $current_model := . }} + {{- range .EnumMaps }} + {{- if and (Deref .Accessor) (IsPrimitive .Value) }} + {{ SnakeCase .Name }}: HashMap<{ .Reference }}, {{ Primitive .Value }}>, + {{- end }} + + {{- if and (Deref .Accessor) (not (IsPrimitive .Value)) }} + {{ SnakeCase .Name }}: HashMap<{{ .Reference }}, {{ .Value }}>, + {{- end }} + + {{- if and (not (Deref .Accessor)) (IsPrimitive .Value) }} + pub {{ SnakeCase .Name }}: HashMap<{{ .Reference }}, {{ Primitive .Value }}>, + {{- end }} + + {{- if and (not (Deref .Accessor)) (not (IsPrimitive .Value)) }} + pub {{ SnakeCase .Name }}: HashMap<{{ .Reference }}, {{ .Value }}>, + {{- end }} + {{- end }} +{{ end }} + +{{ define "rs_enummaps_new_struct_reference" }} + {{ $current_model := . }} + {{- range .EnumMaps }} + {{ SnakeCase .Name }}: HashMap::new(), + {{ end }} +{{ end }} + +{{ define "rs_enummaps_encode" }} + {{- range .EnumMaps }} + {{- if IsPrimitive .Value }} + e.encode_map(self.{{ SnakeCase .Name }}.len(), Kind::U32, {{ PolyglotPrimitive .Value }})?; + for (k, v) in &self.{{ SnakeCase .Name }} { + e.encode_u32(*k as u32)?; + {{- if eq .Value "string"}} + e.{{ PolyglotPrimitiveEncode .Value }}(&v)?; + {{- else }} + e.{{ PolyglotPrimitiveEncode .Value }}(v)?; + {{- end }} + } + {{- else }} + e.encode_map(self.{{ SnakeCase .Name }}.len(), Kind::U32, Kind::Any)?; + for (k, v) in &self.{{ SnakeCase .Name }} { + e.encode_u32(*k as u32)?; + v.encode_self(e)?; + } + {{- end }} + {{- end }} +{{ end }} + +{{ define "rs_enummaps_decode" }} + {{ $current_model := . }} + {{- range .EnumMaps }} + {{- if IsPrimitive .Value }} + let size_{{ SnakeCase .Name }} = d.decode_map(Kind::U32, {{ PolyglotPrimitive .Value }})?; + for _ in 0..size_{{ SnakeCase .Name }} { + let k = {{ .Reference }}::try_from(d.decode_u32()?)?; + let v = d.{{ PolyglotPrimitiveDecode .Value }}()?; + x.{{ SnakeCase .Name }}.insert(k, v); + } + {{- else }} + let size_{{ SnakeCase .Name }} = d.decode_map(Kind::U32, Kind::Any)?; + for _ in 0..size_{{ SnakeCase .Name }} { + let k = {{ .Reference }}::try_from(d.decode_u32()?)?; + let v = {{ .Value }}::decode(d)?.ok_or(DecodingError::InvalidMap)?; + x.{{ SnakeCase .Name }}.insert(k, v); + } + {{- end }} + {{- end }} +{{ end }} \ No newline at end of file diff --git a/extension/generator/rust/templates/enums.rs.templ b/extension/generator/rust/templates/enums.rs.templ new file mode 100644 index 00000000..800efa56 --- /dev/null +++ b/extension/generator/rust/templates/enums.rs.templ @@ -0,0 +1,58 @@ +{{ define "rs_enums_definition" }} + #[derive(Debug, Eq, PartialEq, TryFromPrimitive, Copy, Clone, Hash)] + #[repr(u32)] + pub enum {{ .Name }} { + {{- range $index, $value := .Values }} + {{ $value }} = {{ $index }}, + {{- end }} + } +{{ end }} + +{{ define "rs_enums_struct_reference" }} + {{ $current_model := . }} + {{- range .Enums }} + {{- if (Deref .Accessor) }} + {{ SnakeCase .Name }}: {{ .Reference }}, + {{- else }} + pub {{ SnakeCase .Name }}: {{ .Reference }}, + {{- end -}} + {{ end }} +{{ end }} + +{{ define "rs_enums_new_struct_reference" }} + {{ $current_model := . }} + {{- range .Enums }} + {{ SnakeCase .Name }}: {{ .Reference }}::{{ .Default }}, + {{ end }} +{{ end }} + +{{ define "rs_enums_encode" }} + {{ $current_model := . }} + {{- range .Enums }} + e.encode_u32(self.{{ SnakeCase .Name }} as u32)?; + {{- end }} +{{ end }} + +{{ define "rs_enums_decode" }} + {{ $current_model := . }} + {{- range .Enums }} + x.{{ SnakeCase .Name }} = {{ .Reference }}::try_from(d.decode_u32()?).ok().ok_or(DecodingError::InvalidEnum)?; + {{- end }} +{{ end }} + +{{ define "rs_enums_accessor" }} + {{ $current_model := . }} + {{- range .Enums }} + {{- if .Accessor }} + impl {{ $current_model.Name }} { + pub fn get_{{ SnakeCase .Name }}(&self) -> &{{ .Reference }} { + &self.{{ SnakeCase .Name }} + } + + pub fn set_{{ SnakeCase .Name }}(&mut self, v: {{ .Reference }}) { + self.{{ SnakeCase .Name }} = v; + } + } + {{- end -}} + {{- end }} +{{ end }} \ No newline at end of file diff --git a/extension/generator/rust/templates/guest.rs.templ b/extension/generator/rust/templates/guest.rs.templ new file mode 100644 index 00000000..ed2fdea9 --- /dev/null +++ b/extension/generator/rust/templates/guest.rs.templ @@ -0,0 +1,108 @@ +pub mod types; +use crate::types::{Encode, Decode}; + +use std::io::Cursor; +use polyglot_rs::{Encoder}; + +static HASH: &'static str = "{{ .signature_hash }}"; + +static mut READ_BUFFER: Vec = Vec::new(); +static mut WRITE_BUFFER: Vec = Vec::new(); + +// write serializes the signature into the global WRITE_BUFFER and returns the pointer to the buffer and its size +// +// Users should not use this method. +pub unsafe fn write(ctx: Option<&mut types::{{ .signature_schema.Context }}>) -> (u32, u32) { + let mut cursor = Cursor::new(Vec::new()); + match ctx { + Some(ctx) => { + cursor = match types::{{ .signature_schema.Context }}::encode(Some(ctx), &mut cursor) { + Ok(_) => cursor, + Err(err) => return error(err), + }; + } + None => { + cursor = match types::{{ .signature_schema.Context }}::encode(None, &mut cursor) { + Ok(_) => cursor, + Err(err) => return error(err), + }; + } + } + let vec = cursor.into_inner(); + + WRITE_BUFFER.resize(vec.len() as usize, 0); + WRITE_BUFFER.copy_from_slice(&vec); + + return (WRITE_BUFFER.as_ptr() as u32, WRITE_BUFFER.len() as u32); +} + +// read deserializes signature from the global READ_BUFFER +// +// Users should not use this method. +pub unsafe fn read() -> Result, Box> { + let mut cursor = Cursor::new(&mut READ_BUFFER); + types::{{ .signature_schema.Context }}::decode(&mut cursor) +} + +// error serializes an error into the global WRITE_BUFFER and returns a pointer to the buffer and its size +// +// Users should not use this method. +pub unsafe fn error(error: Box) -> (u32, u32) { + let mut cursor = Cursor::new(Vec::new()); + return match cursor.encode_error(error) { + Ok(_) => { + let vec = cursor.into_inner(); + + WRITE_BUFFER.resize(vec.len() as usize, 0); + WRITE_BUFFER.copy_from_slice(&vec); + + (WRITE_BUFFER.as_ptr() as u32, WRITE_BUFFER.len() as u32) + } + Err(_) => { + (0, 0) + } + }; +} + +// resize resizes the global READ_BUFFER to the given size and returns the pointer to the buffer +// +// Users should not use this method. +pub unsafe fn resize(size: u32) -> *const u8 { + READ_BUFFER.resize(size as usize, 0); + return READ_BUFFER.as_ptr(); +} + +// hash returns the hash of the Scale Signature +// +// Users should not use this method. +pub unsafe fn hash() -> (u32, u32) { + let mut cursor = Cursor::new(Vec::new()); + return match cursor.encode_string(&String::from(HASH)) { + Ok(_) => { + let vec = cursor.into_inner(); + + WRITE_BUFFER.resize(vec.len() as usize, 0); + WRITE_BUFFER.copy_from_slice(&vec); + + (WRITE_BUFFER.as_ptr() as u32, WRITE_BUFFER.len() as u32) + } + Err(_) => { + (0, 0) + } + }; +} + +// next calls the next function in the Scale Function Chain +pub fn next(ctx: Option<&mut types::{{ .signature_schema.Context }}>) -> Result, Box> { + unsafe { + let (ptr, len) = write(ctx); + _next(ptr, len); + read() + } +} + +#[link(wasm_import_module = "env")] +extern "C" { + #[link_name = "next"] + fn _next(ptr: u32, size: u32); +} \ No newline at end of file diff --git a/extension/generator/rust/templates/header.rs.templ b/extension/generator/rust/templates/header.rs.templ new file mode 100644 index 00000000..6a9eabe3 --- /dev/null +++ b/extension/generator/rust/templates/header.rs.templ @@ -0,0 +1,2 @@ +// Code generated by scale-signature {{ .generator_version }}, DO NOT EDIT. +// output: {{ .package_name }} \ No newline at end of file diff --git a/extension/generator/rust/templates/maps.rs.templ b/extension/generator/rust/templates/maps.rs.templ new file mode 100644 index 00000000..d8be9eda --- /dev/null +++ b/extension/generator/rust/templates/maps.rs.templ @@ -0,0 +1,58 @@ +{{ define "rs_maps_struct_reference" }} + {{ $type := .Type }} + {{- range .Entries }} + {{- if (Deref .Accessor) }} + {{ SnakeCase .Name }}: HashMap<{{ Primitive $type }}, {{ Primitive .Value }}>, + {{- else }} + pub {{ SnakeCase .Name }}: HashMap<{{ Primitive $type }}, {{ Primitive .Value }}>, + {{- end }} + {{ end }} +{{ end }} + +{{ define "rs_maps_new_struct_reference" }} + {{ $type := .Type }} + {{- range .Entries }} + {{ SnakeCase .Name }}: HashMap::new(), + {{- end }} +{{ end }} + +{{ define "rs_maps_encode" }} + {{ $type := .Type }} + {{- range .Entries }} + {{- if IsPrimitive .Value }} + e.encode_map(self.{{ SnakeCase .Name }}.len(), {{ PolyglotPrimitive $type }}, {{ PolyglotPrimitive .Value }})?; + for (k, v) in &self.{{ SnakeCase .Name }} { + e.{{ PolyglotPrimitiveEncode $type }}(*k)?; + e.{{ PolyglotPrimitiveEncode .Value }}(*v)?; + } + {{- else }} + e.encode_map(self.{{ SnakeCase .Name }}.len(), {{ PolyglotPrimitive $type }}, Kind::Any)?; + for (k, v) in &self.{{ SnakeCase .Name }} { + e.{{ PolyglotPrimitiveEncode $type }}(*k)?; + v.encode_self(e)?; + } + {{- end }} + + {{- end }} +{{ end }} + +{{ define "rs_maps_decode" }} + {{ $type := .Type }} + {{- range .Entries }} + {{- if IsPrimitive .Value }} + let size_{{ SnakeCase .Name }} = d.decode_map({{ PolyglotPrimitive $type }}, {{ PolyglotPrimitive .Value }})?; + for _ in 0..size_{{ SnakeCase .Name }} { + let k = d.{{ PolyglotPrimitiveDecode $type }}()?; + let v = d.{{ PolyglotPrimitiveDecode .Value }}()?; + x.{{ SnakeCase .Name }}.insert(k, v); + } + {{- else }} + let size_{{ SnakeCase .Name }} = d.decode_map({{ PolyglotPrimitive $type }}, Kind::Any)?; + for _ in 0..size_{{ SnakeCase .Name }} { + let k = d.{{ PolyglotPrimitiveDecode $type }}()?; + let v = {{ .Value }}::decode(d)?.ok_or(DecodingError::InvalidMap)?; + x.{{ SnakeCase .Name }}.insert(k, v); + } + {{- end }} + {{- end }} +{{ end }} \ No newline at end of file diff --git a/extension/generator/rust/templates/modelarrays.rs.templ b/extension/generator/rust/templates/modelarrays.rs.templ new file mode 100644 index 00000000..af3746aa --- /dev/null +++ b/extension/generator/rust/templates/modelarrays.rs.templ @@ -0,0 +1,50 @@ +{{ define "rs_modelarrays_struct_reference" }} + {{- range .ModelArrays }} + {{- if .Accessor }} + {{ SnakeCase .Name }}: Vec<{{ .Reference }}>, + {{- else }} + pub {{ SnakeCase .Name }}: Vec<{{ .Reference }}>, + {{- end -}} + {{ end }} +{{ end }} + +{{ define "rs_modelarrays_new_struct_reference" }} + {{- range .ModelArrays }} + {{ SnakeCase .Name }}: Vec::with_capacity({{ .InitialSize }}), + {{- end }} +{{ end }} + +{{ define "rs_modelarrays_encode" }} + {{- range .ModelArrays }} + e.encode_array(self.{{ SnakeCase .Name }}.len(), Kind::Any)?; + for a in &self.{{ SnakeCase .Name }} { + a.encode_self(e)?; + } + {{- end }} +{{ end }} + +{{ define "rs_modelarrays_decode" }} + {{- range .ModelArrays }} + let size_{{ SnakeCase .Name }} = d.decode_array(Kind::Any)?; + for _ in 0..size_{{ SnakeCase .Name }} { + x.{{ SnakeCase .Name }}.push({{ .Reference }}::decode(d)?.ok_or(DecodingError::InvalidArray)?); + } + {{- end }} +{{ end }} + +{{ define "rs_modelarrays_accessor" }} + {{ $current_model := . }} + {{- range .ModelArrays }} + {{- if .Accessor }} + impl {{ $current_model.Name }} { + pub fn get_{{ SnakeCase .Name }} (&self) -> Option<&Vec<{{ .Reference }}>> { + Some(&self.{{ SnakeCase .Name }}) + } + + pub fn set_{{ SnakeCase .Name }} (&mut self, v: Vec<{{ .Reference }}>) { + self.{{ SnakeCase .Name }} = v; + } + } + {{- end -}} + {{- end }} +{{ end }} \ No newline at end of file diff --git a/extension/generator/rust/templates/models.rs.templ b/extension/generator/rust/templates/models.rs.templ new file mode 100644 index 00000000..c1334abd --- /dev/null +++ b/extension/generator/rust/templates/models.rs.templ @@ -0,0 +1,44 @@ +{{ define "rs_models_struct_reference" }} + {{- range .Models }} + {{- if .Accessor }} + {{ SnakeCase .Name }}: Option<{{ .Reference }}>, + {{- else }} + pub {{ SnakeCase .Name }}: Option<{{ .Reference }}>, + {{- end -}} + {{- end }} +{{ end }} + +{{ define "rs_models_new_struct_reference" }} + {{- range .Models }} + {{ SnakeCase .Name }}: Some({{ .Reference }}::new()), + {{- end }} +{{ end }} + +{{ define "rs_models_encode" }} + {{- range .Models }} + self.{{ SnakeCase .Name }}.encode_self(e)?; + {{- end }} +{{ end }} + +{{ define "rs_models_decode" }} + {{- range .Models }} + x.{{ SnakeCase .Name }} = {{ .Reference }}::decode(d)?; + {{- end }} +{{ end }} + +{{ define "rs_models_accessor" }} + {{ $current_model := . }} + {{- range .Models }} + {{- if .Accessor }} + impl {{ $current_model.Name }} { + pub fn get_{{ SnakeCase .Name }}(&self) -> &Option<{{ .Reference }}> { + &self.{{ SnakeCase .Name }} + } + + pub fn set_{{ SnakeCase .Name }}(&mut self, v: Option<{{ .Reference }}>) { + self.{{ SnakeCase .Name }} = v; + } + } + {{- end -}} + {{- end }} +{{ end }} \ No newline at end of file diff --git a/extension/generator/rust/templates/primitives.rs.templ b/extension/generator/rust/templates/primitives.rs.templ new file mode 100644 index 00000000..6d6d4c73 --- /dev/null +++ b/extension/generator/rust/templates/primitives.rs.templ @@ -0,0 +1,90 @@ +{{ define "rs_primitives_struct_reference" }} + {{ $type := .Type }} + {{- range .Entries }} + {{- if (Deref .Accessor) }} + {{ SnakeCase .Name }}: {{ Primitive $type }}, + {{- else }} + pub {{ SnakeCase .Name }}: {{ Primitive $type }}, + {{- end -}} + {{ end }} +{{ end }} + +{{ define "rs_primitives_new_struct_reference" }} + {{ $type := .Type }} + {{- range .Entries }} + {{ SnakeCase .Name }}: {{ .Default }}, + {{ end }} +{{ end }} + +{{ define "rs_strings_new_struct_reference" }} + {{ $type := .Type }} + {{- range .Entries }} + {{ SnakeCase .Name }}: "{{ .Default }}".to_string(), + {{ end }} +{{ end }} + +{{ define "rs_bytes_new_struct_reference" }} + {{ $type := .Type }} + {{- range .Entries }} + {{ SnakeCase .Name }}: Vec::with_capacity({{ .InitialSize }}), + {{ end }} +{{ end }} + +{{ define "rs_primitives_encode" }} + {{ $type := .Type }} + {{- range .Entries }} + e.{{ PolyglotPrimitiveEncode $type }}(self.{{ SnakeCase .Name }})?; + {{- end }} +{{ end}} + +{{ define "rs_ref_encode" }} + {{ $type := .Type }} + {{- range .Entries }} + e.{{ PolyglotPrimitiveEncode $type }}(&self.{{ SnakeCase .Name }})?; + {{- end }} +{{ end}} + +{{ define "rs_primitives_decode" }} + {{ $type := .Type }} + {{- range .Entries }} + x.{{ SnakeCase .Name }} = d.{{ PolyglotPrimitiveDecode $type }}()?; + {{- end }} +{{ end}} + +{{ define "rs_numbers_accessor" }} + {{ $type := .Type }} + {{ $model := .Model }} + {{- range .Entries }} + {{- if (Deref .Accessor) }} + pub fn get_{{ SnakeCase .Name }}(&self) -> {{ Primitive $type }} { + self.{{ SnakeCase .Name }} + } + + pub fn set_{{ SnakeCase .Name }}(&mut self, v: {{ Primitive $type }}) -> Result<(), Box> { + {{- template "rs_numbers_limit_validator" .LimitValidator }} + self.{{ SnakeCase .Name }} = v; + Ok(()) + } + {{- end -}} + {{ end }} +{{ end }} + +{{ define "rs_strings_accessor" }} + {{ $type := .Type }} + {{ $model := .Model }} + {{- range .Entries }} + {{- if (Deref .Accessor) }} + pub fn get_{{ SnakeCase .Name }}(&self) -> {{ Primitive $type }} { + self.{{ SnakeCase .Name }}.clone() + } + + pub fn set_{{ SnakeCase .Name }}(&mut self, mut v: {{ Primitive $type }}) -> Result<(), Box> { + {{- template "rs_regex_validator" .RegexValidator }} + {{- template "rs_length_validator" .LengthValidator }} + {{- template "rs_case_modifier" .CaseModifier }} + self.{{ SnakeCase .Name }} = v; + Ok(()) + } + {{- end -}} + {{ end }} +{{ end }} diff --git a/extension/generator/rust/templates/refarrays.rs.templ b/extension/generator/rust/templates/refarrays.rs.templ new file mode 100644 index 00000000..bfa6067b --- /dev/null +++ b/extension/generator/rust/templates/refarrays.rs.templ @@ -0,0 +1,9 @@ +{{ define "rs_refarrays_encode" }} + {{ $type := .Type }} + {{- range .Entries }} + e.encode_array(self.{{ SnakeCase .Name }}.len(), {{ PolyglotPrimitive $type }})?; + for a in &self.{{ SnakeCase .Name }} { + e.{{ PolyglotPrimitiveEncode $type }}(&a)?; + } + {{- end }} +{{ end }} \ No newline at end of file diff --git a/extension/generator/rust/templates/refmaps.rs.templ b/extension/generator/rust/templates/refmaps.rs.templ new file mode 100644 index 00000000..cb53ca9f --- /dev/null +++ b/extension/generator/rust/templates/refmaps.rs.templ @@ -0,0 +1,19 @@ +{{ define "rs_refmaps_encode" }} + {{ $type := .Type }} + {{- range .Entries }} + {{- if IsPrimitive .Value }} + e.encode_map(self.{{ SnakeCase .Name }}.len(), {{ PolyglotPrimitive $type }}, {{ PolyglotPrimitive .Value }})?; + for (k, v) in &self.{{ SnakeCase .Name }} { + e.{{ PolyglotPrimitiveEncode $type }}(&k)?; + e.{{ PolyglotPrimitiveEncode .Value }}(&v)?; + } + {{- else }} + e.encode_map(self.{{ SnakeCase .Name }}.len(), {{ PolyglotPrimitive $type }}, Kind::Any)?; + for (k, v) in &self.{{ SnakeCase .Name }} { + e.{{ PolyglotPrimitiveEncode $type }}(&k)?; + v.encode_self(e)?; + } + {{- end }} + + {{- end }} +{{ end }} \ No newline at end of file diff --git a/extension/generator/rust/templates/templates.go b/extension/generator/rust/templates/templates.go new file mode 100644 index 00000000..fdfb581a --- /dev/null +++ b/extension/generator/rust/templates/templates.go @@ -0,0 +1,19 @@ +/* + Copyright 2023 Loophole Labs + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package templates + +import "embed" + +//go:embed * +var FS embed.FS diff --git a/extension/generator/rust/templates/types.rs.templ b/extension/generator/rust/templates/types.rs.templ new file mode 100644 index 00000000..edb108dc --- /dev/null +++ b/extension/generator/rust/templates/types.rs.templ @@ -0,0 +1,262 @@ +#![allow(dead_code)] +#![allow(unused_imports)] +#![allow(unused_variables)] +#![allow(unused_mut)] + +use std::io::Cursor; +use polyglot_rs::{DecodingError, Encoder, Decoder, Kind}; +use num_enum::TryFromPrimitive; +use std::convert::TryFrom; +use std::collections::HashMap; +use regex::Regex; + +pub trait Encode { + fn encode<'a> (a: Option<&Self>, b: &'a mut Cursor>) -> Result<&'a mut Cursor>, Box> where Self: Sized; +} + +trait EncodeSelf { + fn encode_self<'a, 'b> (&'b self, b: &'a mut Cursor>) -> Result<&'a mut Cursor>, Box>; +} + +pub trait Decode { + fn decode (b: &mut Cursor<&mut Vec>) -> Result, Box> where Self: Sized; +} + +{{- range .signature_schema.Enums }} + {{ template "rs_enums_definition" . }} +{{- end }} + +{{- range .signature_schema.Models -}} + {{- if .Description }} + // {{ .Name }}: {{ .Description }} + {{ end -}} + + #[derive(Clone, Debug, PartialEq)] + pub struct {{ .Name }} { + {{ template "rs_models_struct_reference" . }} + {{ template "rs_modelarrays_struct_reference" . }} + + {{ template "rs_primitives_struct_reference" Params "Entries" .Strings "Type" "string" }} + {{ template "rs_arrays_struct_reference" Params "Entries" .StringArrays "Type" "string" }} + {{ template "rs_maps_struct_reference" Params "Entries" .StringMaps "Type" "string" }} + + {{ template "rs_primitives_struct_reference" Params "Entries" .Int32s "Type" "int32" }} + {{ template "rs_arrays_struct_reference" Params "Entries" .Int32Arrays "Type" "int32" }} + {{ template "rs_maps_struct_reference" Params "Entries" .Int32Maps "Type" "int32" }} + + + {{ template "rs_primitives_struct_reference" Params "Entries" .Int64s "Type" "int64" }} + {{ template "rs_arrays_struct_reference" Params "Entries" .Int64Arrays "Type" "int64" }} + {{ template "rs_maps_struct_reference" Params "Entries" .Int64Maps "Type" "int64" }} + + {{ template "rs_primitives_struct_reference" Params "Entries" .Uint32s "Type" "uint32" }} + {{ template "rs_arrays_struct_reference" Params "Entries" .Uint32Arrays "Type" "uint32" }} + {{ template "rs_maps_struct_reference" Params "Entries" .Uint32Maps "Type" "uint32" }} + + {{ template "rs_primitives_struct_reference" Params "Entries" .Uint64s "Type" "uint64" }} + {{ template "rs_arrays_struct_reference" Params "Entries" .Uint64Arrays "Type" "uint64" }} + {{ template "rs_maps_struct_reference" Params "Entries" .Uint64Maps "Type" "uint64" }} + + {{ template "rs_primitives_struct_reference" Params "Entries" .Float32s "Type" "float32" }} + {{ template "rs_arrays_struct_reference" Params "Entries" .Float32Arrays "Type" "float32" }} + + {{ template "rs_primitives_struct_reference" Params "Entries" .Float64s "Type" "float64" }} + {{ template "rs_arrays_struct_reference" Params "Entries" .Float64Arrays "Type" "float64" }} + + {{ template "rs_enums_struct_reference" . }} + {{ template "rs_enumarrays_struct_reference" . }} + {{ template "rs_enummaps_struct_reference" . }} + + {{ template "rs_primitives_struct_reference" Params "Entries" .Bytes "Type" "bytes" }} + {{ template "rs_arrays_struct_reference" Params "Entries" .BytesArrays "Type" "bytes" }} + + {{ template "rs_primitives_struct_reference" Params "Entries" .Bools "Type" "bool" }} + {{ template "rs_arrays_struct_reference" Params "Entries" .BoolArrays "Type" "bool" }} + } + + impl {{ .Name }} { + pub fn new () -> Self { + Self { + {{ template "rs_models_new_struct_reference" . }} + {{ template "rs_modelarrays_new_struct_reference" . }} + + {{ template "rs_strings_new_struct_reference" Params "Entries" .Strings }} + {{ template "rs_arrays_new_struct_reference" Params "Entries" .StringArrays "Type" "string" }} + {{ template "rs_maps_new_struct_reference" Params "Entries" .StringMaps "Type" "string" }} + + {{ template "rs_primitives_new_struct_reference" Params "Entries" .Int32s }} + {{ template "rs_arrays_new_struct_reference" Params "Entries" .Int32Arrays "Type" "int32" }} + {{ template "rs_maps_new_struct_reference" Params "Entries" .Int32Maps "Type" "int32" }} + + {{ template "rs_primitives_new_struct_reference" Params "Entries" .Int64s }} + {{ template "rs_arrays_new_struct_reference" Params "Entries" .Int64Arrays "Type" "int64" }} + {{ template "rs_maps_new_struct_reference" Params "Entries" .Int64Maps "Type" "int64" }} + + {{ template "rs_primitives_new_struct_reference" Params "Entries" .Uint32s }} + {{ template "rs_arrays_new_struct_reference" Params "Entries" .Uint32Arrays "Type" "uint32" }} + {{ template "rs_maps_new_struct_reference" Params "Entries" .Uint32Maps "Type" "uint32" }} + + {{ template "rs_primitives_new_struct_reference" Params "Entries" .Uint64s }} + {{ template "rs_arrays_new_struct_reference" Params "Entries" .Uint64Arrays "Type" "uint64" }} + {{ template "rs_maps_new_struct_reference" Params "Entries" .Uint64Maps "Type" "uint64" }} + + {{ template "rs_primitives_new_struct_reference" Params "Entries" .Float32s }} + {{ template "rs_arrays_new_struct_reference" Params "Entries" .Float32Arrays "Type" "float32" }} + + {{ template "rs_primitives_new_struct_reference" Params "Entries" .Float64s }} + {{ template "rs_arrays_new_struct_reference" Params "Entries" .Float64Arrays "Type" "float64" }} + + {{ template "rs_enums_new_struct_reference" . }} + {{ template "rs_enumarrays_new_struct_reference" . }} + {{ template "rs_enummaps_new_struct_reference" . }} + + {{ template "rs_bytes_new_struct_reference" Params "Entries" .Bytes }} + {{ template "rs_arrays_new_struct_reference" Params "Entries" .BytesArrays "Type" "bytes" }} + + {{ template "rs_primitives_new_struct_reference" Params "Entries" .Bools }} + {{ template "rs_arrays_new_struct_reference" Params "Entries" .BoolArrays "Type" "bool" }} + } + } + + + {{ template "rs_strings_accessor" Params "Model" . "Entries" .Strings "Type" "string" }} + {{ template "rs_numbers_accessor" Params "Model" . "Entries" .Int32s "Type" "int32" }} + {{ template "rs_numbers_accessor" Params "Model" . "Entries" .Int64s "Type" "int64" }} + {{ template "rs_numbers_accessor" Params "Model" . "Entries" .Uint32s "Type" "uint32" }} + {{ template "rs_numbers_accessor" Params "Model" . "Entries" .Uint64s "Type" "uint64" }} + {{ template "rs_numbers_accessor" Params "Model" . "Entries" .Float32s "Type" "float32" }} + {{ template "rs_numbers_accessor" Params "Model" . "Entries" .Float64s "Type" "float32" }} + } + + impl Encode for {{ .Name }} { + fn encode<'a> (a: Option<&{{ .Name }}>, e: &'a mut Cursor>) -> Result<&'a mut Cursor>, Box> { + a.encode_self(e) + } + } + + impl EncodeSelf for {{ .Name }} { + fn encode_self<'a, 'b> (&'b self, e: &'a mut Cursor>) -> Result<&'a mut Cursor>, Box> { + {{ template "rs_models_encode" . }} + {{ template "rs_modelarrays_encode" . }} + + {{ template "rs_ref_encode" Params "Entries" .Strings "Type" "string" }} + {{ template "rs_refarrays_encode" Params "Entries" .StringArrays "Type" "string" }} + {{ template "rs_refmaps_encode" Params "Entries" .StringMaps "Type" "string" }} + + {{ template "rs_primitives_encode" Params "Entries" .Int32s "Type" "int32" }} + {{ template "rs_arrays_encode" Params "Entries" .Int32Arrays "Type" "int32" }} + {{ template "rs_maps_encode" Params "Entries" .Int32Maps "Type" "int32" }} + + {{ template "rs_primitives_encode" Params "Entries" .Int64s "Type" "int64" }} + {{ template "rs_arrays_encode" Params "Entries" .Int64Arrays "Type" "int64" }} + {{ template "rs_maps_encode" Params "Entries" .Int64Maps "Type" "int64" }} + + {{ template "rs_primitives_encode" Params "Entries" .Uint32s "Type" "uint32" }} + {{ template "rs_arrays_encode" Params "Entries" .Uint32Arrays "Type" "uint32" }} + {{ template "rs_maps_encode" Params "Entries" .Uint32Maps "Type" "uint32" }} + + {{ template "rs_primitives_encode" Params "Entries" .Uint64s "Type" "uint64" }} + {{ template "rs_arrays_encode" Params "Entries" .Uint64Arrays "Type" "uint64" }} + {{ template "rs_maps_encode" Params "Entries" .Uint64Maps "Type" "uint64" }} + + {{ template "rs_primitives_encode" Params "Entries" .Float32s "Type" "float32" }} + {{ template "rs_arrays_encode" Params "Entries" .Float32Arrays "Type" "float32" }} + + {{ template "rs_primitives_encode" Params "Entries" .Float64s "Type" "float64" }} + {{ template "rs_arrays_encode" Params "Entries" .Float64Arrays "Type" "float64" }} + + {{ template "rs_enums_encode" . }} + {{ template "rs_enumarrays_encode" . }} + {{ template "rs_enummaps_encode" . }} + + {{ template "rs_ref_encode" Params "Entries" .Bytes "Type" "bytes" }} + {{ template "rs_refarrays_encode" Params "Entries" .BytesArrays "Type" "bytes" }} + + {{ template "rs_primitives_encode" Params "Entries" .Bools "Type" "bool" }} + {{ template "rs_arrays_encode" Params "Entries" .BoolArrays "Type" "bool" }} + + Ok(e) + } + } + + impl EncodeSelf for Option<&{{ .Name }}> { + fn encode_self<'a, 'b> (&'b self, e: &'a mut Cursor>) -> Result<&'a mut Cursor>, Box> { + if let Some(x) = self { + x.encode_self(e)?; + } else { + e.encode_none()?; + } + Ok(e) + } + } + + impl EncodeSelf for Option<{{ .Name }}> { + fn encode_self<'a, 'b> (&'b self, e: &'a mut Cursor>) -> Result<&'a mut Cursor>, Box> { + if let Some(x) = self { + x.encode_self(e)?; + } else { + e.encode_none()?; + } + Ok(e) + } + } + + impl Decode for {{ .Name }} { + fn decode (d: &mut Cursor<&mut Vec>) -> Result, Box> { + if d.decode_none() { + return Ok(None); + } + + if let Ok(error) = d.decode_error() { + return Err(error); + } + + let mut x = {{ .Name }}::new(); + + {{ template "rs_models_decode" . }} + {{ template "rs_modelarrays_decode" . }} + + {{ template "rs_primitives_decode" Params "Entries" .Strings "Type" "string" }} + {{ template "rs_arrays_decode" Params "Entries" .StringArrays "Type" "string" }} + {{ template "rs_maps_decode" Params "Entries" .StringMaps "Type" "string" }} + + {{ template "rs_primitives_decode" Params "Entries" .Int32s "Type" "int32" }} + {{ template "rs_arrays_decode" Params "Entries" .Int32Arrays "Type" "int32" }} + {{ template "rs_maps_decode" Params "Entries" .Int32Maps "Type" "int32" }} + + {{ template "rs_primitives_decode" Params "Entries" .Int64s "Type" "int64" }} + {{ template "rs_arrays_decode" Params "Entries" .Int64Arrays "Type" "int64" }} + {{ template "rs_maps_decode" Params "Entries" .Int64Maps "Type" "int64" }} + + {{ template "rs_primitives_decode" Params "Entries" .Uint32s "Type" "uint32" }} + {{ template "rs_arrays_decode" Params "Entries" .Uint32Arrays "Type" "uint32" }} + {{ template "rs_maps_decode" Params "Entries" .Uint32Maps "Type" "uint32" }} + + {{ template "rs_primitives_decode" Params "Entries" .Uint64s "Type" "uint64" }} + {{ template "rs_arrays_decode" Params "Entries" .Uint64Arrays "Type" "uint64" }} + {{ template "rs_maps_decode" Params "Entries" .Uint64Maps "Type" "uint64" }} + + {{ template "rs_primitives_decode" Params "Entries" .Float32s "Type" "float32" }} + {{ template "rs_arrays_decode" Params "Entries" .Float32Arrays "Type" "float32" }} + + {{ template "rs_primitives_decode" Params "Entries" .Float64s "Type" "float64" }} + {{ template "rs_arrays_decode" Params "Entries" .Float64Arrays "Type" "float64" }} + + {{ template "rs_enums_decode" . }} + {{ template "rs_enumarrays_decode" . }} + {{ template "rs_enummaps_decode" . }} + + {{ template "rs_primitives_decode" Params "Entries" .Bytes "Type" "bytes" }} + {{ template "rs_arrays_decode" Params "Entries" .BytesArrays "Type" "bytes" }} + + {{ template "rs_primitives_decode" Params "Entries" .Bools "Type" "bool" }} + {{ template "rs_arrays_decode" Params "Entries" .BoolArrays "Type" "bool" }} + + Ok(Some(x)) + } + } + + {{ template "rs_models_accessor" . }} + {{ template "rs_modelarrays_accessor" . }} + {{ template "rs_enums_accessor" . }} +{{ end -}} \ No newline at end of file diff --git a/extension/generator/rust/templates/validators.rs.templ b/extension/generator/rust/templates/validators.rs.templ new file mode 100644 index 00000000..5308a27d --- /dev/null +++ b/extension/generator/rust/templates/validators.rs.templ @@ -0,0 +1,53 @@ +{{ define "rs_numbers_limit_validator" }} +{{- if . }} + {{- if and .Maximum .Minimum }} + if v > {{ .Maximum }} || v < {{ .Minimum }} { + return Err(Box::::from("value must be between { .Minimum }} and {{ .Maximum }}")); + } + {{- else if .Minimum }} + if v < {{ .Minimum }} { + return Err(Box::::from("value must be greater than or equal to {{ .Minimum }}")); + } + {{- else if .Maximum }} + if v > {{ .Maximum }} { + return Err(Box::::from("value must be less than or equal to {{ .Maximum }}")); + } + {{- end }} +{{- end }} +{{ end }} + +{{ define "rs_regex_validator" }} + {{- if . }} + if !Regex::new("^[a-zA-Z0-9]*$")?.is_match(v.as_str()) { + return Err(Box::::from("value must match {{ .Expression }}")); + } + {{- end }} +{{ end }} + +{{ define "rs_length_validator" }} + {{- if . }} + {{- if and .Maximum .Minimum }} + if v.len() > {{ .Maximum }} || v.len() < {{ .Minimum }} { + return Err(Box::::from("value must be between { .Minimum }} and {{ .Maximum }}")); + } + {{- else if .Minimum }} + if v.len() < {{ .Minimum }} { + return Err(Box::::from("value must be greater than or equal to {{ .Minimum }}")); + } + {{- else if .Maximum }} + if v.len() > {{ .Maximum }} { + return Err(Box::::from("value must be less than or equal to {{ .Maximum }}")); + } + {{- end }} + {{- end }} +{{ end }} + +{{ define "rs_case_modifier" }} + {{- if . }} + {{- if eq .Kind "upper" }} + v = v.to_uppercase(); + {{- else if eq .Kind "lower" }} + v = v.to_lowercase(); + {{- end }} + {{- end }} +{{ end }} \ No newline at end of file diff --git a/storage/extension.go b/storage/extension.go index 9771c5ef..e765910a 100644 --- a/storage/extension.go +++ b/storage/extension.go @@ -307,6 +307,9 @@ func GenerateExtension(ext *extension.Schema, name string, tag string, org strin GolangPackageImportPath: "extension", GolangPackageName: ext.Name, GolangPackageVersion: "v0.1.0", + + RustPackageName: fmt.Sprintf("%s_%s_%s_guest", org, name, tag), + RustPackageVersion: "0.1.0", }) if err != nil { return err