From 395ccbab2f776df466f9b884443375030ea3b6f4 Mon Sep 17 00:00:00 2001 From: Robert Goss Date: Sat, 23 Sep 2023 00:10:36 +0100 Subject: [PATCH 01/26] Create initial files --- .gitignore | 2 +- Build/build.bat | 4 +--- Build/build.sh | 2 +- Examples/Primes/libPrimes.xml | 1 + Source/automaticcomponenttoolkit.go | 23 +++++++++++++++++++++++ 5 files changed, 27 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index f9b9a402..0ccbc689 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,4 @@ Examples/**/obj debug .vscode -.DS_Store \ No newline at end of file +tmp \ No newline at end of file diff --git a/Build/build.bat b/Build/build.bat index 9298216f..07dbbede 100644 --- a/Build/build.bat +++ b/Build/build.bat @@ -3,9 +3,7 @@ set startingDir="%CD%" set basepath="%~dp0" cd %basepath%\..\Source -set Sources=actutils.go automaticcomponenttoolkit.go buildbindingccpp.go buildbindingccppdocumentation.go buildbindingcsharp.go buildbindinggo.go buildbindingnode.go buildbindingpascal.go buildbindingpython.go buildimplementationcpp.go buildbindingjava.go buildimplementationpascal.go componentdefinition.go componentdiff.go languagewriter.go languagec.go languagecpp.go languagepascal.go - -set GOOS=windows +set Sources=actutils.go automaticcomponenttoolkit.go buildbindingccpp.go buildbindingcsharp.go buildbindinggo.go buildbindingnode.go buildbindingpascal.go buildbindingpython.go buildimplementationcpp.go buildimplementationpascal.go buildimplementationrust.go componentdefinition.go componentdiff.go languagewriter.go languagec.go languagecpp.go languagepascal.go set GOARCH=amd64 echo "Build act.win64.exe" go build -o ..\act.win64.exe %Sources% diff --git a/Build/build.sh b/Build/build.sh index 5d4b9e49..710303f0 100755 --- a/Build/build.sh +++ b/Build/build.sh @@ -6,7 +6,7 @@ startingpath="$(pwd)" basepath="$(cd "$(dirname "$0")" && pwd)" cd "$basepath/../Source" -Sources="actutils.go automaticcomponenttoolkit.go buildbindingccpp.go buildbindingccppdocumentation.go buildbindingcsharp.go buildbindinggo.go buildbindingnode.go buildbindingpascal.go buildbindingpython.go buildbindingjava.go buildimplementationcpp.go buildimplementationpascal.go componentdefinition.go componentdiff.go languagewriter.go languagec.go languagecpp.go languagepascal.go" +Sources="actutils.go automaticcomponenttoolkit.go buildbindingccpp.go buildbindingccppdocumentation.go buildbindingcsharp.go buildbindinggo.go buildbindingnode.go buildbindingpascal.go buildbindingpython.go buildbindingjava.go buildimplementationcpp.go buildimplementationpascal.go buildimplementationrust.go componentdefinition.go componentdiff.go languagewriter.go languagec.go languagecpp.go languagepascal.go" echo "Build act.win64.exe" export GOARCH="amd64" diff --git a/Examples/Primes/libPrimes.xml b/Examples/Primes/libPrimes.xml index 6f83e41f..941baccc 100644 --- a/Examples/Primes/libPrimes.xml +++ b/Examples/Primes/libPrimes.xml @@ -19,6 +19,7 @@ + diff --git a/Source/automaticcomponenttoolkit.go b/Source/automaticcomponenttoolkit.go index 760a3167..6e61a595 100644 --- a/Source/automaticcomponenttoolkit.go +++ b/Source/automaticcomponenttoolkit.go @@ -547,6 +547,29 @@ func createComponent(component ComponentDefinition, outfolderBase string, bindin } } + case "Rust": + { + outputFolderImplementationProject := outputFolderImplementations + "/Rust" + outputFolderImplementationRust := outputFolderImplementations + "/Rust/Interfaces" + outputFolderImplementationRustStub := outputFolderImplementations + "/Rust/Stub" + + err = os.MkdirAll(outputFolderImplementationRust, os.ModePerm) + if err != nil { + return err + } + + err = os.MkdirAll(outputFolderImplementationRustStub, os.ModePerm) + if err != nil { + return err + } + + err = BuildImplementationRust(component, outputFolderImplementationRust, outputFolderImplementationRustStub, + outputFolderImplementationProject, implementation) + if err != nil { + return err + } + } + case "Fortran": { log.Printf("Implementation in language \"%s\" is not yet supported.", implementation.Language) From 762cf5e184a073436e24385aaca9eb8d5cc33ad8 Mon Sep 17 00:00:00 2001 From: Robert Goss Date: Sat, 23 Sep 2023 01:10:26 +0100 Subject: [PATCH 02/26] Create initial files --- Source/buildimplementationrust.go | 92 +++++++++++++++++++ Source/languagewriter.go | 145 +++++++++++++++--------------- 2 files changed, 166 insertions(+), 71 deletions(-) create mode 100644 Source/buildimplementationrust.go diff --git a/Source/buildimplementationrust.go b/Source/buildimplementationrust.go new file mode 100644 index 00000000..f314b1a0 --- /dev/null +++ b/Source/buildimplementationrust.go @@ -0,0 +1,92 @@ +/*++ + +Copyright (C) 2023 Autodesk Inc. (Original Author) + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--*/ + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// buildimplementationrust .go +// functions to generate Rust interface classes, implementation stubs and wrapper code that maps to +// the rust interfaces. +////////////////////////////////////////////////////////////////////////////////////////////////////// + +package main + +import ( + "fmt" + "log" + "path" +) + +// BuildImplementationPascal builds Pascal interface classes, implementation stubs and wrapper code that maps to the Pascal header +func BuildImplementationRust(component ComponentDefinition, outputFolder string, stubOutputFolder string, projectOutputFolder string, implementation ComponentDefinitionImplementation) error { + + LibraryName := component.LibraryName + BaseName := component.BaseName + indentString := getIndentationString(implementation.Indentation) + + IntfHeaderName := path.Join(outputFolder, BaseName+"_interfaces.rs") + log.Printf("Creating \"%s\"", IntfHeaderName) + interfaceshppfile, err := CreateLanguageFile(IntfHeaderName, indentString) + if err != nil { + return err + } + interfaceshppfile.WriteCLicenseHeader(component, + fmt.Sprintf("This is an autogenerated rust file in order to allow easy\ndevelopment of %s. The implementer of %s needs to\nderive concrete classes from the abstract classes in this header.", LibraryName, LibraryName), + true) + err = buildRustInterfaces(component, interfaceshppfile, implementation.ClassIdentifier) + if err != nil { + return err + } + + if len(projectOutputFolder) > 0 { + CargoFileName := path.Join(projectOutputFolder, "Cargo.toml") + if !FileExists(CargoFileName) { + log.Printf("Creating Cargo file \"%s\" for Rust Implementation", CargoFileName) + CargoFile, err := CreateLanguageFile(CargoFileName, indentString) + if err != nil { + return err + } + CargoFile.WriteTomlLicenseHeader(component, + fmt.Sprintf("This is an autogenerated Cargo file for the development of %s.", LibraryName), + true) + buildCargoForRustImplementation(component, CargoFile) + } else { + log.Printf("Omitting recreation of Cargo file \"%s\" for Rust Implementation", CargoFileName) + } + } + + return nil +} + +func buildRustInterfaces(component ComponentDefinition, w LanguageWriter, ClassIdentifier string) error { + + return nil +} + +func buildCargoForRustImplementation(component ComponentDefinition, w LanguageWriter) error { + + return nil +} diff --git a/Source/languagewriter.go b/Source/languagewriter.go index e2df7fa3..1e47261c 100644 --- a/Source/languagewriter.go +++ b/Source/languagewriter.go @@ -42,10 +42,10 @@ import ( // LanguageWriter is a wrapper around a io.Writer that handles indentation type LanguageWriter struct { - Indentation int + Indentation int IndentString string - Writer io.Writer - CurrentLine string + Writer io.Writer + CurrentLine string } func max(x, y int) int { @@ -56,80 +56,84 @@ func max(x, y int) int { } // AddIndentationLevel adds number of indentation the writers output -func (writer *LanguageWriter) AddIndentationLevel (levels int) (error) { - writer.Indentation = max(writer.Indentation + levels, 0) +func (writer *LanguageWriter) AddIndentationLevel(levels int) error { + writer.Indentation = max(writer.Indentation+levels, 0) return nil } // ResetIndentationLevel adds indentation to all output -func (writer *LanguageWriter) ResetIndentationLevel () (error) { +func (writer *LanguageWriter) ResetIndentationLevel() error { writer.Indentation = 0 return nil } // Writeln formats a string and writes it to a line. Pairs of leading spaces will be replaced by the indent IndentString. -func (writer *LanguageWriter) Writeln (format string, a ...interface{}) (int, error) { +func (writer *LanguageWriter) Writeln(format string, a ...interface{}) (int, error) { - leadingSpaces := 0; + leadingSpaces := 0 for _, rune := range format { if rune == ' ' { - leadingSpaces = leadingSpaces + 1; + leadingSpaces = leadingSpaces + 1 } else { - break; + break } } - leadingIndents := leadingSpaces / 2; - - indentedFormat := strings.Repeat (writer.IndentString, leadingIndents + writer.Indentation) + format[leadingIndents * 2:]; - return fmt.Fprintf (writer.Writer, indentedFormat + "\n", a...); + leadingIndents := leadingSpaces / 2 + + indentedFormat := strings.Repeat(writer.IndentString, leadingIndents+writer.Indentation) + format[leadingIndents*2:] + return fmt.Fprintf(writer.Writer, indentedFormat+"\n", a...) } // Writelns writes multiple lines and processes indentation -func (writer *LanguageWriter) Writelns (prefix string, lines []string) (error) { +func (writer *LanguageWriter) Writelns(prefix string, lines []string) error { for idx := 0; idx < len(lines); idx++ { - _, err := writer.Writeln (prefix + lines[idx]); + _, err := writer.Writeln(prefix + lines[idx]) if err != nil { - return err; + return err } } - - return nil; + + return nil } - + // BeginLine clears the CurrentLine buffer -func (writer *LanguageWriter) BeginLine () () { - writer.CurrentLine = ""; +func (writer *LanguageWriter) BeginLine() { + writer.CurrentLine = "" } // Printf formats a string and appends it to the CurrentLine buffer -func (writer *LanguageWriter) Printf (format string, a ...interface{}) () { - writer.CurrentLine = writer.CurrentLine + fmt.Sprintf (format, a...); +func (writer *LanguageWriter) Printf(format string, a ...interface{}) { + writer.CurrentLine = writer.CurrentLine + fmt.Sprintf(format, a...) } // EndLine flushes the CurrentBuffer to the internal writer -func (writer *LanguageWriter) EndLine () (int, error) { - return writer.Writeln (writer.CurrentLine); +func (writer *LanguageWriter) EndLine() (int, error) { + return writer.Writeln(writer.CurrentLine) } +// WriteTomlLicenseHeader writes a license header into a writer with TOML-style comments prefixing each line +func (writer *LanguageWriter) WriteTomlLicenseHeader(component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx(writer.Writer, component, abstract, includeVersion, "", "", "# ") +} // WriteCMakeLicenseHeader writes a license header into a writer with CMake-style comments -func (writer *LanguageWriter) WriteCMakeLicenseHeader (component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx (writer.Writer, component, abstract, includeVersion, "#[[", "\n]]"); +func (writer *LanguageWriter) WriteCMakeLicenseHeader(component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx(writer.Writer, component, abstract, includeVersion, "#[[", "\n]]", "") } // WriteCLicenseHeader writes a license header into a writer with C-style comments -func (writer *LanguageWriter) WriteCLicenseHeader (component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx (writer.Writer, component, abstract, includeVersion, "/*", "*/"); +func (writer *LanguageWriter) WriteCLicenseHeader(component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx(writer.Writer, component, abstract, includeVersion, "/*", "*/", "") } // WritePascalLicenseHeader writes a license header into a writer Pascal-style comments -func (writer *LanguageWriter) WritePascalLicenseHeader (component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx (writer.Writer, component, abstract, includeVersion, "(*", "*)"); +func (writer *LanguageWriter) WritePascalLicenseHeader(component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx(writer.Writer, component, abstract, includeVersion, "(*", "*)", "") } // WritePythonLicenseHeader writes a license header into a writer Python-style comments -func (writer *LanguageWriter) WritePythonLicenseHeader (component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx (writer.Writer, component, abstract, includeVersion, "'''", "'''"); +func (writer *LanguageWriter) WritePythonLicenseHeader(component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx(writer.Writer, component, abstract, includeVersion, "'''", "'''", "") } // WriteJavaLicenseHeader writes a license header into a writer Java-style comments @@ -138,62 +142,61 @@ func (writer *LanguageWriter) WriteJavaLicenseHeader (component ComponentDefinit } // WritePlainLicenseHeader writes a license header into a writer without comments -func (writer *LanguageWriter) WritePlainLicenseHeader (component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx (writer.Writer, component, abstract, includeVersion, "", ""); +func (writer *LanguageWriter) WritePlainLicenseHeader(component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx(writer.Writer, component, abstract, includeVersion, "", "", "") } // WriteLicenseHeader writes a license header into a writer with C-style comments -func WriteLicenseHeader (w io.Writer, component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx (w, component, abstract, includeVersion, "/*", "*/"); +func WriteLicenseHeader(w io.Writer, component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx(w, component, abstract, includeVersion, "/*", "*/", "") } // writeLicenseHeaderEx writes a license header into a writer. -func writeLicenseHeaderEx (w io.Writer, component ComponentDefinition, abstract string, includeVersion bool, CommandStart string, CommandEnd string) { - ACTVersion := component.ACTVersion; - version := component.Version; - copyright := component.Copyright; - year := component.Year; - - if (len(CommandStart) > 0) { - fmt.Fprintf (w, "%s++\n", CommandStart); - fmt.Fprintf (w, "\n"); +func writeLicenseHeaderEx(w io.Writer, component ComponentDefinition, abstract string, includeVersion bool, CommandStart string, CommandEnd string, prefix string) { + ACTVersion := component.ACTVersion + version := component.Version + copyright := component.Copyright + year := component.Year + + if len(CommandStart) > 0 { + fmt.Fprintf(w, "%s++\n", CommandStart) + fmt.Fprintf(w, "\n") } - fmt.Fprintf (w, "Copyright (C) %d %s\n", year, copyright); - fmt.Fprintf (w, "\n"); + fmt.Fprintf(w, "%sCopyright (C) %d %s\n", prefix, year, copyright) + fmt.Fprintf(w, "%s\n", prefix) for i := 0; i < len(component.License.Lines); i++ { - line := component.License.Lines[i]; - fmt.Fprintf (w, "%s\n", line.Value); + line := component.License.Lines[i] + fmt.Fprintf(w, "%s%s\n", prefix, line.Value) } - fmt.Fprintf (w, "\n"); - if (includeVersion) { - fmt.Fprintf (w, "This file has been generated by the Automatic Component Toolkit (ACT) version %s.\n", ACTVersion) - fmt.Fprintf (w, "\n"); + fmt.Fprintf(w, "%s\n", prefix) + if includeVersion { + fmt.Fprintf(w, "%sThis file has been generated by the Automatic Component Toolkit (ACT) version %s.\n", prefix, ACTVersion) + fmt.Fprintf(w, "%s\n", prefix) } - if (len(abstract) > 0 ) { - fmt.Fprintf (w, "Abstract: %s\n", abstract); - if (includeVersion) { - fmt.Fprintf (w, "\nInterface version: %d.%d.%d\n", majorVersion(version), minorVersion(version), microVersion(version)) + if len(abstract) > 0 { + fmt.Fprintf(w, "%sAbstract: %s\n", prefix, abstract) + if includeVersion { + fmt.Fprintf(w, "%s\n%sInterface version: %d.%d.%d\n", prefix, prefix, majorVersion(version), minorVersion(version), microVersion(version)) } } - fmt.Fprintf (w, "\n"); - if (len(CommandEnd) > 0) { - fmt.Fprintf (w, "%s\n", CommandEnd); - fmt.Fprintf (w, "\n"); + fmt.Fprintf(w, "\n") + if len(CommandEnd) > 0 { + fmt.Fprintf(w, "%s\n", CommandEnd) + fmt.Fprintf(w, "\n") } } - // CreateLanguageFile creates a LanguageWriter and sets its indent string -func CreateLanguageFile (fileName string, indentString string) (LanguageWriter, error) { - var result LanguageWriter; - var err error; - +func CreateLanguageFile(fileName string, indentString string) (LanguageWriter, error) { + var result LanguageWriter + var err error + result.IndentString = indentString result.Indentation = 0 result.Writer, err = os.Create(fileName) if err != nil { - return result, err; + return result, err } - - return result, nil; + + return result, nil } From c92b20a1932770dde405fefb031e5140f1f3a560 Mon Sep 17 00:00:00 2001 From: Robert Goss Date: Sat, 23 Sep 2023 01:45:30 +0100 Subject: [PATCH 03/26] Get basic project structure --- Source/buildimplementationrust.go | 58 ++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/Source/buildimplementationrust.go b/Source/buildimplementationrust.go index f314b1a0..5f03a63b 100644 --- a/Source/buildimplementationrust.go +++ b/Source/buildimplementationrust.go @@ -38,6 +38,8 @@ import ( "fmt" "log" "path" + "path/filepath" + "strings" ) // BuildImplementationPascal builds Pascal interface classes, implementation stubs and wrapper code that maps to the Pascal header @@ -47,20 +49,45 @@ func BuildImplementationRust(component ComponentDefinition, outputFolder string, BaseName := component.BaseName indentString := getIndentationString(implementation.Indentation) - IntfHeaderName := path.Join(outputFolder, BaseName+"_interfaces.rs") - log.Printf("Creating \"%s\"", IntfHeaderName) - interfaceshppfile, err := CreateLanguageFile(IntfHeaderName, indentString) + stubIdentifier := "" + if len(implementation.StubIdentifier) > 0 { + stubIdentifier = "_" + strings.ToLower(implementation.StubIdentifier) + } + + IntfHeaderName := BaseName + "_interfaces.rs" + IntfHeaderPath := path.Join(outputFolder, IntfHeaderName) + log.Printf("Creating \"%s\"", IntfHeaderPath) + interfacesrsfile, err := CreateLanguageFile(IntfHeaderPath, indentString) if err != nil { return err } - interfaceshppfile.WriteCLicenseHeader(component, + interfacesrsfile.WriteCLicenseHeader(component, fmt.Sprintf("This is an autogenerated rust file in order to allow easy\ndevelopment of %s. The implementer of %s needs to\nderive concrete classes from the abstract classes in this header.", LibraryName, LibraryName), true) - err = buildRustInterfaces(component, interfaceshppfile, implementation.ClassIdentifier) + err = buildRustInterfaces(component, interfacesrsfile, implementation.ClassIdentifier) if err != nil { return err } + IntfWrapperStubName := path.Join(stubOutputFolder, BaseName+stubIdentifier+".rs") + if !FileExists(IntfWrapperStubName) { + log.Printf("Creating \"%s\"", IntfWrapperStubName) + stubfile, err := CreateLanguageFile(IntfWrapperStubName, indentString) + if err != nil { + return err + } + stubfile.WriteCLicenseHeader(component, + fmt.Sprintf("This is an autogenerated Rust implementation file in order to allow easy\ndevelopment of %s. It needs to be generated only once.", LibraryName), + true) + + err = buildRustGlobalStubFile(component, stubfile, implementation.ClassIdentifier) + if err != nil { + return err + } + } else { + log.Printf("Omitting recreation of implementation stub \"%s\"", IntfWrapperStubName) + } + if len(projectOutputFolder) > 0 { CargoFileName := path.Join(projectOutputFolder, "Cargo.toml") if !FileExists(CargoFileName) { @@ -72,7 +99,11 @@ func BuildImplementationRust(component ComponentDefinition, outputFolder string, CargoFile.WriteTomlLicenseHeader(component, fmt.Sprintf("This is an autogenerated Cargo file for the development of %s.", LibraryName), true) - buildCargoForRustImplementation(component, CargoFile) + LibPath, err := filepath.Rel(projectOutputFolder, IntfWrapperStubName) + if err != nil { + return err + } + buildCargoForRustImplementation(component, CargoFile, LibPath) } else { log.Printf("Omitting recreation of Cargo file \"%s\" for Rust Implementation", CargoFileName) } @@ -81,12 +112,23 @@ func BuildImplementationRust(component ComponentDefinition, outputFolder string, return nil } -func buildRustInterfaces(component ComponentDefinition, w LanguageWriter, ClassIdentifier string) error { +func buildRustGlobalStubFile(component ComponentDefinition, w LanguageWriter, ClassIdentifier string) error { return nil } -func buildCargoForRustImplementation(component ComponentDefinition, w LanguageWriter) error { +func buildRustInterfaces(component ComponentDefinition, w LanguageWriter, ClassIdentifier string) error { + + return nil +} +func buildCargoForRustImplementation(component ComponentDefinition, w LanguageWriter, path string) error { + NameSpace := component.NameSpace + w.Writeln("[package]") + w.Writeln(" name = %s", NameSpace) + w.Writeln(" version = 0.1.0 # Version of the library not the interface") + w.Writeln("[lib]") + w.Writeln(" path = \"%s\"", path) + w.Writeln(" crate-type = [\"cdylib\"]") return nil } From fa5cd4f71aed55d571cb75b950a7b7aeb269a641 Mon Sep 17 00:00:00 2001 From: Robert Goss Date: Sat, 23 Sep 2023 01:54:42 +0100 Subject: [PATCH 04/26] Buildable .so (but empty) --- Source/buildimplementationrust.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/buildimplementationrust.go b/Source/buildimplementationrust.go index 5f03a63b..69c88d9d 100644 --- a/Source/buildimplementationrust.go +++ b/Source/buildimplementationrust.go @@ -125,10 +125,10 @@ func buildRustInterfaces(component ComponentDefinition, w LanguageWriter, ClassI func buildCargoForRustImplementation(component ComponentDefinition, w LanguageWriter, path string) error { NameSpace := component.NameSpace w.Writeln("[package]") - w.Writeln(" name = %s", NameSpace) - w.Writeln(" version = 0.1.0 # Version of the library not the interface") + w.Writeln(" name = \"%s\"", NameSpace) + w.Writeln(" version = \"0.1.0\"") w.Writeln("[lib]") - w.Writeln(" path = \"%s\"", path) + w.Writeln(" path = \"%s\"", strings.ReplaceAll(path, "\\", "/")) w.Writeln(" crate-type = [\"cdylib\"]") return nil } From 5833caacd92520faab1e818e2d946daeeef57d2b Mon Sep 17 00:00:00 2001 From: Robert Goss Date: Sat, 23 Sep 2023 16:16:14 +0100 Subject: [PATCH 05/26] Get interface compiled in --- Examples/Primes/libPrimes.xml | 2 +- Source/buildimplementationrust.go | 36 ++++++++++++++++++------------- run.bat | 4 ++++ 3 files changed, 26 insertions(+), 16 deletions(-) create mode 100644 run.bat diff --git a/Examples/Primes/libPrimes.xml b/Examples/Primes/libPrimes.xml index 941baccc..4fa25d33 100644 --- a/Examples/Primes/libPrimes.xml +++ b/Examples/Primes/libPrimes.xml @@ -19,7 +19,7 @@ - + diff --git a/Source/buildimplementationrust.go b/Source/buildimplementationrust.go index 69c88d9d..59190165 100644 --- a/Source/buildimplementationrust.go +++ b/Source/buildimplementationrust.go @@ -44,7 +44,7 @@ import ( // BuildImplementationPascal builds Pascal interface classes, implementation stubs and wrapper code that maps to the Pascal header func BuildImplementationRust(component ComponentDefinition, outputFolder string, stubOutputFolder string, projectOutputFolder string, implementation ComponentDefinitionImplementation) error { - + forceRebuild := true LibraryName := component.LibraryName BaseName := component.BaseName indentString := getIndentationString(implementation.Indentation) @@ -54,23 +54,23 @@ func BuildImplementationRust(component ComponentDefinition, outputFolder string, stubIdentifier = "_" + strings.ToLower(implementation.StubIdentifier) } - IntfHeaderName := BaseName + "_interfaces.rs" - IntfHeaderPath := path.Join(outputFolder, IntfHeaderName) - log.Printf("Creating \"%s\"", IntfHeaderPath) - interfacesrsfile, err := CreateLanguageFile(IntfHeaderPath, indentString) + IntfFileName := BaseName + "_interfaces.rs" + IntfFilePath := path.Join(outputFolder, IntfFileName) + log.Printf("Creating \"%s\"", IntfFilePath) + IntfRSFile, err := CreateLanguageFile(IntfFilePath, indentString) if err != nil { return err } - interfacesrsfile.WriteCLicenseHeader(component, + IntfRSFile.WriteCLicenseHeader(component, fmt.Sprintf("This is an autogenerated rust file in order to allow easy\ndevelopment of %s. The implementer of %s needs to\nderive concrete classes from the abstract classes in this header.", LibraryName, LibraryName), true) - err = buildRustInterfaces(component, interfacesrsfile, implementation.ClassIdentifier) + err = buildRustInterfaces(component, IntfRSFile, implementation.ClassIdentifier) if err != nil { return err } IntfWrapperStubName := path.Join(stubOutputFolder, BaseName+stubIdentifier+".rs") - if !FileExists(IntfWrapperStubName) { + if forceRebuild || !FileExists(IntfWrapperStubName) { log.Printf("Creating \"%s\"", IntfWrapperStubName) stubfile, err := CreateLanguageFile(IntfWrapperStubName, indentString) if err != nil { @@ -79,8 +79,11 @@ func BuildImplementationRust(component ComponentDefinition, outputFolder string, stubfile.WriteCLicenseHeader(component, fmt.Sprintf("This is an autogenerated Rust implementation file in order to allow easy\ndevelopment of %s. It needs to be generated only once.", LibraryName), true) - - err = buildRustGlobalStubFile(component, stubfile, implementation.ClassIdentifier) + RelInterfaceFile, err := filepath.Rel(stubOutputFolder, IntfFilePath) + if err != nil { + return err + } + err = buildRustGlobalStubFile(component, stubfile, implementation.ClassIdentifier, RelInterfaceFile) if err != nil { return err } @@ -90,7 +93,7 @@ func BuildImplementationRust(component ComponentDefinition, outputFolder string, if len(projectOutputFolder) > 0 { CargoFileName := path.Join(projectOutputFolder, "Cargo.toml") - if !FileExists(CargoFileName) { + if forceRebuild || !FileExists(CargoFileName) { log.Printf("Creating Cargo file \"%s\" for Rust Implementation", CargoFileName) CargoFile, err := CreateLanguageFile(CargoFileName, indentString) if err != nil { @@ -112,18 +115,21 @@ func BuildImplementationRust(component ComponentDefinition, outputFolder string, return nil } -func buildRustGlobalStubFile(component ComponentDefinition, w LanguageWriter, ClassIdentifier string) error { - +func buildRustGlobalStubFile(component ComponentDefinition, w LanguageWriter, ClassIdentifier string, RelInterfaceFile string) error { + w.Writeln("") + // Get all modules + w.Writeln("#[path = \"%s\"]", strings.ReplaceAll(RelInterfaceFile, "\\", "/")) + IntfName := strings.TrimSuffix(filepath.Base(RelInterfaceFile), ".rs") + w.Writeln("mod %s;", IntfName) return nil } func buildRustInterfaces(component ComponentDefinition, w LanguageWriter, ClassIdentifier string) error { - return nil } func buildCargoForRustImplementation(component ComponentDefinition, w LanguageWriter, path string) error { - NameSpace := component.NameSpace + projectName := strings.ToLower(component.NameSpace) w.Writeln("[package]") w.Writeln(" name = \"%s\"", NameSpace) w.Writeln(" version = \"0.1.0\"") diff --git a/run.bat b/run.bat new file mode 100644 index 00000000..04cc5fdd --- /dev/null +++ b/run.bat @@ -0,0 +1,4 @@ +CALL Build/build.bat +act.exe tmp/libPrimes.xml -o tmp +cd tmp/libPrimes_component/Implementations/Rust +cargo b \ No newline at end of file From 9b1ca8f9e2b3dd75a1d768f7758b2682f7d71a5b Mon Sep 17 00:00:00 2001 From: Robert Goss Date: Sat, 23 Sep 2023 21:37:43 +0100 Subject: [PATCH 06/26] Merge branch 'gossr/rust_impl' of https://github.com/robertgoss/AutomaticComponentToolkit into gossr/rust_impl --- Source/buildimplementationrust.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/buildimplementationrust.go b/Source/buildimplementationrust.go index 59190165..69b2d0a4 100644 --- a/Source/buildimplementationrust.go +++ b/Source/buildimplementationrust.go @@ -131,7 +131,7 @@ func buildRustInterfaces(component ComponentDefinition, w LanguageWriter, ClassI func buildCargoForRustImplementation(component ComponentDefinition, w LanguageWriter, path string) error { projectName := strings.ToLower(component.NameSpace) w.Writeln("[package]") - w.Writeln(" name = \"%s\"", NameSpace) + w.Writeln(" name = \"%s\"", projectName) w.Writeln(" version = \"0.1.0\"") w.Writeln("[lib]") w.Writeln(" path = \"%s\"", strings.ReplaceAll(path, "\\", "/")) From 186f12bbfac19394690e374e67f9870c277b9638 Mon Sep 17 00:00:00 2001 From: Robert Goss Date: Sat, 23 Sep 2023 22:51:59 +0100 Subject: [PATCH 07/26] Output struct and enum types needed --- Build/build.bat | 2 +- Build/build.sh | 2 +- Source/buildimplementationrust.go | 4 + Source/languagerust.go | 153 ++++++++++++++++++++++++++++++ 4 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 Source/languagerust.go diff --git a/Build/build.bat b/Build/build.bat index 07dbbede..0dd1d6e8 100644 --- a/Build/build.bat +++ b/Build/build.bat @@ -3,7 +3,7 @@ set startingDir="%CD%" set basepath="%~dp0" cd %basepath%\..\Source -set Sources=actutils.go automaticcomponenttoolkit.go buildbindingccpp.go buildbindingcsharp.go buildbindinggo.go buildbindingnode.go buildbindingpascal.go buildbindingpython.go buildimplementationcpp.go buildimplementationpascal.go buildimplementationrust.go componentdefinition.go componentdiff.go languagewriter.go languagec.go languagecpp.go languagepascal.go +set Sources=actutils.go automaticcomponenttoolkit.go buildbindingccpp.go buildbindingcsharp.go buildbindinggo.go buildbindingnode.go buildbindingpascal.go buildbindingpython.go buildimplementationcpp.go buildimplementationpascal.go buildimplementationrust.go componentdefinition.go componentdiff.go languagewriter.go languagec.go languagecpp.go languagepascal.go languagerust.go set GOARCH=amd64 echo "Build act.win64.exe" go build -o ..\act.win64.exe %Sources% diff --git a/Build/build.sh b/Build/build.sh index 710303f0..8e8a31ce 100755 --- a/Build/build.sh +++ b/Build/build.sh @@ -6,7 +6,7 @@ startingpath="$(pwd)" basepath="$(cd "$(dirname "$0")" && pwd)" cd "$basepath/../Source" -Sources="actutils.go automaticcomponenttoolkit.go buildbindingccpp.go buildbindingccppdocumentation.go buildbindingcsharp.go buildbindinggo.go buildbindingnode.go buildbindingpascal.go buildbindingpython.go buildbindingjava.go buildimplementationcpp.go buildimplementationpascal.go buildimplementationrust.go componentdefinition.go componentdiff.go languagewriter.go languagec.go languagecpp.go languagepascal.go" +Sources="actutils.go automaticcomponenttoolkit.go buildbindingccpp.go buildbindingccppdocumentation.go buildbindingcsharp.go buildbindinggo.go buildbindingnode.go buildbindingpascal.go buildbindingpython.go buildbindingjava.go buildimplementationcpp.go buildimplementationpascal.go buildimplementationrust.go componentdefinition.go componentdiff.go languagewriter.go languagec.go languagecpp.go languagepascal.go languagerust.go" echo "Build act.win64.exe" export GOARCH="amd64" diff --git a/Source/buildimplementationrust.go b/Source/buildimplementationrust.go index 69b2d0a4..1b5e60f7 100644 --- a/Source/buildimplementationrust.go +++ b/Source/buildimplementationrust.go @@ -64,6 +64,10 @@ func BuildImplementationRust(component ComponentDefinition, outputFolder string, IntfRSFile.WriteCLicenseHeader(component, fmt.Sprintf("This is an autogenerated rust file in order to allow easy\ndevelopment of %s. The implementer of %s needs to\nderive concrete classes from the abstract classes in this header.", LibraryName, LibraryName), true) + err = writeRustBaseTypeDefinitions(component, IntfRSFile, component.NameSpace, BaseName) + if err != nil { + return err + } err = buildRustInterfaces(component, IntfRSFile, implementation.ClassIdentifier) if err != nil { return err diff --git a/Source/languagerust.go b/Source/languagerust.go new file mode 100644 index 00000000..c66113e9 --- /dev/null +++ b/Source/languagerust.go @@ -0,0 +1,153 @@ +/*++ + +Copyright (C) 2023 Autodesk Inc. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--*/ + +////////////////////////////////////////////////////////////////////////////////////////////////////// +// languagerust.go +// functions to generate the Rust-layer of a library's API (can be used in bindings or implementation) +////////////////////////////////////////////////////////////////////////////////////////////////////// + +package main + +import ( + "fmt" + "strings" +) + +func writeRustBaseTypeDefinitions(componentdefinition ComponentDefinition, w LanguageWriter, NameSpace string, BaseName string) error { + w.Writeln("#[allow(unused_imports)]") + w.Writeln("use std::ffi;") + w.Writeln("") + w.Writeln("/*************************************************************************************************************************") + w.Writeln(" Version definition for %s", NameSpace) + w.Writeln("**************************************************************************************************************************/") + w.Writeln("") + w.Writeln("const %s_VERSION_MAJOR : usize = %d;", strings.ToUpper(NameSpace), majorVersion(componentdefinition.Version)) + w.Writeln("const %s_VERSION_MINOR : usize = %d;", strings.ToUpper(NameSpace), minorVersion(componentdefinition.Version)) + w.Writeln("const %s_VERSION_MICRO : usize= %d;", strings.ToUpper(NameSpace), microVersion(componentdefinition.Version)) + w.Writeln("const %s_VERSION_PRERELEASEINFO : &str = \"%s\";", strings.ToUpper(NameSpace), preReleaseInfo(componentdefinition.Version)) + w.Writeln("const %s_VERSION_BUILDINFO : &str = \"%s\";", strings.ToUpper(NameSpace), buildInfo(componentdefinition.Version)) + + w.Writeln("") + w.Writeln("") + + if len(componentdefinition.Enums) > 0 { + w.Writeln("/*************************************************************************************************************************") + w.Writeln(" Enum definitions for %s", NameSpace) + w.Writeln("**************************************************************************************************************************/") + w.Writeln("") + for i := 0; i < len(componentdefinition.Enums); i++ { + enuminfo := componentdefinition.Enums[i] + w.Writeln("#[repr(C, u16)]") + w.Writeln("#[allow(non_snake_case)]") + w.Writeln("pub enum %s {", enuminfo.Name) + for j := 0; j < len(enuminfo.Options); j++ { + option := enuminfo.Options[j] + sep := "," + if j == len(enuminfo.Options)-1 { + sep = "" + } + w.Writeln(" pub %s = %d%s", option.Name, option.Value, sep) + } + w.Writeln("}") + w.Writeln("") + } + } + + if len(componentdefinition.Structs) > 0 { + w.Writeln("/*************************************************************************************************************************") + w.Writeln(" Interface Struct definitions for %s", NameSpace) + w.Writeln("**************************************************************************************************************************/") + w.Writeln("") + for i := 0; i < len(componentdefinition.Structs); i++ { + structinfo := componentdefinition.Structs[i] + w.Writeln("#[repr(C)]") + w.Writeln("#[allow(non_snake_case)]") + w.Writeln("pub struct %s {", structinfo.Name) + for j := 0; j < len(structinfo.Members); j++ { + member := structinfo.Members[j] + last := j == len(structinfo.Members)-1 + err := writeRustMemberLine(member, w, structinfo.Name, last) + if err != nil { + return err + } + } + w.Writeln("}") + w.Writeln("") + } + } + + return nil +} + +func writeRustMemberLine(member ComponentDefinitionMember, w LanguageWriter, StructName string, last bool) error { + suffix := "," + if last { + suffix = "" + } + arraysuffix := suffix + if member.Rows > 0 { + if member.Columns > 0 { + arraysuffix = fmt.Sprintf("[%d][%d]%s", member.Columns, member.Rows, suffix) + } else { + arraysuffix = fmt.Sprintf("[%d]%s", member.Rows, suffix) + } + } + switch member.Type { + case "uint8": + w.Writeln(" pub %s: u8%s", member.Name, arraysuffix) + case "uint16": + w.Writeln(" pub %s: u16%s", member.Name, arraysuffix) + case "uint32": + w.Writeln(" pub %s: u32%s", member.Name, arraysuffix) + case "uint64": + w.Writeln(" pub %s: u64%s", member.Name, arraysuffix) + case "int8": + w.Writeln(" pub %s: i8%s", member.Name, arraysuffix) + case "int16": + w.Writeln(" pub %s: i16%s", member.Name, arraysuffix) + case "int32": + w.Writeln(" pub %s: i32%s", member.Name, arraysuffix) + case "int64": + w.Writeln(" pub %s: i64%s", member.Name, arraysuffix) + case "bool": + w.Writeln(" pub %s: bool%s", member.Name, arraysuffix) + case "single": + w.Writeln(" pub %s: f32%s", member.Name, arraysuffix) + case "double": + w.Writeln(" pub %s: f64%s", member.Name, arraysuffix) + case "pointer": + w.Writeln(" pub %s: c_void%s", member.Name, arraysuffix) + case "string": + return fmt.Errorf("it is not possible for struct %s to contain a string value", StructName) + case "class", "optionalclass": + return fmt.Errorf("it is not possible for struct %s to contain a handle value", StructName) + case "enum": + w.Writeln(" pub %s: u16%s", member.Name, arraysuffix) + } + return nil +} From 846785a65c65e854fc1e938a705f7ec8906ce1bd Mon Sep 17 00:00:00 2001 From: Robert Goss Date: Sun, 24 Sep 2023 19:56:08 +0100 Subject: [PATCH 08/26] Start generating parameters --- Source/languagerust.go | 157 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) diff --git a/Source/languagerust.go b/Source/languagerust.go index c66113e9..559b4c97 100644 --- a/Source/languagerust.go +++ b/Source/languagerust.go @@ -55,6 +55,12 @@ func writeRustBaseTypeDefinitions(componentdefinition ComponentDefinition, w Lan w.Writeln("") w.Writeln("") + w.Writeln("/*************************************************************************************************************************") + w.Writeln(" Basic pointers definition for %s", NameSpace) + w.Writeln("**************************************************************************************************************************/") + w.Writeln("") + w.Writeln("type Handle = std::ffi::c_void") + if len(componentdefinition.Enums) > 0 { w.Writeln("/*************************************************************************************************************************") w.Writeln(" Enum definitions for %s", NameSpace) @@ -101,6 +107,33 @@ func writeRustBaseTypeDefinitions(componentdefinition ComponentDefinition, w Lan } } + if len(componentdefinition.Functions) > 0 { + w.Writeln("/*************************************************************************************************************************") + w.Writeln(" Function type definitions for %s", NameSpace) + w.Writeln("**************************************************************************************************************************/") + w.Writeln("") + for i := 0; i < len(componentdefinition.Functions); i++ { + funcinfo := componentdefinition.Functions[i] + w.Writeln("// %s", funcinfo.FunctionDescription) + w.Writeln("//") + parameterString := "" + for j := 0; j < len(funcinfo.Params); j++ { + RustParameters, err := generatePlainRustParameters(funcinfo.Params[j]) + RustParameter := RustParameters[0] + if err != nil { + return err + } + w.Writeln("// %s", RustParameter.ParamComment) + if j == 0 { + parameterString += fmt.Sprintf("%s : %s", RustParameter.ParamName, RustParameter.ParamType) + } else { + parameterString += fmt.Sprintf(", %s : %s", RustParameter.ParamName, RustParameter.ParamType) + } + } + w.Writeln("type %s = unsafe extern \"C\" fn(%s);", funcinfo.FunctionName, parameterString) + } + } + return nil } @@ -151,3 +184,127 @@ func writeRustMemberLine(member ComponentDefinitionMember, w LanguageWriter, Str } return nil } + +// CParameter is a handy representation of a function parameter in C +type RustParameter struct { + ParamType string + ParamName string + ParamComment string +} + +func generatePlainRustParameters(param ComponentDefinitionParam) ([]RustParameter, error) { + +} + +func generateRustParameterType(param ComponentDefinitionParam, isPlain bool) (string, error) { + RustParamTypeName := "" + ParamTypeName := param.ParamType + ParamClass := param.ParamClass + switch ParamTypeName { + case "uint8": + RustParamTypeName = "u8" + + case "uint16": + RustParamTypeName = "u16" + + case "uint32": + RustParamTypeName = "u32" + + case "uint64": + RustParamTypeName = "u64" + + case "int8": + RustParamTypeName = "i8" + + case "int16": + RustParamTypeName = "i16" + + case "int32": + RustParamTypeName = "i32" + + case "int64": + RustParamTypeName = "i64" + + case "bool": + if isPlain { + RustParamTypeName = "u8" + } else { + RustParamTypeName = "bool" + } + + case "single": + RustParamTypeName = "f32" + + case "double": + RustParamTypeName = "f64" + + case "pointer": + RustParamTypeName = "c_void" + + case "string": + if isPlain { + RustParamTypeName = "*mut char" + } else { + // TODO + return "", fmt.Errorf("Not yet handled") + } + + case "enum": + if isPlain { + RustParamTypeName = fmt.Sprintf("u16") + } else { + // TODO + return "", fmt.Errorf("Not yet handled") + } + + case "functiontype": + if isPlain { + RustParamTypeName = fmt.Sprintf("%s", ParamClass) + } else { + // TODO + return "", fmt.Errorf("Not yet handled") + } + + case "struct": + if isPlain { + RustParamTypeName = fmt.Sprintf("*mut %s", ParamClass) + } else { + // TODO + return "", fmt.Errorf("Not yet handled") + } + + case "basicarray": + basicTypeName, err := generateRustParameterType(param, isPlain) + if err != nil { + return "", err + } + + if isPlain { + RustParamTypeName = fmt.Sprintf("*mut %s", basicTypeName) + } else { + // TODO + return "", fmt.Errorf("Not yet handled") + } + + case "structarray": + if isPlain { + RustParamTypeName = fmt.Sprintf("*mut %s", ParamClass) + } else { + // TODO + return "", fmt.Errorf("Not yet handled") + } + + case "class", "optionalclass": + if isPlain { + RustParamTypeName = fmt.Sprintf("Handle") + } else { + // TODO + return "", fmt.Errorf("Not yet handled") + } + + default: + return "", fmt.Errorf("invalid parameter type \"%s\" for Pascal parameter", ParamTypeName) + } + + return RustParamTypeName, nil +} From d3a745ac49dd1a1455ae1a90a4d57f11c05f60a7 Mon Sep 17 00:00:00 2001 From: Robert Goss Date: Sun, 24 Sep 2023 21:45:43 +0100 Subject: [PATCH 09/26] Add basic writing of interfaces --- Source/buildimplementationrust.go | 76 +++++++++++++++++ Source/languagerust.go | 131 +++++++++++++++++++++--------- 2 files changed, 167 insertions(+), 40 deletions(-) diff --git a/Source/buildimplementationrust.go b/Source/buildimplementationrust.go index 1b5e60f7..9c7076d3 100644 --- a/Source/buildimplementationrust.go +++ b/Source/buildimplementationrust.go @@ -129,6 +129,18 @@ func buildRustGlobalStubFile(component ComponentDefinition, w LanguageWriter, Cl } func buildRustInterfaces(component ComponentDefinition, w LanguageWriter, ClassIdentifier string) error { + NameSpace := component.NameSpace + w.Writeln("/*************************************************************************************************************************") + w.Writeln(" Traits defined for %s", NameSpace) + w.Writeln("**************************************************************************************************************************/") + w.Writeln("") + for i := 0; i < len(component.Classes); i++ { + classinfo := component.Classes[i] + err := writeRustTrait(component, classinfo, w) + if err != nil { + return err + } + } return nil } @@ -142,3 +154,67 @@ func buildCargoForRustImplementation(component ComponentDefinition, w LanguageWr w.Writeln(" crate-type = [\"cdylib\"]") return nil } + +func writeRustTrait(component ComponentDefinition, class ComponentDefinitionClass, w LanguageWriter) error { + w.Writeln("// Trait for interface %s", class.ClassName) + w.Writeln("//") + if class.ClassDescription != "" { + w.Writeln("// %s", class.ClassDescription) + w.Writeln("//") + } + parentClassString := "" + if !component.isBaseClass(class) { + if class.ParentClass == "" { + parentClassString = fmt.Sprintf(": %s ", component.Global.BaseClassName) + } else { + parentClassString = fmt.Sprintf(": %s ", class.ParentClass) + } + } + w.Writeln("trait %s %s {", class.ClassName, parentClassString) + w.AddIndentationLevel(1) + // TODO add the base class methods! + + for j := 0; j < len(class.Methods); j++ { + method := class.Methods[j] + w.Writeln("") + err := writeRustTraitFn(method, w) + if err != nil { + return err + } + } + w.ResetIndentationLevel() + w.Writeln("}") + w.Writeln("") + w.Writeln("") + return nil +} + +func writeRustTraitFn(method ComponentDefinitionMethod, w LanguageWriter) error { + methodName := toSnakeCase(method.MethodName) + w.Writeln("// %s", methodName) + w.Writeln("//") + w.Writeln("// %s", method.MethodDescription) + parameterString := "&mut self" + returnType := "" + for k := 0; k < len(method.Params); k++ { + param := method.Params[k] + RustParams, err := generateRustParameters(param, false) + if err != nil { + return err + } + RustParam := RustParams[0] + if param.ParamPass != "return" { + parameterString += fmt.Sprintf(", %s : %s", RustParam.ParamName, RustParam.ParamType) + } else { + returnType = RustParam.ParamType + } + w.Writeln("// %s", RustParam.ParamComment) + } + w.Writeln("//") + if returnType == "" { + w.Writeln("fn %s(%s);", methodName, parameterString) + } else { + w.Writeln("fn %s(%s) -> %s;", methodName, parameterString, returnType) + } + return nil +} diff --git a/Source/languagerust.go b/Source/languagerust.go index 559b4c97..a9db05d1 100644 --- a/Source/languagerust.go +++ b/Source/languagerust.go @@ -35,9 +35,16 @@ package main import ( "fmt" + "regexp" "strings" ) +func toSnakeCase(BaseType string) string { + var matchCapital = regexp.MustCompile("(.)([A-Z])") + underscored := matchCapital.ReplaceAllString(BaseType, "${1}_${2}") + return strings.ToLower(underscored) +} + func writeRustBaseTypeDefinitions(componentdefinition ComponentDefinition, w LanguageWriter, NameSpace string, BaseName string) error { w.Writeln("#[allow(unused_imports)]") w.Writeln("use std::ffi;") @@ -59,7 +66,7 @@ func writeRustBaseTypeDefinitions(componentdefinition ComponentDefinition, w Lan w.Writeln(" Basic pointers definition for %s", NameSpace) w.Writeln("**************************************************************************************************************************/") w.Writeln("") - w.Writeln("type Handle = std::ffi::c_void") + w.Writeln("type Handle = std::ffi::c_void;") if len(componentdefinition.Enums) > 0 { w.Writeln("/*************************************************************************************************************************") @@ -69,15 +76,16 @@ func writeRustBaseTypeDefinitions(componentdefinition ComponentDefinition, w Lan for i := 0; i < len(componentdefinition.Enums); i++ { enuminfo := componentdefinition.Enums[i] w.Writeln("#[repr(C, u16)]") - w.Writeln("#[allow(non_snake_case)]") - w.Writeln("pub enum %s {", enuminfo.Name) + enumName := enuminfo.Name + w.Writeln("pub enum %s {", enumName) for j := 0; j < len(enuminfo.Options); j++ { option := enuminfo.Options[j] sep := "," if j == len(enuminfo.Options)-1 { sep = "" } - w.Writeln(" pub %s = %d%s", option.Name, option.Value, sep) + optionName := option.Name + w.Writeln(" pub %s = %d%s", optionName, option.Value, sep) } w.Writeln("}") w.Writeln("") @@ -91,9 +99,9 @@ func writeRustBaseTypeDefinitions(componentdefinition ComponentDefinition, w Lan w.Writeln("") for i := 0; i < len(componentdefinition.Structs); i++ { structinfo := componentdefinition.Structs[i] + structName := structinfo.Name w.Writeln("#[repr(C)]") - w.Writeln("#[allow(non_snake_case)]") - w.Writeln("pub struct %s {", structinfo.Name) + w.Writeln("pub struct %s {", structName) for j := 0; j < len(structinfo.Members); j++ { member := structinfo.Members[j] last := j == len(structinfo.Members)-1 @@ -118,7 +126,7 @@ func writeRustBaseTypeDefinitions(componentdefinition ComponentDefinition, w Lan w.Writeln("//") parameterString := "" for j := 0; j < len(funcinfo.Params); j++ { - RustParameters, err := generatePlainRustParameters(funcinfo.Params[j]) + RustParameters, err := generateRustParameters(funcinfo.Params[j], true) RustParameter := RustParameters[0] if err != nil { return err @@ -130,7 +138,9 @@ func writeRustBaseTypeDefinitions(componentdefinition ComponentDefinition, w Lan parameterString += fmt.Sprintf(", %s : %s", RustParameter.ParamName, RustParameter.ParamType) } } - w.Writeln("type %s = unsafe extern \"C\" fn(%s);", funcinfo.FunctionName, parameterString) + w.Writeln("//") + funcName := funcinfo.FunctionName + w.Writeln("type %s = unsafe extern \"C\" fn(%s);", funcName, parameterString) } } @@ -150,37 +160,38 @@ func writeRustMemberLine(member ComponentDefinitionMember, w LanguageWriter, Str arraysuffix = fmt.Sprintf("[%d]%s", member.Rows, suffix) } } + memberName := toSnakeCase(member.Name) switch member.Type { case "uint8": - w.Writeln(" pub %s: u8%s", member.Name, arraysuffix) + w.Writeln(" pub %s: u8%s", memberName, arraysuffix) case "uint16": - w.Writeln(" pub %s: u16%s", member.Name, arraysuffix) + w.Writeln(" pub %s: u16%s", memberName, arraysuffix) case "uint32": - w.Writeln(" pub %s: u32%s", member.Name, arraysuffix) + w.Writeln(" pub %s: u32%s", memberName, arraysuffix) case "uint64": - w.Writeln(" pub %s: u64%s", member.Name, arraysuffix) + w.Writeln(" pub %s: u64%s", memberName, arraysuffix) case "int8": - w.Writeln(" pub %s: i8%s", member.Name, arraysuffix) + w.Writeln(" pub %s: i8%s", memberName, arraysuffix) case "int16": - w.Writeln(" pub %s: i16%s", member.Name, arraysuffix) + w.Writeln(" pub %s: i16%s", memberName, arraysuffix) case "int32": - w.Writeln(" pub %s: i32%s", member.Name, arraysuffix) + w.Writeln(" pub %s: i32%s", memberName, arraysuffix) case "int64": - w.Writeln(" pub %s: i64%s", member.Name, arraysuffix) + w.Writeln(" pub %s: i64%s", memberName, arraysuffix) case "bool": - w.Writeln(" pub %s: bool%s", member.Name, arraysuffix) + w.Writeln(" pub %s: bool%s", memberName, arraysuffix) case "single": - w.Writeln(" pub %s: f32%s", member.Name, arraysuffix) + w.Writeln(" pub %s: f32%s", memberName, arraysuffix) case "double": - w.Writeln(" pub %s: f64%s", member.Name, arraysuffix) + w.Writeln(" pub %s: f64%s", memberName, arraysuffix) case "pointer": - w.Writeln(" pub %s: c_void%s", member.Name, arraysuffix) + w.Writeln(" pub %s: c_void%s", memberName, arraysuffix) case "string": return fmt.Errorf("it is not possible for struct %s to contain a string value", StructName) case "class", "optionalclass": return fmt.Errorf("it is not possible for struct %s to contain a handle value", StructName) case "enum": - w.Writeln(" pub %s: u16%s", member.Name, arraysuffix) + w.Writeln(" pub %s: u16%s", memberName, arraysuffix) } return nil } @@ -192,8 +203,28 @@ type RustParameter struct { ParamComment string } -func generatePlainRustParameters(param ComponentDefinitionParam) ([]RustParameter, error) { +func generateRustParameters(param ComponentDefinitionParam, isPlain bool) ([]RustParameter, error) { + Params := make([]RustParameter, 1) + ParamTypeName, err := generateRustParameterType(param, isPlain) + if err != nil { + return nil, err + } + + if isPlain { + if param.ParamType == "basicarray" { + return nil, fmt.Errorf("Not yet handled") + } + if param.ParamType == "structarray" { + return nil, fmt.Errorf("Not yet handled") + } + } + + Params[0].ParamType = ParamTypeName + Params[0].ParamName = toSnakeCase(param.ParamName) + Params[0].ParamComment = fmt.Sprintf("* @param[%s] %s - %s", param.ParamPass, Params[0].ParamName, param.ParamDescription) + + return Params, nil } func generateRustParameterType(param ComponentDefinitionParam, isPlain bool) (string, error) { @@ -246,35 +277,43 @@ func generateRustParameterType(param ComponentDefinitionParam, isPlain bool) (st RustParamTypeName = "*mut char" } else { // TODO - return "", fmt.Errorf("Not yet handled") + return "", fmt.Errorf("%s Not yet handled", param.ParamType) } case "enum": if isPlain { RustParamTypeName = fmt.Sprintf("u16") } else { - // TODO - return "", fmt.Errorf("Not yet handled") + switch param.ParamPass { + case "out": + RustParamTypeName = fmt.Sprintf("&mut %s", ParamClass) + case "in", "return": + RustParamTypeName = fmt.Sprintf("%s", ParamClass) + } } case "functiontype": - if isPlain { - RustParamTypeName = fmt.Sprintf("%s", ParamClass) - } else { - // TODO - return "", fmt.Errorf("Not yet handled") - } + RustParamTypeName = fmt.Sprintf("%s", ParamClass) case "struct": if isPlain { RustParamTypeName = fmt.Sprintf("*mut %s", ParamClass) } else { - // TODO - return "", fmt.Errorf("Not yet handled") + switch param.ParamPass { + case "out": + RustParamTypeName = fmt.Sprintf("&mut %s", ParamClass) + case "in": + RustParamTypeName = fmt.Sprintf("& %s", ParamClass) + case "return": + RustParamTypeName = fmt.Sprintf("%s", ParamClass) + } } case "basicarray": - basicTypeName, err := generateRustParameterType(param, isPlain) + basicParam := param + basicParam.ParamType = param.ParamClass + basicParam.ParamPass = "return" + basicTypeName, err := generateRustParameterType(basicParam, isPlain) if err != nil { return "", err } @@ -282,16 +321,28 @@ func generateRustParameterType(param ComponentDefinitionParam, isPlain bool) (st if isPlain { RustParamTypeName = fmt.Sprintf("*mut %s", basicTypeName) } else { - // TODO - return "", fmt.Errorf("Not yet handled") + switch param.ParamPass { + case "out": + RustParamTypeName = fmt.Sprintf("&mut Vec<%s>", basicTypeName) + case "in": + RustParamTypeName = fmt.Sprintf("&[%s]", basicTypeName) + case "return": + RustParamTypeName = fmt.Sprintf("Vec<%s>", basicTypeName) + } } case "structarray": if isPlain { RustParamTypeName = fmt.Sprintf("*mut %s", ParamClass) } else { - // TODO - return "", fmt.Errorf("Not yet handled") + switch param.ParamPass { + case "out": + RustParamTypeName = fmt.Sprintf("&mut Vec<%s>", ParamClass) + case "in": + RustParamTypeName = fmt.Sprintf("&[%s]", ParamClass) + case "return": + RustParamTypeName = fmt.Sprintf("Vec<%s>", ParamClass) + } } case "class", "optionalclass": @@ -299,11 +350,11 @@ func generateRustParameterType(param ComponentDefinitionParam, isPlain bool) (st RustParamTypeName = fmt.Sprintf("Handle") } else { // TODO - return "", fmt.Errorf("Not yet handled") + return "", fmt.Errorf("%s Not yet handled", param.ParamType) } default: - return "", fmt.Errorf("invalid parameter type \"%s\" for Pascal parameter", ParamTypeName) + return "", fmt.Errorf("invalid parameter type \"%s\" for Rust parameter", ParamTypeName) } return RustParamTypeName, nil From 8c2a41c0800cc764a3bae4d90a300691dd51e890 Mon Sep 17 00:00:00 2001 From: Robert Goss Date: Sun, 24 Sep 2023 22:48:14 +0100 Subject: [PATCH 10/26] Handle base class auto methods --- Source/buildimplementationrust.go | 15 ++++++++++++--- Source/languagerust.go | 12 +++++++++--- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/Source/buildimplementationrust.go b/Source/buildimplementationrust.go index 9c7076d3..f4cf4ede 100644 --- a/Source/buildimplementationrust.go +++ b/Source/buildimplementationrust.go @@ -172,10 +172,19 @@ func writeRustTrait(component ComponentDefinition, class ComponentDefinitionClas } w.Writeln("trait %s %s {", class.ClassName, parentClassString) w.AddIndentationLevel(1) - // TODO add the base class methods! + methods := class.Methods + if component.isBaseClass(class) { + methods = append( + methods, + GetLastErrorMessageMethod(), + ClearErrorMessageMethod(), + RegisterErrorMessageMethod(), + IncRefCountMethod(), + DecRefCountMethod()) + } - for j := 0; j < len(class.Methods); j++ { - method := class.Methods[j] + for j := 0; j < len(methods); j++ { + method := methods[j] w.Writeln("") err := writeRustTraitFn(method, w) if err != nil { diff --git a/Source/languagerust.go b/Source/languagerust.go index a9db05d1..1da9030e 100644 --- a/Source/languagerust.go +++ b/Source/languagerust.go @@ -276,8 +276,14 @@ func generateRustParameterType(param ComponentDefinitionParam, isPlain bool) (st if isPlain { RustParamTypeName = "*mut char" } else { - // TODO - return "", fmt.Errorf("%s Not yet handled", param.ParamType) + switch param.ParamPass { + case "out": + RustParamTypeName = "&mut str" + case "in": + RustParamTypeName = "&str" + case "return": + RustParamTypeName = "String" + } } case "enum": @@ -303,7 +309,7 @@ func generateRustParameterType(param ComponentDefinitionParam, isPlain bool) (st case "out": RustParamTypeName = fmt.Sprintf("&mut %s", ParamClass) case "in": - RustParamTypeName = fmt.Sprintf("& %s", ParamClass) + RustParamTypeName = fmt.Sprintf("&%s", ParamClass) case "return": RustParamTypeName = fmt.Sprintf("%s", ParamClass) } From 9760ff300f7529524d62390522329971d34e3693 Mon Sep 17 00:00:00 2001 From: Robert Goss Date: Sun, 24 Sep 2023 23:12:17 +0100 Subject: [PATCH 11/26] Make interface for global methods --- Source/buildimplementationrust.go | 42 ++++++++++++++++++++++++++++--- Source/languagerust.go | 10 ++++++-- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/Source/buildimplementationrust.go b/Source/buildimplementationrust.go index f4cf4ede..646da31a 100644 --- a/Source/buildimplementationrust.go +++ b/Source/buildimplementationrust.go @@ -141,6 +141,14 @@ func buildRustInterfaces(component ComponentDefinition, w LanguageWriter, ClassI return err } } + w.Writeln("/*************************************************************************************************************************") + w.Writeln(" Trait defined for global methods of %s", NameSpace) + w.Writeln("**************************************************************************************************************************/") + w.Writeln("") + err := writeRustGlobalTrait(component, w) + if err != nil { + return err + } return nil } @@ -186,7 +194,7 @@ func writeRustTrait(component ComponentDefinition, class ComponentDefinitionClas for j := 0; j < len(methods); j++ { method := methods[j] w.Writeln("") - err := writeRustTraitFn(method, w) + err := writeRustTraitFn(method, w, true) if err != nil { return err } @@ -198,12 +206,15 @@ func writeRustTrait(component ComponentDefinition, class ComponentDefinitionClas return nil } -func writeRustTraitFn(method ComponentDefinitionMethod, w LanguageWriter) error { +func writeRustTraitFn(method ComponentDefinitionMethod, w LanguageWriter, hasSelf bool) error { methodName := toSnakeCase(method.MethodName) w.Writeln("// %s", methodName) w.Writeln("//") w.Writeln("// %s", method.MethodDescription) - parameterString := "&mut self" + parameterString := "" + if hasSelf { + parameterString += "&mut self" + } returnType := "" for k := 0; k < len(method.Params); k++ { param := method.Params[k] @@ -213,7 +224,11 @@ func writeRustTraitFn(method ComponentDefinitionMethod, w LanguageWriter) error } RustParam := RustParams[0] if param.ParamPass != "return" { - parameterString += fmt.Sprintf(", %s : %s", RustParam.ParamName, RustParam.ParamType) + if parameterString == "" { + parameterString += fmt.Sprintf("%s : %s", RustParam.ParamName, RustParam.ParamType) + } else { + parameterString += fmt.Sprintf(", %s : %s", RustParam.ParamName, RustParam.ParamType) + } } else { returnType = RustParam.ParamType } @@ -227,3 +242,22 @@ func writeRustTraitFn(method ComponentDefinitionMethod, w LanguageWriter) error } return nil } + +func writeRustGlobalTrait(component ComponentDefinition, w LanguageWriter) error { + w.Writeln("// Wrapper trait for global methods") + w.Writeln("//") + w.Writeln("trait Wrapper {") + w.AddIndentationLevel(1) + methods := component.Global.Methods + for j := 0; j < len(methods); j++ { + method := methods[j] + w.Writeln("") + err := writeRustTraitFn(method, w, false) + if err != nil { + return err + } + } + w.ResetIndentationLevel() + w.Writeln("}") + return nil +} diff --git a/Source/languagerust.go b/Source/languagerust.go index 1da9030e..29a037c4 100644 --- a/Source/languagerust.go +++ b/Source/languagerust.go @@ -355,8 +355,14 @@ func generateRustParameterType(param ComponentDefinitionParam, isPlain bool) (st if isPlain { RustParamTypeName = fmt.Sprintf("Handle") } else { - // TODO - return "", fmt.Errorf("%s Not yet handled", param.ParamType) + switch param.ParamPass { + case "out": + RustParamTypeName = fmt.Sprintf("&mut impl %s", ParamClass) + case "in": + RustParamTypeName = fmt.Sprintf("& impl %s", ParamClass) + case "return": + RustParamTypeName = fmt.Sprintf("Box", ParamClass) + } } default: From 8901513722baf5b7ee67f50ad0fdbc8f9ca1c986 Mon Sep 17 00:00:00 2001 From: Robert Goss Date: Mon, 25 Sep 2023 01:05:54 +0100 Subject: [PATCH 12/26] Create the stub impls --- Source/buildimplementationrust.go | 249 +++++++++++++++++++++++++++--- Source/languagerust.go | 2 +- 2 files changed, 232 insertions(+), 19 deletions(-) diff --git a/Source/buildimplementationrust.go b/Source/buildimplementationrust.go index 646da31a..412a64b1 100644 --- a/Source/buildimplementationrust.go +++ b/Source/buildimplementationrust.go @@ -47,6 +47,7 @@ func BuildImplementationRust(component ComponentDefinition, outputFolder string, forceRebuild := true LibraryName := component.LibraryName BaseName := component.BaseName + modfiles := make([]string, 0) indentString := getIndentationString(implementation.Indentation) stubIdentifier := "" @@ -54,8 +55,10 @@ func BuildImplementationRust(component ComponentDefinition, outputFolder string, stubIdentifier = "_" + strings.ToLower(implementation.StubIdentifier) } - IntfFileName := BaseName + "_interfaces.rs" + InterfaceMod := BaseName + "_interfaces" + IntfFileName := InterfaceMod + ".rs" IntfFilePath := path.Join(outputFolder, IntfFileName) + modfiles = append(modfiles, IntfFilePath) log.Printf("Creating \"%s\"", IntfFilePath) IntfRSFile, err := CreateLanguageFile(IntfFilePath, indentString) if err != nil { @@ -68,12 +71,13 @@ func BuildImplementationRust(component ComponentDefinition, outputFolder string, if err != nil { return err } - err = buildRustInterfaces(component, IntfRSFile, implementation.ClassIdentifier) + err = buildRustInterfaces(component, IntfRSFile) if err != nil { return err } IntfWrapperStubName := path.Join(stubOutputFolder, BaseName+stubIdentifier+".rs") + modfiles = append(modfiles, IntfWrapperStubName) if forceRebuild || !FileExists(IntfWrapperStubName) { log.Printf("Creating \"%s\"", IntfWrapperStubName) stubfile, err := CreateLanguageFile(IntfWrapperStubName, indentString) @@ -83,11 +87,10 @@ func BuildImplementationRust(component ComponentDefinition, outputFolder string, stubfile.WriteCLicenseHeader(component, fmt.Sprintf("This is an autogenerated Rust implementation file in order to allow easy\ndevelopment of %s. It needs to be generated only once.", LibraryName), true) - RelInterfaceFile, err := filepath.Rel(stubOutputFolder, IntfFilePath) if err != nil { return err } - err = buildRustGlobalStubFile(component, stubfile, implementation.ClassIdentifier, RelInterfaceFile) + err = buildRustGlobalStubFile(component, stubfile, InterfaceMod) if err != nil { return err } @@ -95,7 +98,51 @@ func BuildImplementationRust(component ComponentDefinition, outputFolder string, log.Printf("Omitting recreation of implementation stub \"%s\"", IntfWrapperStubName) } + for i := 0; i < len(component.Classes); i++ { + class := component.Classes[i] + StubBase := BaseName + stubIdentifier + StubClassName := path.Join(stubOutputFolder, StubBase+"_"+toSnakeCase(class.ClassName)+".rs") + modfiles = append(modfiles, StubClassName) + if forceRebuild || !FileExists(StubClassName) { + log.Printf("Creating \"%s\"", StubClassName) + stubfile, err := CreateLanguageFile(StubClassName, indentString) + if err != nil { + return err + } + stubfile.WriteCLicenseHeader(component, + fmt.Sprintf("This is an autogenerated Rust implementation file in order to allow easy\ndevelopment of %s. It needs to be generated only once.", LibraryName), + true) + if err != nil { + return err + } + err = buildRustStubFile(component, class, stubfile, InterfaceMod, StubBase) + if err != nil { + return err + } + } else { + log.Printf("Omitting recreation of implementation stub \"%s\"", StubClassName) + } + } + if len(projectOutputFolder) > 0 { + IntfWrapperLibName := path.Join(projectOutputFolder, "lib.rs") + if forceRebuild || !FileExists(IntfWrapperLibName) { + log.Printf("Creating \"%s\"", IntfWrapperLibName) + libfile, err := CreateLanguageFile(IntfWrapperLibName, indentString) + if err != nil { + return err + } + libfile.WriteCLicenseHeader(component, + fmt.Sprintf("This is an autogenerated Rust implementation file in order to allow easy\ndevelopment of %s. It needs to be generated only once.", LibraryName), + true) + err = buildRustGlobalLibFile(component, libfile, projectOutputFolder, modfiles) + if err != nil { + return err + } + } else { + log.Printf("Omitting recreation of lib \"%s\"", IntfWrapperLibName) + } + CargoFileName := path.Join(projectOutputFolder, "Cargo.toml") if forceRebuild || !FileExists(CargoFileName) { log.Printf("Creating Cargo file \"%s\" for Rust Implementation", CargoFileName) @@ -106,7 +153,7 @@ func BuildImplementationRust(component ComponentDefinition, outputFolder string, CargoFile.WriteTomlLicenseHeader(component, fmt.Sprintf("This is an autogenerated Cargo file for the development of %s.", LibraryName), true) - LibPath, err := filepath.Rel(projectOutputFolder, IntfWrapperStubName) + LibPath, err := filepath.Rel(projectOutputFolder, IntfWrapperLibName) if err != nil { return err } @@ -119,16 +166,24 @@ func BuildImplementationRust(component ComponentDefinition, outputFolder string, return nil } -func buildRustGlobalStubFile(component ComponentDefinition, w LanguageWriter, ClassIdentifier string, RelInterfaceFile string) error { +func buildRustGlobalLibFile(component ComponentDefinition, w LanguageWriter, basedir string, modfiles []string) error { w.Writeln("") // Get all modules - w.Writeln("#[path = \"%s\"]", strings.ReplaceAll(RelInterfaceFile, "\\", "/")) - IntfName := strings.TrimSuffix(filepath.Base(RelInterfaceFile), ".rs") - w.Writeln("mod %s;", IntfName) + for i := 0; i < len(modfiles); i++ { + modfile := modfiles[i] + relfile, err := filepath.Rel(basedir, modfile) + if err != nil { + return err + } + w.Writeln("#[path = \"%s\"]", strings.ReplaceAll(relfile, "\\", "/")) + IntfName := strings.TrimSuffix(filepath.Base(relfile), ".rs") + w.Writeln("mod %s;", IntfName) + w.Writeln("") + } return nil } -func buildRustInterfaces(component ComponentDefinition, w LanguageWriter, ClassIdentifier string) error { +func buildRustInterfaces(component ComponentDefinition, w LanguageWriter) error { NameSpace := component.NameSpace w.Writeln("/*************************************************************************************************************************") w.Writeln(" Traits defined for %s", NameSpace) @@ -178,7 +233,7 @@ func writeRustTrait(component ComponentDefinition, class ComponentDefinitionClas parentClassString = fmt.Sprintf(": %s ", class.ParentClass) } } - w.Writeln("trait %s %s {", class.ClassName, parentClassString) + w.Writeln("pub trait %s %s {", class.ClassName, parentClassString) w.AddIndentationLevel(1) methods := class.Methods if component.isBaseClass(class) { @@ -194,7 +249,7 @@ func writeRustTrait(component ComponentDefinition, class ComponentDefinitionClas for j := 0; j < len(methods); j++ { method := methods[j] w.Writeln("") - err := writeRustTraitFn(method, w, true) + err := writeRustTraitFn(method, w, true, false, false) if err != nil { return err } @@ -206,12 +261,13 @@ func writeRustTrait(component ComponentDefinition, class ComponentDefinitionClas return nil } -func writeRustTraitFn(method ComponentDefinitionMethod, w LanguageWriter, hasSelf bool) error { +func writeRustTraitFn(method ComponentDefinitionMethod, w LanguageWriter, hasSelf bool, hasImpl bool, hasImplParent bool) error { methodName := toSnakeCase(method.MethodName) w.Writeln("// %s", methodName) w.Writeln("//") w.Writeln("// %s", method.MethodDescription) parameterString := "" + parameterNames := "" if hasSelf { parameterString += "&mut self" } @@ -229,16 +285,37 @@ func writeRustTraitFn(method ComponentDefinitionMethod, w LanguageWriter, hasSel } else { parameterString += fmt.Sprintf(", %s : %s", RustParam.ParamName, RustParam.ParamType) } + if parameterNames == "" { + parameterNames += RustParam.ParamName + } else { + parameterNames += fmt.Sprintf(", %s", RustParam.ParamName) + } } else { returnType = RustParam.ParamType } w.Writeln("// %s", RustParam.ParamComment) } w.Writeln("//") - if returnType == "" { - w.Writeln("fn %s(%s);", methodName, parameterString) + if !hasImpl { + if returnType == "" { + w.Writeln("fn %s(%s);", methodName, parameterString) + } else { + w.Writeln("fn %s(%s) -> %s;", methodName, parameterString, returnType) + } } else { - w.Writeln("fn %s(%s) -> %s;", methodName, parameterString, returnType) + if returnType == "" { + w.Writeln("fn %s(%s) {", methodName, parameterString) + } else { + w.Writeln("fn %s(%s) -> %s {", methodName, parameterString, returnType) + } + w.AddIndentationLevel(1) + if !hasImplParent { + w.Writeln("unimplemented!();") + } else { + w.Writeln("self.parent.%s(%s)", methodName, parameterNames) + } + w.AddIndentationLevel(-1) + w.Writeln("}") } return nil } @@ -246,18 +323,154 @@ func writeRustTraitFn(method ComponentDefinitionMethod, w LanguageWriter, hasSel func writeRustGlobalTrait(component ComponentDefinition, w LanguageWriter) error { w.Writeln("// Wrapper trait for global methods") w.Writeln("//") - w.Writeln("trait Wrapper {") + w.Writeln("pub trait Wrapper {") + w.AddIndentationLevel(1) + methods := component.Global.Methods + for j := 0; j < len(methods); j++ { + method := methods[j] + w.Writeln("") + err := writeRustTraitFn(method, w, false, false, false) + if err != nil { + return err + } + } + w.ResetIndentationLevel() + w.Writeln("}") + return nil +} + +func buildRustGlobalStubFile(component ComponentDefinition, w LanguageWriter, InterfaceMod string) error { + w.Writeln("") + w.Writeln("use %s::*;", InterfaceMod) + w.Writeln("") + w.Writeln("// Wrapper struct to implement the wrapper trait for global methods") + w.Writeln("struct CWrapper;") + w.Writeln("") + w.Writeln("impl Wrapper for CWrapper {") + w.Writeln("") w.AddIndentationLevel(1) methods := component.Global.Methods for j := 0; j < len(methods); j++ { method := methods[j] w.Writeln("") - err := writeRustTraitFn(method, w, false) + err := writeRustTraitFn(method, w, false, true, false) if err != nil { return err } } w.ResetIndentationLevel() w.Writeln("}") + w.Writeln("") + return nil +} + +func getParentList(component ComponentDefinition, class ComponentDefinitionClass) ([]string, error) { + parents := make([]string, 0) + currClass := class + for !component.isBaseClass(currClass) { + parent := currClass.ParentClass + if parent == "" { + parent = component.baseClass().ClassName + } + parents = append(parents, parent) + parClass, err := getClass(component, parent) + if err != nil { + return parents, err + } + currClass = parClass + } + return parents, nil +} + +func getClass(component ComponentDefinition, name string) (ComponentDefinitionClass, error) { + for i := 0; i < len(component.Classes); i++ { + class := component.Classes[i] + if class.ClassName == name { + return class, nil + } + } + return component.baseClass(), fmt.Errorf("Cannot find class %s", name) +} + +func buildRustStubFile(component ComponentDefinition, class ComponentDefinitionClass, w LanguageWriter, InterfaceMod string, StubBase string) error { + Name := class.ClassName + parents, err := getParentList(component, class) + if err != nil { + return err + } + w.Writeln("") + w.Writeln("use %s::*;", InterfaceMod) + if len(parents) > 0 { + parentName := parents[0] + w.Writeln("use %s_%s::C%s;", StubBase, toSnakeCase(parentName), parentName) + } + w.Writeln("") + w.Writeln("// Stub struct to implement the %s trait", Name) + if len(parents) == 0 { + w.Writeln("pub struct C%s;", Name) + } else { + w.Writeln("pub struct C%s {", Name) + w.AddIndentationLevel(1) + w.Writeln("parent : C%s", parents[0]) + w.ResetIndentationLevel() + w.Writeln("}") + w.Writeln("") + w.Writeln("// Implementation of parent traits via parent") + w.Writeln("") + for i := 0; i < len(parents); i++ { + parent := parents[i] + parentClass, err := getClass(component, parent) + if err != nil { + return err + } + w.Writeln("impl %s for C%s {", parent, Name) + w.AddIndentationLevel(1) + methods := parentClass.Methods + if component.isBaseClass(parentClass) { + methods = append( + methods, + GetLastErrorMessageMethod(), + ClearErrorMessageMethod(), + RegisterErrorMessageMethod(), + IncRefCountMethod(), + DecRefCountMethod()) + } + for j := 0; j < len(methods); j++ { + method := methods[j] + w.Writeln("") + err := writeRustTraitFn(method, w, true, true, true) + if err != nil { + return err + } + } + w.ResetIndentationLevel() + w.Writeln("}") + } + } + w.Writeln("") + w.Writeln("impl %s for C%s {", Name, Name) + w.Writeln("") + w.AddIndentationLevel(1) + methods := class.Methods + if component.isBaseClass(class) { + methods = append( + methods, + GetLastErrorMessageMethod(), + ClearErrorMessageMethod(), + RegisterErrorMessageMethod(), + IncRefCountMethod(), + DecRefCountMethod()) + } + for j := 0; j < len(methods); j++ { + method := methods[j] + w.Writeln("") + err := writeRustTraitFn(method, w, true, true, false) + if err != nil { + return err + } + } + w.ResetIndentationLevel() + w.Writeln("}") + w.Writeln("") return nil } diff --git a/Source/languagerust.go b/Source/languagerust.go index 29a037c4..6d9a0303 100644 --- a/Source/languagerust.go +++ b/Source/languagerust.go @@ -140,7 +140,7 @@ func writeRustBaseTypeDefinitions(componentdefinition ComponentDefinition, w Lan } w.Writeln("//") funcName := funcinfo.FunctionName - w.Writeln("type %s = unsafe extern \"C\" fn(%s);", funcName, parameterString) + w.Writeln("pub type %s = unsafe extern \"C\" fn(%s);", funcName, parameterString) } } From fc311f7763337cda28259e3bdba96e73a2f828be Mon Sep 17 00:00:00 2001 From: Robert Goss Date: Mon, 25 Sep 2023 22:48:47 +0100 Subject: [PATCH 13/26] Basic conversion of parameters for global wrappers --- Source/buildimplementationrust.go | 193 +++++++++++++++++++++++++++++- Source/languagerust.go | 100 +++++++++++----- 2 files changed, 264 insertions(+), 29 deletions(-) diff --git a/Source/buildimplementationrust.go b/Source/buildimplementationrust.go index 412a64b1..dbe055f0 100644 --- a/Source/buildimplementationrust.go +++ b/Source/buildimplementationrust.go @@ -76,6 +76,38 @@ func BuildImplementationRust(component ComponentDefinition, outputFolder string, return err } + IntfWrapperFileName := BaseName + "_interface_wrapper.rs" + IntfWrapperFilePath := path.Join(outputFolder, IntfWrapperFileName) + modfiles = append(modfiles, IntfWrapperFilePath) + log.Printf("Creating \"%s\"", IntfWrapperFilePath) + IntfWrapperRSFile, err := CreateLanguageFile(IntfWrapperFilePath, indentString) + if err != nil { + return err + } + IntfWrapperRSFile.WriteCLicenseHeader(component, + fmt.Sprintf("This is an autogenerated Rust implementation file in order to allow easy\ndevelopment of %s. The functions in this file need to be implemented. It needs to be generated only once.", LibraryName), + true) + err = buildRustWrapper(component, IntfWrapperRSFile, InterfaceMod) + if err != nil { + return err + } + + IntfHandleFileName := BaseName + "_interface_handle.rs" + IntfHandleFilePath := path.Join(outputFolder, IntfHandleFileName) + modfiles = append(modfiles, IntfHandleFilePath) + log.Printf("Creating \"%s\"", IntfHandleFilePath) + IntfHandleRSFile, err := CreateLanguageFile(IntfHandleFilePath, indentString) + if err != nil { + return err + } + IntfHandleRSFile.WriteCLicenseHeader(component, + fmt.Sprintf("This is an autogenerated Rust implementation file in order to allow easy\ndevelopment of %s. The functions in this file need to be implemented. It needs to be generated only once.", LibraryName), + true) + err = buildRustHandle(component, IntfHandleRSFile, InterfaceMod) + if err != nil { + return err + } + IntfWrapperStubName := path.Join(stubOutputFolder, BaseName+stubIdentifier+".rs") modfiles = append(modfiles, IntfWrapperStubName) if forceRebuild || !FileExists(IntfWrapperStubName) { @@ -344,7 +376,7 @@ func buildRustGlobalStubFile(component ComponentDefinition, w LanguageWriter, In w.Writeln("use %s::*;", InterfaceMod) w.Writeln("") w.Writeln("// Wrapper struct to implement the wrapper trait for global methods") - w.Writeln("struct CWrapper;") + w.Writeln("pub struct CWrapper;") w.Writeln("") w.Writeln("impl Wrapper for CWrapper {") w.Writeln("") @@ -474,3 +506,162 @@ func buildRustStubFile(component ComponentDefinition, class ComponentDefinitionC w.Writeln("") return nil } + +func buildRustWrapper(component ComponentDefinition, w LanguageWriter, InterfaceMod string) error { + // Imports + ModName := strings.ToLower(component.NameSpace) + w.Writeln("") + w.Writeln("// Calls from the C-Interface to the Rust traits via the CWrapper") + w.Writeln("// These are the symbols exposed in the shared object interface") + w.Writeln("") + w.Writeln("use %s::*;", InterfaceMod) + w.Writeln("use %s::CWrapper;", ModName) + w.Writeln("use std::ffi::{c_char, CStr};") + w.Writeln("") + cprefix := ModName + "_" + // Build the global methods + err := writeGlobalRustWrapper(component, w, cprefix) + if err != nil { + return err + } + return nil +} + +func buildRustHandle(component ComponentDefinition, w LanguageWriter, InterfaceMod string) error { + w.Writeln("") + w.Writeln("// Handle passed through interface define the casting maps needed to extract") + w.Writeln("") + w.Writeln("use %s::*;", InterfaceMod) + w.Writeln("") + w.Writeln("impl HandleImpl {") + w.AddIndentationLevel(1) + for i := 0; i < len(component.Classes); i++ { + class := component.Classes[i] + writeRustHandleAs(component, w, class, false) + writeRustHandleAs(component, w, class, true) + w.Writeln("") + } + w.AddIndentationLevel(-1) + w.Writeln("}") + return nil +} + +func writeRustHandleAs(component ComponentDefinition, w LanguageWriter, class ComponentDefinitionClass, mut bool) error { + //parents, err := getParentList(component, class) + //if err != nil { + // return err + //} + Name := class.ClassName + if !mut { + w.Writeln("pub fn as_%s(&self) -> Option<&dyn %s> {", toSnakeCase(Name), Name) + } else { + w.Writeln("pub fn as_mut_%s(&mut self) -> Option<&mut dyn %s> {", toSnakeCase(Name), Name) + } + w.AddIndentationLevel(1) + w.Writeln("None") + w.AddIndentationLevel(-1) + w.Writeln("}") + return nil +} + +func writeGlobalRustWrapper(component ComponentDefinition, w LanguageWriter, cprefix string) error { + methods := component.Global.Methods + for i := 0; i < len(methods); i++ { + method := methods[i] + err := writeRustMethodWrapper(method, w, cprefix) + if err != nil { + return err + } + w.Writeln("") + } + return nil +} + +func writeRustMethodWrapper(method ComponentDefinitionMethod, w LanguageWriter, cprefix string) error { + // Build up the parameter strings + parameterString := "" + returnName := "" + for k := 0; k < len(method.Params); k++ { + param := method.Params[k] + RustParams, err := generateRustParameters(param, true) + if err != nil { + return err + } + for i := 0; i < len(RustParams); i++ { + RustParam := RustParams[i] + if parameterString == "" { + parameterString += fmt.Sprintf("%s : %s", RustParam.ParamName, RustParam.ParamType) + } else { + parameterString += fmt.Sprintf(", %s : %s", RustParam.ParamName, RustParam.ParamType) + } + } + } + w.Writeln("pub fn %s%s(%s) -> i32 {", cprefix, strings.ToLower(method.MethodName), parameterString) + w.AddIndentationLevel(1) + argsString := "" + for k := 0; k < len(method.Params); k++ { + param := method.Params[k] + OName, err := writeRustParameterConversionArg(param, w) + if err != nil { + return err + } + if OName != "" { + if argsString == "" { + argsString = OName + } else { + argsString += fmt.Sprintf(", %s", OName) + } + } + } + if returnName != "" { + w.Writeln("let %s = CWrapper::%s(%s);", returnName, toSnakeCase(method.MethodName), argsString) + } else { + w.Writeln("CWrapper::%s(%s);", toSnakeCase(method.MethodName), argsString) + } + w.Writeln("// All ok") + w.Writeln("0") + w.AddIndentationLevel(-1) + w.Writeln("}") + return nil +} + +func writeRustParameterConversionArg(param ComponentDefinitionParam, w LanguageWriter) (string, error) { + if param.ParamPass == "return" { + return "", nil + } + IName := toSnakeCase(param.ParamName) + OName := "_" + IName + switch param.ParamType { + case "uint8", "uint16", "uint32", "uint64", "int8", "int16", "int32", "int64", "single", "double": + if param.ParamPass == "in" { + w.Writeln("let %s = %s;", OName, IName) + } else { + w.Writeln("let %s = unsafe {&mut *%s};", OName, IName) + } + case "class", "optionalclass": + if param.ParamPass == "in" { + HName := "_Handle_" + IName + w.Writeln("let %s = unsafe {&*%s};", HName, IName) + w.Writeln("let %s = %s.as_%s().unwrap();", OName, HName, toSnakeCase(param.ParamClass)) + } else { + HName := "_Handle_" + IName + w.Writeln("let %s = unsafe {&mut *%s};", HName, IName) + w.Writeln("let %s = %s.as_mut_%s().unwrap();", OName, HName, toSnakeCase(param.ParamClass)) + } + case "string": + if param.ParamPass == "in" { + SName := "_Str_" + IName + w.Writeln("let %s = unsafe{ CStr::from_ptr(%s) };", SName, IName) + w.Writeln("let %s = %s.to_str().unwrap();", OName, SName) + } else { + SName := "_String_" + IName + w.Writeln("let mut %s = String::new();", SName) + w.Writeln("let %s = &mut %s;", OName, SName) + } + case "bool", "pointer", "struct", "basicarray", "structarray": + //return fmt.Errorf("Conversion of type %s for parameter %s not supported", param.ParamType, IName) + default: + return "", fmt.Errorf("Conversion of type %s for parameter %s not supported as is unknown", param.ParamType, IName) + } + return OName, nil +} diff --git a/Source/languagerust.go b/Source/languagerust.go index 6d9a0303..d4a5ef86 100644 --- a/Source/languagerust.go +++ b/Source/languagerust.go @@ -47,7 +47,7 @@ func toSnakeCase(BaseType string) string { func writeRustBaseTypeDefinitions(componentdefinition ComponentDefinition, w LanguageWriter, NameSpace string, BaseName string) error { w.Writeln("#[allow(unused_imports)]") - w.Writeln("use std::ffi;") + w.Writeln("use std::ffi::c_void;") w.Writeln("") w.Writeln("/*************************************************************************************************************************") w.Writeln(" Version definition for %s", NameSpace) @@ -63,10 +63,28 @@ func writeRustBaseTypeDefinitions(componentdefinition ComponentDefinition, w Lan w.Writeln("") w.Writeln("/*************************************************************************************************************************") - w.Writeln(" Basic pointers definition for %s", NameSpace) + w.Writeln(" Handle definiton for %s", NameSpace) w.Writeln("**************************************************************************************************************************/") w.Writeln("") - w.Writeln("type Handle = std::ffi::c_void;") + w.Writeln("// Enum of all traits - this acts as a handle as we pass trait pointers through the interface") + w.Writeln("pub enum HandleImpl {") + w.AddIndentationLevel(1) + for i := 0; i < len(componentdefinition.Classes); i++ { + class := componentdefinition.Classes[i] + if i != len(componentdefinition.Classes)-1 { + w.Writeln("T%s(Box),", class.ClassName, class.ClassName) + } else { + w.Writeln("T%s(Box)", class.ClassName, class.ClassName) + } + } + w.AddIndentationLevel(-1) + w.Writeln("}") + w.Writeln("") + w.Writeln("pub type Handle = *mut HandleImpl;") + for i := 0; i < len(componentdefinition.Classes); i++ { + class := componentdefinition.Classes[i] + w.Writeln("pub type %sHandle = *mut HandleImpl;", class.ClassName) + } if len(componentdefinition.Enums) > 0 { w.Writeln("/*************************************************************************************************************************") @@ -211,6 +229,25 @@ func generateRustParameters(param ComponentDefinitionParam, isPlain bool) ([]Rus } if isPlain { + if param.ParamType == "string" { + if param.ParamPass == "out" { + Params = make([]RustParameter, 3) + Params[0].ParamType = "u64" + Params[0].ParamName = toSnakeCase(param.ParamName) + "_buffer_size" + Params[0].ParamComment = fmt.Sprintf("* @param[in] %s - size of the buffer (including trailing 0)", Params[0].ParamName) + + Params[1].ParamType = "*mut u64" + Params[1].ParamName = toSnakeCase(param.ParamName) + "_needed_chars" + Params[1].ParamComment = fmt.Sprintf("* @param[out] %s - will be filled with the count of the written bytes, or needed buffer size.", Params[1].ParamName) + + Params[2].ParamType = "*mut c_char" + Params[2].ParamName = toSnakeCase(param.ParamName) + "_buffer" + Params[2].ParamComment = fmt.Sprintf("* @param[out] %s - %s buffer of %s, may be NULL", Params[2].ParamName, param.ParamClass, param.ParamDescription) + + return Params, nil + } + } + if param.ParamType == "basicarray" { return nil, fmt.Errorf("Not yet handled") } @@ -231,50 +268,51 @@ func generateRustParameterType(param ComponentDefinitionParam, isPlain bool) (st RustParamTypeName := "" ParamTypeName := param.ParamType ParamClass := param.ParamClass + BasicType := false switch ParamTypeName { case "uint8": RustParamTypeName = "u8" - + BasicType = true case "uint16": RustParamTypeName = "u16" - + BasicType = true case "uint32": RustParamTypeName = "u32" - + BasicType = true case "uint64": RustParamTypeName = "u64" - + BasicType = true case "int8": RustParamTypeName = "i8" - + BasicType = true case "int16": RustParamTypeName = "i16" - + BasicType = true case "int32": RustParamTypeName = "i32" - + BasicType = true case "int64": RustParamTypeName = "i64" - + BasicType = true case "bool": if isPlain { RustParamTypeName = "u8" } else { RustParamTypeName = "bool" } - + BasicType = true case "single": RustParamTypeName = "f32" - + BasicType = true case "double": RustParamTypeName = "f64" - + BasicType = true case "pointer": RustParamTypeName = "c_void" - + BasicType = true case "string": if isPlain { - RustParamTypeName = "*mut char" + RustParamTypeName = "*const c_char" } else { switch param.ParamPass { case "out": @@ -290,17 +328,12 @@ func generateRustParameterType(param ComponentDefinitionParam, isPlain bool) (st if isPlain { RustParamTypeName = fmt.Sprintf("u16") } else { - switch param.ParamPass { - case "out": - RustParamTypeName = fmt.Sprintf("&mut %s", ParamClass) - case "in", "return": - RustParamTypeName = fmt.Sprintf("%s", ParamClass) - } + RustParamTypeName = ParamClass } - + BasicType = true case "functiontype": RustParamTypeName = fmt.Sprintf("%s", ParamClass) - + BasicType = true case "struct": if isPlain { RustParamTypeName = fmt.Sprintf("*mut %s", ParamClass) @@ -353,13 +386,14 @@ func generateRustParameterType(param ComponentDefinitionParam, isPlain bool) (st case "class", "optionalclass": if isPlain { - RustParamTypeName = fmt.Sprintf("Handle") + RustParamTypeName = fmt.Sprintf("%sHandle", ParamClass) + BasicType = true } else { switch param.ParamPass { case "out": - RustParamTypeName = fmt.Sprintf("&mut impl %s", ParamClass) + RustParamTypeName = fmt.Sprintf("&mut dyn %s", ParamClass) case "in": - RustParamTypeName = fmt.Sprintf("& impl %s", ParamClass) + RustParamTypeName = fmt.Sprintf("& dyn %s", ParamClass) case "return": RustParamTypeName = fmt.Sprintf("Box", ParamClass) } @@ -368,6 +402,16 @@ func generateRustParameterType(param ComponentDefinitionParam, isPlain bool) (st default: return "", fmt.Errorf("invalid parameter type \"%s\" for Rust parameter", ParamTypeName) } - + if BasicType { + if param.ParamPass == "out" { + if isPlain { + RustParamOutTypeName := fmt.Sprintf("*mut %s", RustParamTypeName) + return RustParamOutTypeName, nil + } else { + RustParamOutTypeName := fmt.Sprintf("&mut %s", RustParamTypeName) + return RustParamOutTypeName, nil + } + } + } return RustParamTypeName, nil } From 09fe8bddedeb7d8ba5a5a5aa709be39106ce236e Mon Sep 17 00:00:00 2001 From: Robert Goss Date: Tue, 26 Sep 2023 00:39:55 +0100 Subject: [PATCH 14/26] Fix merge issues --- Build/build.bat | 2 +- Examples/Primes/libPrimes.xml | 6 +++++- Source/languagewriter.go | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Build/build.bat b/Build/build.bat index 0dd1d6e8..4ee2cdb6 100644 --- a/Build/build.bat +++ b/Build/build.bat @@ -3,7 +3,7 @@ set startingDir="%CD%" set basepath="%~dp0" cd %basepath%\..\Source -set Sources=actutils.go automaticcomponenttoolkit.go buildbindingccpp.go buildbindingcsharp.go buildbindinggo.go buildbindingnode.go buildbindingpascal.go buildbindingpython.go buildimplementationcpp.go buildimplementationpascal.go buildimplementationrust.go componentdefinition.go componentdiff.go languagewriter.go languagec.go languagecpp.go languagepascal.go languagerust.go +set Sources=actutils.go automaticcomponenttoolkit.go buildbindingccpp.go buildbindingccppdocumentation.go buildbindingcsharp.go buildbindinggo.go buildbindingjava.go buildbindingnode.go buildbindingpascal.go buildbindingpython.go buildimplementationcpp.go buildimplementationpascal.go buildimplementationrust.go componentdefinition.go componentdiff.go languagewriter.go languagec.go languagecpp.go languagepascal.go languagerust.go set GOARCH=amd64 echo "Build act.win64.exe" go build -o ..\act.win64.exe %Sources% diff --git a/Examples/Primes/libPrimes.xml b/Examples/Primes/libPrimes.xml index 4fa25d33..1d7ec1fb 100644 --- a/Examples/Primes/libPrimes.xml +++ b/Examples/Primes/libPrimes.xml @@ -46,6 +46,9 @@ + + + @@ -75,7 +78,8 @@ + releasemethod="ReleaseInstance" versionmethod="GetVersion" errormethod="GetLastError" journalmethod="SetJournal" + classtypeidmethod="ClassTypeId"> diff --git a/Source/languagewriter.go b/Source/languagewriter.go index 1e47261c..5777ed7e 100644 --- a/Source/languagewriter.go +++ b/Source/languagewriter.go @@ -137,8 +137,8 @@ func (writer *LanguageWriter) WritePythonLicenseHeader(component ComponentDefini } // WriteJavaLicenseHeader writes a license header into a writer Java-style comments -func (writer *LanguageWriter) WriteJavaLicenseHeader (component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx (writer.Writer, component, abstract, includeVersion, "/*", "*/"); +func (writer *LanguageWriter) WriteJavaLicenseHeader(component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx(writer.Writer, component, abstract, includeVersion, "/*", "*/", "") } // WritePlainLicenseHeader writes a license header into a writer without comments From 8d2bb3c93c578f65d86e61dc2a6e71e509502bbc Mon Sep 17 00:00:00 2001 From: Robert Goss Date: Tue, 26 Sep 2023 01:12:35 +0100 Subject: [PATCH 15/26] Add the coercion for the handler type --- Source/buildimplementationrust.go | 34 ++++++++++++++++++++++++++----- run.bat | 2 +- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/Source/buildimplementationrust.go b/Source/buildimplementationrust.go index dbe055f0..443d0617 100644 --- a/Source/buildimplementationrust.go +++ b/Source/buildimplementationrust.go @@ -199,6 +199,9 @@ func BuildImplementationRust(component ComponentDefinition, outputFolder string, } func buildRustGlobalLibFile(component ComponentDefinition, w LanguageWriter, basedir string, modfiles []string) error { + w.Writeln("") + w.Writeln("#![feature(trait_upcasting)]") + w.Writeln("#![allow(incomplete_features)]") w.Writeln("") // Get all modules for i := 0; i < len(modfiles); i++ { @@ -414,6 +417,18 @@ func getParentList(component ComponentDefinition, class ComponentDefinitionClass return parents, nil } +func getChildList(component ComponentDefinition, class ComponentDefinitionClass) []string { + children := make([]string, 0) + for i := 0; i < len(component.Classes); i++ { + child := component.Classes[i] + if child.ParentClass == class.ClassName { + children = append(children, child.ClassName) + children = append(children, getChildList(component, child)...) + } + } + return children +} + func getClass(component ComponentDefinition, name string) (ComponentDefinitionClass, error) { for i := 0; i < len(component.Classes); i++ { class := component.Classes[i] @@ -547,10 +562,7 @@ func buildRustHandle(component ComponentDefinition, w LanguageWriter, InterfaceM } func writeRustHandleAs(component ComponentDefinition, w LanguageWriter, class ComponentDefinitionClass, mut bool) error { - //parents, err := getParentList(component, class) - //if err != nil { - // return err - //} + children := getChildList(component, class) Name := class.ClassName if !mut { w.Writeln("pub fn as_%s(&self) -> Option<&dyn %s> {", toSnakeCase(Name), Name) @@ -558,7 +570,19 @@ func writeRustHandleAs(component ComponentDefinition, w LanguageWriter, class Co w.Writeln("pub fn as_mut_%s(&mut self) -> Option<&mut dyn %s> {", toSnakeCase(Name), Name) } w.AddIndentationLevel(1) - w.Writeln("None") + w.Writeln("match self {") + w.AddIndentationLevel(1) + for i := 0; i < len(children); i++ { + child := children[i] + if !mut { + w.Writeln("HandleImpl::T%s(ptr) => Some(ptr.as_ref()),", child) + } else { + w.Writeln("HandleImpl::T%s(ptr) => Some(ptr.as_mut()),", child) + } + } + w.Writeln("_ => None") + w.AddIndentationLevel(-1) + w.Writeln("}") w.AddIndentationLevel(-1) w.Writeln("}") return nil diff --git a/run.bat b/run.bat index 04cc5fdd..02babfa0 100644 --- a/run.bat +++ b/run.bat @@ -1,4 +1,4 @@ CALL Build/build.bat act.exe tmp/libPrimes.xml -o tmp cd tmp/libPrimes_component/Implementations/Rust -cargo b \ No newline at end of file +cargo +nightly b \ No newline at end of file From 23cee1c2497658817a08228e03d488066d53ebc0 Mon Sep 17 00:00:00 2001 From: Robert Goss Date: Tue, 26 Sep 2023 02:19:57 +0100 Subject: [PATCH 16/26] Check errors, get string out --- Source/buildimplementationrust.go | 81 ++++++++++++++++++++++++++----- Source/languagerust.go | 34 ++++++++++--- 2 files changed, 95 insertions(+), 20 deletions(-) diff --git a/Source/buildimplementationrust.go b/Source/buildimplementationrust.go index 443d0617..fd7b1334 100644 --- a/Source/buildimplementationrust.go +++ b/Source/buildimplementationrust.go @@ -202,6 +202,7 @@ func buildRustGlobalLibFile(component ComponentDefinition, w LanguageWriter, bas w.Writeln("") w.Writeln("#![feature(trait_upcasting)]") w.Writeln("#![allow(incomplete_features)]") + w.Writeln("#![feature(vec_into_raw_parts)]") w.Writeln("") // Get all modules for i := 0; i < len(modfiles); i++ { @@ -589,10 +590,11 @@ func writeRustHandleAs(component ComponentDefinition, w LanguageWriter, class Co } func writeGlobalRustWrapper(component ComponentDefinition, w LanguageWriter, cprefix string) error { + errorprefix := strings.ToUpper(component.NameSpace) methods := component.Global.Methods for i := 0; i < len(methods); i++ { method := methods[i] - err := writeRustMethodWrapper(method, w, cprefix) + err := writeRustMethodWrapper(method, w, cprefix, errorprefix) if err != nil { return err } @@ -601,7 +603,7 @@ func writeGlobalRustWrapper(component ComponentDefinition, w LanguageWriter, cpr return nil } -func writeRustMethodWrapper(method ComponentDefinitionMethod, w LanguageWriter, cprefix string) error { +func writeRustMethodWrapper(method ComponentDefinitionMethod, w LanguageWriter, cprefix string, errorprefix string) error { // Build up the parameter strings parameterString := "" returnName := "" @@ -625,7 +627,7 @@ func writeRustMethodWrapper(method ComponentDefinitionMethod, w LanguageWriter, argsString := "" for k := 0; k < len(method.Params); k++ { param := method.Params[k] - OName, err := writeRustParameterConversionArg(param, w) + OName, err := writeRustParameterConversionArg(param, w, errorprefix) if err != nil { return err } @@ -642,14 +644,21 @@ func writeRustMethodWrapper(method ComponentDefinitionMethod, w LanguageWriter, } else { w.Writeln("CWrapper::%s(%s);", toSnakeCase(method.MethodName), argsString) } + for k := 0; k < len(method.Params); k++ { + param := method.Params[k] + err := writeRustParameterConversionOutPost(param, w, errorprefix) + if err != nil { + return err + } + } w.Writeln("// All ok") - w.Writeln("0") + w.Writeln("%s_SUCCESS", errorprefix) w.AddIndentationLevel(-1) w.Writeln("}") return nil } -func writeRustParameterConversionArg(param ComponentDefinitionParam, w LanguageWriter) (string, error) { +func writeRustParameterConversionArg(param ComponentDefinitionParam, w LanguageWriter, errorprefix string) (string, error) { if param.ParamPass == "return" { return "", nil } @@ -660,25 +669,39 @@ func writeRustParameterConversionArg(param ComponentDefinitionParam, w LanguageW if param.ParamPass == "in" { w.Writeln("let %s = %s;", OName, IName) } else { + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", IName, errorprefix) w.Writeln("let %s = unsafe {&mut *%s};", OName, IName) } case "class", "optionalclass": if param.ParamPass == "in" { - HName := "_Handle_" + IName + HName := "_handle_" + IName + OpName := "_optional_" + IName + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", IName, errorprefix) w.Writeln("let %s = unsafe {&*%s};", HName, IName) - w.Writeln("let %s = %s.as_%s().unwrap();", OName, HName, toSnakeCase(param.ParamClass)) + w.Writeln("let %s = %s.as_%s();", OpName, HName, toSnakeCase(param.ParamClass)) + w.Writeln("if %s.is_none() { return %s_ERROR_INVALIDPARAM; }", OpName, errorprefix) + w.Writeln("let %s = %s.unwrap();", OName, OpName) + } else { - HName := "_Handle_" + IName + HName := "_handle_" + IName + OpName := "_optional_" + IName + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", IName, errorprefix) w.Writeln("let %s = unsafe {&mut *%s};", HName, IName) - w.Writeln("let %s = %s.as_mut_%s().unwrap();", OName, HName, toSnakeCase(param.ParamClass)) + w.Writeln("let %s = %s.as_mut_%s();", OpName, HName, toSnakeCase(param.ParamClass)) + w.Writeln("if %s.is_none() { return %s_ERROR_INVALIDPARAM; }", OpName, errorprefix) + w.Writeln("let %s = %s.unwrap();", OName, OpName) } case "string": if param.ParamPass == "in" { - SName := "_Str_" + IName + SName := "_str_" + IName + OpName := "_optional_" + IName + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", IName, errorprefix) w.Writeln("let %s = unsafe{ CStr::from_ptr(%s) };", SName, IName) - w.Writeln("let %s = %s.to_str().unwrap();", OName, SName) + w.Writeln("let %s = %s.to_str();", OpName, SName) + w.Writeln("if %s.is_err() { return %s_ERROR_INVALIDPARAM; }", OpName, errorprefix) + w.Writeln("let %s = %s.unwrap();", OName, OpName) } else { - SName := "_String_" + IName + SName := "_string_" + IName w.Writeln("let mut %s = String::new();", SName) w.Writeln("let %s = &mut %s;", OName, SName) } @@ -689,3 +712,37 @@ func writeRustParameterConversionArg(param ComponentDefinitionParam, w LanguageW } return OName, nil } + +func writeRustParameterConversionOutPost(param ComponentDefinitionParam, w LanguageWriter, errorprefix string) error { + if param.ParamPass != "out" { + return nil + } + // Any remaining bit needed to wire out variables + IName := toSnakeCase(param.ParamName) + switch param.ParamType { + case "uint8", "uint16", "uint32", "uint64", "int8", "int16", "int32", "int64", "single", "double", "class", "optionalclass": + return nil + case "string": + // Check the buffer size and if null + BuffSizeName := IName + "_buffer_size" + BuffName := IName + "_buffer" + BuffSLName := "_buffer_slice_" + IName + CNName := IName + "_needed_chars" + CNRName := "_" + CNName + SName := "_string_" + IName + SLName := "_slice_" + IName + w.Writeln("let %s = %s.as_bytes();", SLName, SName) + w.Writeln("if %s > %s.len() { return %s_ERROR_BUFFERTOOSMALL; }", BuffSizeName, SLName, errorprefix) + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", BuffName, errorprefix) + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", CNName, errorprefix) + w.Writeln("let mut %s = unsafe { std::slice::from_raw_parts_mut(%s, %s.len()) };", BuffSLName, BuffName, SLName) + w.Writeln("%s.clone_from_slice(%s);", BuffSLName, SLName) + w.Writeln("let mut %s = unsafe { &mut *%s };", CNRName, CNName) + w.Writeln("*%s = %s.len();", CNRName, SLName) + case "bool", "pointer", "struct", "basicarray", "structarray": + //return fmt.Errorf("Conversion of type %s for parameter %s not supported", param.ParamType, IName) + default: + return fmt.Errorf("Conversion of type %s for parameter %s not supported as is unknown", param.ParamType, IName) + } + return nil +} diff --git a/Source/languagerust.go b/Source/languagerust.go index d4a5ef86..aae198b9 100644 --- a/Source/languagerust.go +++ b/Source/languagerust.go @@ -53,15 +53,33 @@ func writeRustBaseTypeDefinitions(componentdefinition ComponentDefinition, w Lan w.Writeln(" Version definition for %s", NameSpace) w.Writeln("**************************************************************************************************************************/") w.Writeln("") - w.Writeln("const %s_VERSION_MAJOR : usize = %d;", strings.ToUpper(NameSpace), majorVersion(componentdefinition.Version)) - w.Writeln("const %s_VERSION_MINOR : usize = %d;", strings.ToUpper(NameSpace), minorVersion(componentdefinition.Version)) - w.Writeln("const %s_VERSION_MICRO : usize= %d;", strings.ToUpper(NameSpace), microVersion(componentdefinition.Version)) - w.Writeln("const %s_VERSION_PRERELEASEINFO : &str = \"%s\";", strings.ToUpper(NameSpace), preReleaseInfo(componentdefinition.Version)) - w.Writeln("const %s_VERSION_BUILDINFO : &str = \"%s\";", strings.ToUpper(NameSpace), buildInfo(componentdefinition.Version)) + w.Writeln("pub const %s_VERSION_MAJOR : usize = %d;", strings.ToUpper(NameSpace), majorVersion(componentdefinition.Version)) + w.Writeln("pub const %s_VERSION_MINOR : usize = %d;", strings.ToUpper(NameSpace), minorVersion(componentdefinition.Version)) + w.Writeln("pub const %s_VERSION_MICRO : usize= %d;", strings.ToUpper(NameSpace), microVersion(componentdefinition.Version)) + w.Writeln("pub const %s_VERSION_PRERELEASEINFO : &str = \"%s\";", strings.ToUpper(NameSpace), preReleaseInfo(componentdefinition.Version)) + w.Writeln("pub const %s_VERSION_BUILDINFO : &str = \"%s\";", strings.ToUpper(NameSpace), buildInfo(componentdefinition.Version)) w.Writeln("") w.Writeln("") + if len(componentdefinition.Errors.Errors) > 0 { + w.Writeln("/*************************************************************************************************************************") + w.Writeln(" Error constants for %s", NameSpace) + w.Writeln("**************************************************************************************************************************/") + w.Writeln("") + w.Writeln("pub const %s_SUCCESS : i32 = 0;", strings.ToUpper(NameSpace)) + for i := 0; i < len(componentdefinition.Errors.Errors); i++ { + errorcode := componentdefinition.Errors.Errors[i] + if errorcode.Description != "" { + w.Writeln("pub const %s_ERROR_%s : i32 = %d; /** %s */", strings.ToUpper(NameSpace), errorcode.Name, errorcode.Code, errorcode.Description) + } else { + w.Writeln("pub const %s_ERROR_%s : i32 = %d;", strings.ToUpper(NameSpace), errorcode.Name, errorcode.Code) + } + } + w.Writeln("") + w.Writeln("") + } + w.Writeln("/*************************************************************************************************************************") w.Writeln(" Handle definiton for %s", NameSpace) w.Writeln("**************************************************************************************************************************/") @@ -232,15 +250,15 @@ func generateRustParameters(param ComponentDefinitionParam, isPlain bool) ([]Rus if param.ParamType == "string" { if param.ParamPass == "out" { Params = make([]RustParameter, 3) - Params[0].ParamType = "u64" + Params[0].ParamType = "usize" Params[0].ParamName = toSnakeCase(param.ParamName) + "_buffer_size" Params[0].ParamComment = fmt.Sprintf("* @param[in] %s - size of the buffer (including trailing 0)", Params[0].ParamName) - Params[1].ParamType = "*mut u64" + Params[1].ParamType = "*mut usize" Params[1].ParamName = toSnakeCase(param.ParamName) + "_needed_chars" Params[1].ParamComment = fmt.Sprintf("* @param[out] %s - will be filled with the count of the written bytes, or needed buffer size.", Params[1].ParamName) - Params[2].ParamType = "*mut c_char" + Params[2].ParamType = "*mut u8" Params[2].ParamName = toSnakeCase(param.ParamName) + "_buffer" Params[2].ParamComment = fmt.Sprintf("* @param[out] %s - %s buffer of %s, may be NULL", Params[2].ParamName, param.ParamClass, param.ParamDescription) From 1e91177e00f2635c6f4c08c9c97d457515155ca8 Mon Sep 17 00:00:00 2001 From: Robert Goss Date: Tue, 26 Sep 2023 04:18:10 +0100 Subject: [PATCH 17/26] Handle return parameters --- Source/buildimplementationrust.go | 61 ++++++++++++++++++++++++++++++- Source/languagerust.go | 6 +++ 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/Source/buildimplementationrust.go b/Source/buildimplementationrust.go index fd7b1334..79326619 100644 --- a/Source/buildimplementationrust.go +++ b/Source/buildimplementationrust.go @@ -637,6 +637,8 @@ func writeRustMethodWrapper(method ComponentDefinitionMethod, w LanguageWriter, } else { argsString += fmt.Sprintf(", %s", OName) } + } else { + returnName = "_return_" + toSnakeCase(param.ParamName) } } if returnName != "" { @@ -650,6 +652,10 @@ func writeRustMethodWrapper(method ComponentDefinitionMethod, w LanguageWriter, if err != nil { return err } + err = writeRustParameterConversionReturn(param, w, errorprefix) + if err != nil { + return err + } } w.Writeln("// All ok") w.Writeln("%s_SUCCESS", errorprefix) @@ -706,7 +712,7 @@ func writeRustParameterConversionArg(param ComponentDefinitionParam, w LanguageW w.Writeln("let %s = &mut %s;", OName, SName) } case "bool", "pointer", "struct", "basicarray", "structarray": - //return fmt.Errorf("Conversion of type %s for parameter %s not supported", param.ParamType, IName) + return "", fmt.Errorf("Conversion of type %s for parameter %s not supported - yet", param.ParamType, IName) default: return "", fmt.Errorf("Conversion of type %s for parameter %s not supported as is unknown", param.ParamType, IName) } @@ -740,7 +746,58 @@ func writeRustParameterConversionOutPost(param ComponentDefinitionParam, w Langu w.Writeln("let mut %s = unsafe { &mut *%s };", CNRName, CNName) w.Writeln("*%s = %s.len();", CNRName, SLName) case "bool", "pointer", "struct", "basicarray", "structarray": - //return fmt.Errorf("Conversion of type %s for parameter %s not supported", param.ParamType, IName) + // + return fmt.Errorf("Conversion of type %s for parameter %s not supported - yet", param.ParamType, IName) + default: + return fmt.Errorf("Conversion of type %s for parameter %s not supported as is unknown", param.ParamType, IName) + } + return nil +} + +func writeRustParameterConversionReturn(param ComponentDefinitionParam, w LanguageWriter, errorprefix string) error { + if param.ParamPass != "return" { + return nil + } + // Take the returned variable and send it to output pars + IName := toSnakeCase(param.ParamName) + RetName := "_return_" + IName + switch param.ParamType { + case "uint8", "uint16", "uint32", "uint64", "int8", "int16", "int32", "int64", "single", "double": + RefName := "_ref_" + IName + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", IName, errorprefix) + w.Writeln("let mut %s = unsafe{&mut *%s};", RefName, IName) + w.Writeln("*%s = %s;", RefName, RetName) + case "string": + // Check the buffer size and if null + BuffSizeName := IName + "_buffer_size" + BuffName := IName + "_buffer" + BuffSLName := "_buffer_slice_" + IName + CNName := IName + "_needed_chars" + CNRName := "_" + CNName + SLName := "_slice_" + IName + w.Writeln("let %s = %s.as_bytes();", SLName, RetName) + w.Writeln("if %s > %s.len() { return %s_ERROR_BUFFERTOOSMALL; }", BuffSizeName, SLName, errorprefix) + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", BuffName, errorprefix) + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", CNName, errorprefix) + w.Writeln("let mut %s = unsafe { std::slice::from_raw_parts_mut(%s, %s.len()) };", BuffSLName, BuffName, SLName) + w.Writeln("%s.clone_from_slice(%s);", BuffSLName, SLName) + w.Writeln("let mut %s = unsafe { &mut *%s };", CNRName, CNName) + w.Writeln("*%s = %s.len();", CNRName, SLName) + case "class", "optionalclass": + HName := "_handle_" + IName + RefName := "_ref_" + IName + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", IName, errorprefix) + w.Writeln("let %s = Box::new(HandleImpl::T%s(%s));", HName, param.ParamClass, RetName) + w.Writeln("let mut %s = unsafe{&mut *%s};", RefName, IName) + w.Writeln("*%s = Box::into_raw(%s);", RefName, HName) + case "bool": + RefName := "_ref_" + IName + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", IName, errorprefix) + w.Writeln("let mut %s = unsafe{&mut *%s};", RefName, IName) + w.Writeln("*%s = %s as u8;", RefName, RetName) + case "pointer", "struct", "basicarray", "structarray": + // TODO + return fmt.Errorf("Conversion of type %s for parameter %s not supported - yet", param.ParamType, IName) default: return fmt.Errorf("Conversion of type %s for parameter %s not supported as is unknown", param.ParamType, IName) } diff --git a/Source/languagerust.go b/Source/languagerust.go index aae198b9..49a363aa 100644 --- a/Source/languagerust.go +++ b/Source/languagerust.go @@ -430,6 +430,12 @@ func generateRustParameterType(param ComponentDefinitionParam, isPlain bool) (st return RustParamOutTypeName, nil } } + if param.ParamPass == "return" { + if isPlain { + RustParamOutTypeName := fmt.Sprintf("*mut %s", RustParamTypeName) + return RustParamOutTypeName, nil + } + } } return RustParamTypeName, nil } From 8a4ff6a141dd309f6248054defc3e5e2046f16bc Mon Sep 17 00:00:00 2001 From: Robert Goss Date: Tue, 26 Sep 2023 16:33:24 +0100 Subject: [PATCH 18/26] Support all parameter types in wrapper --- Source/buildimplementationrust.go | 182 +++++++++++++++++++++++++++--- Source/languagerust.go | 95 +++++++++++++++- 2 files changed, 255 insertions(+), 22 deletions(-) diff --git a/Source/buildimplementationrust.go b/Source/buildimplementationrust.go index 79326619..2963aa4f 100644 --- a/Source/buildimplementationrust.go +++ b/Source/buildimplementationrust.go @@ -540,6 +540,14 @@ func buildRustWrapper(component ComponentDefinition, w LanguageWriter, Interface if err != nil { return err } + // Build other methods + for i := 0; i < len(component.Classes); i++ { + class := component.Classes[i] + err := writeClassRustWrapper(component, class, w, cprefix) + if err != nil { + return err + } + } return nil } @@ -594,7 +602,7 @@ func writeGlobalRustWrapper(component ComponentDefinition, w LanguageWriter, cpr methods := component.Global.Methods for i := 0; i < len(methods); i++ { method := methods[i] - err := writeRustMethodWrapper(method, w, cprefix, errorprefix) + err := writeRustMethodWrapper(method, nil, w, cprefix, errorprefix) if err != nil { return err } @@ -603,10 +611,40 @@ func writeGlobalRustWrapper(component ComponentDefinition, w LanguageWriter, cpr return nil } -func writeRustMethodWrapper(method ComponentDefinitionMethod, w LanguageWriter, cprefix string, errorprefix string) error { +func writeClassRustWrapper(component ComponentDefinition, class ComponentDefinitionClass, w LanguageWriter, cprefix string) error { + errorprefix := strings.ToUpper(component.NameSpace) + methods := class.Methods + classprefix := cprefix + strings.ToLower(class.ClassName) + "_" + for i := 0; i < len(methods); i++ { + method := methods[i] + err := writeRustMethodWrapper(method, &class, w, classprefix, errorprefix) + if err != nil { + return err + } + w.Writeln("") + } + return nil +} + +func writeRustMethodWrapper(method ComponentDefinitionMethod, optclass *ComponentDefinitionClass, w LanguageWriter, cprefix string, errorprefix string) error { // Build up the parameter strings parameterString := "" returnName := "" + // Handle self parameter if non global + if optclass != nil { + SelfParam := ComponentDefinitionParam{ + ParamName: "self_", + ParamType: "class", + ParamClass: optclass.ClassName, + ParamPass: "in"} + SelfRustParams, err := generateRustParameters(SelfParam, true) + if err != nil { + return err + } + SelfRustParam := SelfRustParams[0] + parameterString += fmt.Sprintf("%s : %s", SelfRustParam.ParamName, SelfRustParam.ParamType) + } + // Handle method parameters for k := 0; k < len(method.Params); k++ { param := method.Params[k] RustParams, err := generateRustParameters(param, true) @@ -624,6 +662,21 @@ func writeRustMethodWrapper(method ComponentDefinitionMethod, w LanguageWriter, } w.Writeln("pub fn %s%s(%s) -> i32 {", cprefix, strings.ToLower(method.MethodName), parameterString) w.AddIndentationLevel(1) + // Convert self parameter if non global + ClassName := "" + if optclass != nil { + SelfParam := ComponentDefinitionParam{ + ParamName: "self_", + ParamType: "class", + ParamClass: optclass.ClassName, + ParamPass: "out"} + CName, err := writeRustParameterConversionArg(SelfParam, w, errorprefix) + if err != nil { + return err + } + ClassName = CName + } + // Convert method parameters argsString := "" for k := 0; k < len(method.Params); k++ { param := method.Params[k] @@ -641,11 +694,22 @@ func writeRustMethodWrapper(method ComponentDefinitionMethod, w LanguageWriter, returnName = "_return_" + toSnakeCase(param.ParamName) } } - if returnName != "" { - w.Writeln("let %s = CWrapper::%s(%s);", returnName, toSnakeCase(method.MethodName), argsString) + if ClassName != "" { + w.Writeln("// Call into trait class") + if returnName != "" { + w.Writeln("let %s = %s.%s(%s);", returnName, ClassName, toSnakeCase(method.MethodName), argsString) + } else { + w.Writeln("%s.%s(%s);", ClassName, toSnakeCase(method.MethodName), argsString) + } } else { - w.Writeln("CWrapper::%s(%s);", toSnakeCase(method.MethodName), argsString) + w.Writeln("// Call into wrapper for global") + if returnName != "" { + w.Writeln("let %s = CWrapper::%s(%s);", returnName, toSnakeCase(method.MethodName), argsString) + } else { + w.Writeln("CWrapper::%s(%s);", toSnakeCase(method.MethodName), argsString) + } } + w.Writeln("") for k := 0; k < len(method.Params); k++ { param := method.Params[k] err := writeRustParameterConversionOutPost(param, w, errorprefix) @@ -657,7 +721,7 @@ func writeRustMethodWrapper(method ComponentDefinitionMethod, w LanguageWriter, return err } } - w.Writeln("// All ok") + w.Writeln("// All ok - return success") w.Writeln("%s_SUCCESS", errorprefix) w.AddIndentationLevel(-1) w.Writeln("}") @@ -669,9 +733,10 @@ func writeRustParameterConversionArg(param ComponentDefinitionParam, w LanguageW return "", nil } IName := toSnakeCase(param.ParamName) + w.Writeln("// Convert parameter %s to be used as an argument", IName) OName := "_" + IName switch param.ParamType { - case "uint8", "uint16", "uint32", "uint64", "int8", "int16", "int32", "int64", "single", "double": + case "uint8", "uint16", "uint32", "uint64", "int8", "int16", "int32", "int64", "single", "double", "functiontype": if param.ParamPass == "in" { w.Writeln("let %s = %s;", OName, IName) } else { @@ -711,11 +776,54 @@ func writeRustParameterConversionArg(param ComponentDefinitionParam, w LanguageW w.Writeln("let mut %s = String::new();", SName) w.Writeln("let %s = &mut %s;", OName, SName) } - case "bool", "pointer", "struct", "basicarray", "structarray": - return "", fmt.Errorf("Conversion of type %s for parameter %s not supported - yet", param.ParamType, IName) + case "basicarray": + basicParam := param + basicParam.ParamType = param.ParamClass + basicParam.ParamPass = "in" + basicTypeName, err := generateRustParameterType(basicParam, true) + if err != nil { + return "", err + } + if param.ParamPass == "in" { + BuffName := IName + "_buffer" + BuffSizeName := IName + "_buffer_size" + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", BuffName, errorprefix) + w.Writeln("let %s = unsafe { std::slice::from_raw_parts(%s, %s) };", OName, BuffName, BuffSizeName) + } else { + AName := "_array_" + IName + w.Writeln("let mut %s : Vec<%s> = Vec::new();", AName, basicTypeName) + w.Writeln("let %s = &mut %s;", OName, AName) + } + case "structarray": + basicTypeName := param.ParamClass + if param.ParamPass == "in" { + BuffName := IName + "_buffer" + BuffSizeName := IName + "_buffer_size" + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", BuffName, errorprefix) + w.Writeln("let %s = unsafe { std::slice::from_raw_parts(%s, %s) };", OName, BuffName, BuffSizeName) + } else { + AName := "_array_" + IName + w.Writeln("let mut %s : Vec<%s> = Vec::new();", AName, basicTypeName) + w.Writeln("let %s = &mut %s;", OName, AName) + } + case "struct", "pointer": + if param.ParamPass == "in" { + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", IName, errorprefix) + w.Writeln("let %s = unsafe {&*%s};", OName, IName) + } else { + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", IName, errorprefix) + w.Writeln("let %s = unsafe {&mut *%s};", OName, IName) + } + case "bool": + if param.ParamPass == "in" { + w.Writeln("let %s = %s != 0;", OName, IName) + } else { + w.Writeln("let %s = true;", OName) + } default: return "", fmt.Errorf("Conversion of type %s for parameter %s not supported as is unknown", param.ParamType, IName) } + w.Writeln("") return OName, nil } @@ -726,9 +834,10 @@ func writeRustParameterConversionOutPost(param ComponentDefinitionParam, w Langu // Any remaining bit needed to wire out variables IName := toSnakeCase(param.ParamName) switch param.ParamType { - case "uint8", "uint16", "uint32", "uint64", "int8", "int16", "int32", "int64", "single", "double", "class", "optionalclass": + case "uint8", "uint16", "uint32", "uint64", "int8", "int16", "int32", "int64", "single", "double", "class", "optionalclass", "functiontype", "struct", "pointer": return nil case "string": + w.Writeln("// Pass the string %s via output parameters", IName) // Check the buffer size and if null BuffSizeName := IName + "_buffer_size" BuffName := IName + "_buffer" @@ -745,9 +854,32 @@ func writeRustParameterConversionOutPost(param ComponentDefinitionParam, w Langu w.Writeln("%s.clone_from_slice(%s);", BuffSLName, SLName) w.Writeln("let mut %s = unsafe { &mut *%s };", CNRName, CNName) w.Writeln("*%s = %s.len();", CNRName, SLName) - case "bool", "pointer", "struct", "basicarray", "structarray": - // - return fmt.Errorf("Conversion of type %s for parameter %s not supported - yet", param.ParamType, IName) + w.Writeln("") + case "basicarray", "structarray": + w.Writeln("// Pass the array %s via output parameters", IName) + // Check the buffer size and if null + BuffSizeName := IName + "_buffer_size" + BuffName := IName + "_buffer" + BuffSLName := "_buffer_slice_" + IName + CountName := IName + "_count" + CountRName := "_" + CountName + AName := "_array_" + IName + SLName := "_slice_" + IName + w.Writeln("let %s = %s.as_slice();", SLName, AName) + w.Writeln("if %s > %s.len() { return %s_ERROR_BUFFERTOOSMALL; }", BuffSizeName, SLName, errorprefix) + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", BuffName, errorprefix) + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", CountName, errorprefix) + w.Writeln("let mut %s = unsafe { std::slice::from_raw_parts_mut(%s, %s.len()) };", BuffSLName, BuffName, SLName) + w.Writeln("%s.clone_from_slice(%s);", BuffSLName, SLName) + w.Writeln("let mut %s = unsafe { &mut *%s };", CountRName, CountName) + w.Writeln("*%s = %s.len();", CountRName, SLName) + w.Writeln("") + case "bool": + OName := "_" + IName + RefName := "_ref_" + IName + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", IName, errorprefix) + w.Writeln("let mut %s = unsafe{&mut *%s};", RefName, IName) + w.Writeln("*%s = %s as u8;", RefName, OName) default: return fmt.Errorf("Conversion of type %s for parameter %s not supported as is unknown", param.ParamType, IName) } @@ -760,9 +892,10 @@ func writeRustParameterConversionReturn(param ComponentDefinitionParam, w Langua } // Take the returned variable and send it to output pars IName := toSnakeCase(param.ParamName) + w.Writeln("// Pass the return value %s via output parameters", IName) RetName := "_return_" + IName switch param.ParamType { - case "uint8", "uint16", "uint32", "uint64", "int8", "int16", "int32", "int64", "single", "double": + case "uint8", "uint16", "uint32", "uint64", "int8", "int16", "int32", "int64", "single", "double", "functiontype", "struct", "pointer": RefName := "_ref_" + IName w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", IName, errorprefix) w.Writeln("let mut %s = unsafe{&mut *%s};", RefName, IName) @@ -783,6 +916,23 @@ func writeRustParameterConversionReturn(param ComponentDefinitionParam, w Langua w.Writeln("%s.clone_from_slice(%s);", BuffSLName, SLName) w.Writeln("let mut %s = unsafe { &mut *%s };", CNRName, CNName) w.Writeln("*%s = %s.len();", CNRName, SLName) + case "basicarray", "structarray": + // Check the buffer size and if null + BuffSizeName := IName + "_buffer_size" + BuffName := IName + "_buffer" + BuffSLName := "_buffer_slice_" + IName + CountName := IName + "_count" + CountRName := "_" + CountName + SLName := "_slice_" + IName + w.Writeln("let %s = %s.as_slice();", SLName, RetName) + w.Writeln("if %s > %s.len() { return %s_ERROR_BUFFERTOOSMALL; }", BuffSizeName, SLName, errorprefix) + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", BuffName, errorprefix) + w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", CountName, errorprefix) + w.Writeln("let mut %s = unsafe { std::slice::from_raw_parts_mut(%s, %s.len()) };", BuffSLName, BuffName, SLName) + w.Writeln("%s.clone_from_slice(%s);", BuffSLName, SLName) + w.Writeln("let mut %s = unsafe { &mut *%s };", CountRName, CountName) + w.Writeln("*%s = %s.len();", CountRName, SLName) + w.Writeln("") case "class", "optionalclass": HName := "_handle_" + IName RefName := "_ref_" + IName @@ -795,11 +945,9 @@ func writeRustParameterConversionReturn(param ComponentDefinitionParam, w Langua w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", IName, errorprefix) w.Writeln("let mut %s = unsafe{&mut *%s};", RefName, IName) w.Writeln("*%s = %s as u8;", RefName, RetName) - case "pointer", "struct", "basicarray", "structarray": - // TODO - return fmt.Errorf("Conversion of type %s for parameter %s not supported - yet", param.ParamType, IName) default: return fmt.Errorf("Conversion of type %s for parameter %s not supported as is unknown", param.ParamType, IName) } + w.Writeln("") return nil } diff --git a/Source/languagerust.go b/Source/languagerust.go index 49a363aa..94e83c6b 100644 --- a/Source/languagerust.go +++ b/Source/languagerust.go @@ -137,6 +137,7 @@ func writeRustBaseTypeDefinitions(componentdefinition ComponentDefinition, w Lan structinfo := componentdefinition.Structs[i] structName := structinfo.Name w.Writeln("#[repr(C)]") + w.Writeln("#[derive(Clone)]") w.Writeln("pub struct %s {", structName) for j := 0; j < len(structinfo.Members); j++ { member := structinfo.Members[j] @@ -267,11 +268,70 @@ func generateRustParameters(param ComponentDefinitionParam, isPlain bool) ([]Rus } if param.ParamType == "basicarray" { - return nil, fmt.Errorf("Not yet handled") + basicParam := param + basicParam.ParamType = param.ParamClass + basicParam.ParamPass = "in" + basicTypeName, err := generateRustParameterType(basicParam, isPlain) + if err != nil { + return nil, err + } + if param.ParamPass == "out" { + Params = make([]RustParameter, 3) + Params[0].ParamType = "usize" + Params[0].ParamName = toSnakeCase(param.ParamName) + "_buffer_size" + Params[0].ParamComment = fmt.Sprintf("* @param[in] %s - size of the buffer (including trailing 0)", Params[0].ParamName) + + Params[1].ParamType = "*mut usize" + Params[1].ParamName = toSnakeCase(param.ParamName) + "_count" + Params[1].ParamComment = fmt.Sprintf("* @param[out] %s - will be filled with the count of the written elements, or needed buffer size.", Params[1].ParamName) + + Params[2].ParamType = fmt.Sprintf("*mut %s", basicTypeName) + Params[2].ParamName = toSnakeCase(param.ParamName) + "_buffer" + Params[2].ParamComment = fmt.Sprintf("* @param[out] %s - %s buffer of %s, may be NULL", Params[0].ParamName, param.ParamClass, param.ParamDescription) + + return Params, nil + } else { + Params = make([]RustParameter, 2) + Params[0].ParamType = "usize" + Params[0].ParamName = toSnakeCase(param.ParamName) + "_buffer_size" + Params[0].ParamComment = fmt.Sprintf("* @param[in] %s - size of the buffer (including trailing 0)", Params[0].ParamName) + + Params[1].ParamType = fmt.Sprintf("*const %s", basicTypeName) + Params[1].ParamName = toSnakeCase(param.ParamName) + "_buffer" + Params[1].ParamComment = fmt.Sprintf("* @param[in] %s - %s buffer of %s, may be NULL", Params[0].ParamName, param.ParamClass, param.ParamDescription) + + return Params, nil + } } if param.ParamType == "structarray" { - return nil, fmt.Errorf("Not yet handled") + if param.ParamPass == "out" { + Params = make([]RustParameter, 3) + Params[0].ParamType = "usize" + Params[0].ParamName = toSnakeCase(param.ParamName) + "_buffer_size" + Params[0].ParamComment = fmt.Sprintf("* @param[in] %s - size of the buffer (including trailing 0)", Params[0].ParamName) + + Params[1].ParamType = "*mut usize" + Params[1].ParamName = toSnakeCase(param.ParamName) + "_count" + Params[1].ParamComment = fmt.Sprintf("* @param[out] %s - will be filled with the count of the written elements, or needed buffer size.", Params[1].ParamName) + + Params[2].ParamType = fmt.Sprintf("*mut %s", param.ParamClass) + Params[2].ParamName = toSnakeCase(param.ParamName) + "_buffer" + Params[2].ParamComment = fmt.Sprintf("* @param[out] %s - %s buffer of %s, may be NULL", Params[0].ParamName, param.ParamClass, param.ParamDescription) + + return Params, nil + } else { + Params = make([]RustParameter, 2) + Params[0].ParamType = "usize" + Params[0].ParamName = toSnakeCase(param.ParamName) + "_buffer_size" + Params[0].ParamComment = fmt.Sprintf("* @param[in] %s - size of the buffer (including trailing 0)", Params[0].ParamName) + + Params[1].ParamType = fmt.Sprintf("*const %s", param.ParamClass) + Params[1].ParamName = toSnakeCase(param.ParamName) + "_buffer" + Params[1].ParamComment = fmt.Sprintf("* @param[in] %s - %s buffer of %s, may be NULL", Params[0].ParamName, param.ParamClass, param.ParamDescription) + + return Params, nil + } } } @@ -326,8 +386,29 @@ func generateRustParameterType(param ComponentDefinitionParam, isPlain bool) (st RustParamTypeName = "f64" BasicType = true case "pointer": - RustParamTypeName = "c_void" - BasicType = true + basicParam := param + basicParam.ParamType = param.ParamClass + basicParam.ParamPass = "return" + basicTypeName, err := generateRustParameterType(basicParam, isPlain) + if err != nil { + basicTypeName = param.ParamClass + } + if isPlain { + if param.ParamPass == "in" { + RustParamTypeName = fmt.Sprintf("*const %s", basicTypeName) + } else { + RustParamTypeName = fmt.Sprintf("*mut %s", basicTypeName) + } + } else { + switch param.ParamPass { + case "out": + RustParamTypeName = fmt.Sprintf("&mut %s", basicTypeName) + case "in": + RustParamTypeName = fmt.Sprintf("&%s", basicTypeName) + case "return": + RustParamTypeName = fmt.Sprintf("%s", basicTypeName) + } + } case "string": if isPlain { RustParamTypeName = "*const c_char" @@ -354,7 +435,11 @@ func generateRustParameterType(param ComponentDefinitionParam, isPlain bool) (st BasicType = true case "struct": if isPlain { - RustParamTypeName = fmt.Sprintf("*mut %s", ParamClass) + if param.ParamPass == "in" { + RustParamTypeName = fmt.Sprintf("*const %s", ParamClass) + } else { + RustParamTypeName = fmt.Sprintf("*mut %s", ParamClass) + } } else { switch param.ParamPass { case "out": From 3c13291dab12573c321b605abc3f9c4245a0599d Mon Sep 17 00:00:00 2001 From: Robert Goss Date: Tue, 26 Sep 2023 16:48:04 +0100 Subject: [PATCH 19/26] Avoid spurious rust warnings if interfaces unused --- Source/buildimplementationrust.go | 15 +++++++++++---- Source/languagerust.go | 11 ++++++++++- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/Source/buildimplementationrust.go b/Source/buildimplementationrust.go index 2963aa4f..dd29bb66 100644 --- a/Source/buildimplementationrust.go +++ b/Source/buildimplementationrust.go @@ -315,16 +315,21 @@ func writeRustTraitFn(method ComponentDefinitionMethod, w LanguageWriter, hasSel return err } RustParam := RustParams[0] + paramName := RustParam.ParamName + if hasImpl && !hasImplParent { + // For stubed out methods avoid warning about unued parameters + paramName = "_" + RustParam.ParamName + } if param.ParamPass != "return" { if parameterString == "" { - parameterString += fmt.Sprintf("%s : %s", RustParam.ParamName, RustParam.ParamType) + parameterString += fmt.Sprintf("%s : %s", paramName, RustParam.ParamType) } else { - parameterString += fmt.Sprintf(", %s : %s", RustParam.ParamName, RustParam.ParamType) + parameterString += fmt.Sprintf(", %s : %s", paramName, RustParam.ParamType) } if parameterNames == "" { - parameterNames += RustParam.ParamName + parameterNames += paramName } else { - parameterNames += fmt.Sprintf(", %s", RustParam.ParamName) + parameterNames += fmt.Sprintf(", %s", paramName) } } else { returnType = RustParam.ParamType @@ -557,6 +562,7 @@ func buildRustHandle(component ComponentDefinition, w LanguageWriter, InterfaceM w.Writeln("") w.Writeln("use %s::*;", InterfaceMod) w.Writeln("") + w.Writeln("#[allow(dead_code)]") w.Writeln("impl HandleImpl {") w.AddIndentationLevel(1) for i := 0; i < len(component.Classes); i++ { @@ -660,6 +666,7 @@ func writeRustMethodWrapper(method ComponentDefinitionMethod, optclass *Componen } } } + w.Writeln("#[no_mangle]") w.Writeln("pub fn %s%s(%s) -> i32 {", cprefix, strings.ToLower(method.MethodName), parameterString) w.AddIndentationLevel(1) // Convert self parameter if non global diff --git a/Source/languagerust.go b/Source/languagerust.go index 94e83c6b..1c3dbefe 100644 --- a/Source/languagerust.go +++ b/Source/languagerust.go @@ -53,10 +53,15 @@ func writeRustBaseTypeDefinitions(componentdefinition ComponentDefinition, w Lan w.Writeln(" Version definition for %s", NameSpace) w.Writeln("**************************************************************************************************************************/") w.Writeln("") + w.Writeln("#[allow(dead_code)]") w.Writeln("pub const %s_VERSION_MAJOR : usize = %d;", strings.ToUpper(NameSpace), majorVersion(componentdefinition.Version)) + w.Writeln("#[allow(dead_code)]") w.Writeln("pub const %s_VERSION_MINOR : usize = %d;", strings.ToUpper(NameSpace), minorVersion(componentdefinition.Version)) + w.Writeln("#[allow(dead_code)]") w.Writeln("pub const %s_VERSION_MICRO : usize= %d;", strings.ToUpper(NameSpace), microVersion(componentdefinition.Version)) + w.Writeln("#[allow(dead_code)]") w.Writeln("pub const %s_VERSION_PRERELEASEINFO : &str = \"%s\";", strings.ToUpper(NameSpace), preReleaseInfo(componentdefinition.Version)) + w.Writeln("#[allow(dead_code)]") w.Writeln("pub const %s_VERSION_BUILDINFO : &str = \"%s\";", strings.ToUpper(NameSpace), buildInfo(componentdefinition.Version)) w.Writeln("") @@ -67,9 +72,11 @@ func writeRustBaseTypeDefinitions(componentdefinition ComponentDefinition, w Lan w.Writeln(" Error constants for %s", NameSpace) w.Writeln("**************************************************************************************************************************/") w.Writeln("") + w.Writeln("#[allow(dead_code)]") w.Writeln("pub const %s_SUCCESS : i32 = 0;", strings.ToUpper(NameSpace)) for i := 0; i < len(componentdefinition.Errors.Errors); i++ { errorcode := componentdefinition.Errors.Errors[i] + w.Writeln("#[allow(dead_code)]") if errorcode.Description != "" { w.Writeln("pub const %s_ERROR_%s : i32 = %d; /** %s */", strings.ToUpper(NameSpace), errorcode.Name, errorcode.Code, errorcode.Description) } else { @@ -85,6 +92,8 @@ func writeRustBaseTypeDefinitions(componentdefinition ComponentDefinition, w Lan w.Writeln("**************************************************************************************************************************/") w.Writeln("") w.Writeln("// Enum of all traits - this acts as a handle as we pass trait pointers through the interface") + w.Writeln("") + w.Writeln("#[allow(dead_code)]") w.Writeln("pub enum HandleImpl {") w.AddIndentationLevel(1) for i := 0; i < len(componentdefinition.Classes); i++ { @@ -101,7 +110,7 @@ func writeRustBaseTypeDefinitions(componentdefinition ComponentDefinition, w Lan w.Writeln("pub type Handle = *mut HandleImpl;") for i := 0; i < len(componentdefinition.Classes); i++ { class := componentdefinition.Classes[i] - w.Writeln("pub type %sHandle = *mut HandleImpl;", class.ClassName) + w.Writeln("pub type %sHandle =Handle;", class.ClassName) } if len(componentdefinition.Enums) > 0 { From c32c0d0a6a135983e0e56c74cfeffe142fc1b1d4 Mon Sep 17 00:00:00 2001 From: Robert Goss Date: Wed, 27 Sep 2023 00:07:17 +0100 Subject: [PATCH 20/26] Tidy up some dev changes --- Source/buildimplementationrust.go | 1 + run.bat | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) delete mode 100644 run.bat diff --git a/Source/buildimplementationrust.go b/Source/buildimplementationrust.go index dd29bb66..36aeeaf4 100644 --- a/Source/buildimplementationrust.go +++ b/Source/buildimplementationrust.go @@ -245,6 +245,7 @@ func buildRustInterfaces(component ComponentDefinition, w LanguageWriter) error func buildCargoForRustImplementation(component ComponentDefinition, w LanguageWriter, path string) error { projectName := strings.ToLower(component.NameSpace) + projectName = strings.CutPrefix(projectName, "lib") w.Writeln("[package]") w.Writeln(" name = \"%s\"", projectName) w.Writeln(" version = \"0.1.0\"") diff --git a/run.bat b/run.bat deleted file mode 100644 index 02babfa0..00000000 --- a/run.bat +++ /dev/null @@ -1,4 +0,0 @@ -CALL Build/build.bat -act.exe tmp/libPrimes.xml -o tmp -cd tmp/libPrimes_component/Implementations/Rust -cargo +nightly b \ No newline at end of file From 1e9d82b73cb63220ef3ee9debb97b8918a92c65a Mon Sep 17 00:00:00 2001 From: Robert Goss Date: Wed, 27 Sep 2023 00:07:46 +0100 Subject: [PATCH 21/26] Tidy some personal dev changes --- .gitignore | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 0ccbc689..13671da7 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,4 @@ Examples/**/obj # Visual Studio Code debug -.vscode - -tmp \ No newline at end of file +.vscode \ No newline at end of file From 146900133114224e7f683ca28f294d1fc427b2fd Mon Sep 17 00:00:00 2001 From: Robert Goss Date: Wed, 27 Sep 2023 00:15:52 +0100 Subject: [PATCH 22/26] Fix languagewriter mismerge --- Source/buildimplementationrust.go | 2 +- Source/languagewriter.go | 103 +++++++++++++++--------------- 2 files changed, 53 insertions(+), 52 deletions(-) diff --git a/Source/buildimplementationrust.go b/Source/buildimplementationrust.go index 36aeeaf4..4411272e 100644 --- a/Source/buildimplementationrust.go +++ b/Source/buildimplementationrust.go @@ -245,7 +245,7 @@ func buildRustInterfaces(component ComponentDefinition, w LanguageWriter) error func buildCargoForRustImplementation(component ComponentDefinition, w LanguageWriter, path string) error { projectName := strings.ToLower(component.NameSpace) - projectName = strings.CutPrefix(projectName, "lib") + projectName, _ = strings.CutPrefix(projectName, "lib") w.Writeln("[package]") w.Writeln(" name = \"%s\"", projectName) w.Writeln(" version = \"0.1.0\"") diff --git a/Source/languagewriter.go b/Source/languagewriter.go index 5777ed7e..4de9b5a3 100644 --- a/Source/languagewriter.go +++ b/Source/languagewriter.go @@ -42,10 +42,10 @@ import ( // LanguageWriter is a wrapper around a io.Writer that handles indentation type LanguageWriter struct { - Indentation int + Indentation int IndentString string - Writer io.Writer - CurrentLine string + Writer io.Writer + CurrentLine string } func max(x, y int) int { @@ -56,99 +56,100 @@ func max(x, y int) int { } // AddIndentationLevel adds number of indentation the writers output -func (writer *LanguageWriter) AddIndentationLevel(levels int) error { - writer.Indentation = max(writer.Indentation+levels, 0) +func (writer *LanguageWriter) AddIndentationLevel (levels int) (error) { + writer.Indentation = max(writer.Indentation + levels, 0) return nil } // ResetIndentationLevel adds indentation to all output -func (writer *LanguageWriter) ResetIndentationLevel() error { +func (writer *LanguageWriter) ResetIndentationLevel () (error) { writer.Indentation = 0 return nil } // Writeln formats a string and writes it to a line. Pairs of leading spaces will be replaced by the indent IndentString. -func (writer *LanguageWriter) Writeln(format string, a ...interface{}) (int, error) { +func (writer *LanguageWriter) Writeln (format string, a ...interface{}) (int, error) { - leadingSpaces := 0 + leadingSpaces := 0; for _, rune := range format { if rune == ' ' { - leadingSpaces = leadingSpaces + 1 + leadingSpaces = leadingSpaces + 1; } else { - break + break; } } - leadingIndents := leadingSpaces / 2 - - indentedFormat := strings.Repeat(writer.IndentString, leadingIndents+writer.Indentation) + format[leadingIndents*2:] - return fmt.Fprintf(writer.Writer, indentedFormat+"\n", a...) + leadingIndents := leadingSpaces / 2; + + indentedFormat := strings.Repeat (writer.IndentString, leadingIndents + writer.Indentation) + format[leadingIndents * 2:]; + return fmt.Fprintf (writer.Writer, indentedFormat + "\n", a...); } // Writelns writes multiple lines and processes indentation -func (writer *LanguageWriter) Writelns(prefix string, lines []string) error { +func (writer *LanguageWriter) Writelns (prefix string, lines []string) (error) { for idx := 0; idx < len(lines); idx++ { - _, err := writer.Writeln(prefix + lines[idx]) + _, err := writer.Writeln (prefix + lines[idx]); if err != nil { - return err + return err; } } - - return nil + + return nil; } - + // BeginLine clears the CurrentLine buffer -func (writer *LanguageWriter) BeginLine() { - writer.CurrentLine = "" +func (writer *LanguageWriter) BeginLine () () { + writer.CurrentLine = ""; } // Printf formats a string and appends it to the CurrentLine buffer -func (writer *LanguageWriter) Printf(format string, a ...interface{}) { - writer.CurrentLine = writer.CurrentLine + fmt.Sprintf(format, a...) +func (writer *LanguageWriter) Printf (format string, a ...interface{}) () { + writer.CurrentLine = writer.CurrentLine + fmt.Sprintf (format, a...); } // EndLine flushes the CurrentBuffer to the internal writer -func (writer *LanguageWriter) EndLine() (int, error) { - return writer.Writeln(writer.CurrentLine) +func (writer *LanguageWriter) EndLine () (int, error) { + return writer.Writeln (writer.CurrentLine); } -// WriteTomlLicenseHeader writes a license header into a writer with TOML-style comments prefixing each line -func (writer *LanguageWriter) WriteTomlLicenseHeader(component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx(writer.Writer, component, abstract, includeVersion, "", "", "# ") -} // WriteCMakeLicenseHeader writes a license header into a writer with CMake-style comments -func (writer *LanguageWriter) WriteCMakeLicenseHeader(component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx(writer.Writer, component, abstract, includeVersion, "#[[", "\n]]", "") +func (writer *LanguageWriter) WriteCMakeLicenseHeader (component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx (writer.Writer, component, abstract, includeVersion, "#[[", "\n]]", ""); } // WriteCLicenseHeader writes a license header into a writer with C-style comments -func (writer *LanguageWriter) WriteCLicenseHeader(component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx(writer.Writer, component, abstract, includeVersion, "/*", "*/", "") +func (writer *LanguageWriter) WriteCLicenseHeader (component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx (writer.Writer, component, abstract, includeVersion, "/*", "*/", ""); } // WritePascalLicenseHeader writes a license header into a writer Pascal-style comments -func (writer *LanguageWriter) WritePascalLicenseHeader(component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx(writer.Writer, component, abstract, includeVersion, "(*", "*)", "") +func (writer *LanguageWriter) WritePascalLicenseHeader (component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx (writer.Writer, component, abstract, includeVersion, "(*", "*)", ""); } // WritePythonLicenseHeader writes a license header into a writer Python-style comments -func (writer *LanguageWriter) WritePythonLicenseHeader(component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx(writer.Writer, component, abstract, includeVersion, "'''", "'''", "") +func (writer *LanguageWriter) WritePythonLicenseHeader (component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx (writer.Writer, component, abstract, includeVersion, "'''", "'''", ""); } // WriteJavaLicenseHeader writes a license header into a writer Java-style comments -func (writer *LanguageWriter) WriteJavaLicenseHeader(component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx(writer.Writer, component, abstract, includeVersion, "/*", "*/", "") +func (writer *LanguageWriter) WriteJavaLicenseHeader (component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx (writer.Writer, component, abstract, includeVersion, "/*", "*/", ""); +} + +// WriteTomlLicenseHeader writes a license header into a writer for TOML-style line prefix comments +func (writer *LanguageWriter) WriteTomlLicenseHeader (component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx (writer.Writer, component, abstract, includeVersion, "", "", "//"); } // WritePlainLicenseHeader writes a license header into a writer without comments -func (writer *LanguageWriter) WritePlainLicenseHeader(component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx(writer.Writer, component, abstract, includeVersion, "", "", "") +func (writer *LanguageWriter) WritePlainLicenseHeader (component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx (writer.Writer, component, abstract, includeVersion, "", "", ""); } // WriteLicenseHeader writes a license header into a writer with C-style comments -func WriteLicenseHeader(w io.Writer, component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx(w, component, abstract, includeVersion, "/*", "*/", "") +func WriteLicenseHeader (w io.Writer, component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx (w, component, abstract, includeVersion, "/*", "*/", ""); } // writeLicenseHeaderEx writes a license header into a writer. @@ -187,16 +188,16 @@ func writeLicenseHeaderEx(w io.Writer, component ComponentDefinition, abstract s } // CreateLanguageFile creates a LanguageWriter and sets its indent string -func CreateLanguageFile(fileName string, indentString string) (LanguageWriter, error) { - var result LanguageWriter - var err error - +func CreateLanguageFile (fileName string, indentString string) (LanguageWriter, error) { + var result LanguageWriter; + var err error; + result.IndentString = indentString result.Indentation = 0 result.Writer, err = os.Create(fileName) if err != nil { - return result, err + return result, err; } - - return result, nil + + return result, nil; } From ab3ecdd969872a62b6d46056dd01bcde81af3ff4 Mon Sep 17 00:00:00 2001 From: Robert Goss Date: Fri, 29 Sep 2023 00:59:47 +0100 Subject: [PATCH 23/26] Track memory across boundary add error API --- Source/buildimplementationrust.go | 144 +++++++++++++++++++++++++----- Source/languagerust.go | 6 +- Source/languagewriter.go | 99 ++++++++++---------- 3 files changed, 172 insertions(+), 77 deletions(-) diff --git a/Source/buildimplementationrust.go b/Source/buildimplementationrust.go index 4411272e..f5980f9c 100644 --- a/Source/buildimplementationrust.go +++ b/Source/buildimplementationrust.go @@ -278,15 +278,13 @@ func writeRustTrait(component ComponentDefinition, class ComponentDefinitionClas methods, GetLastErrorMessageMethod(), ClearErrorMessageMethod(), - RegisterErrorMessageMethod(), - IncRefCountMethod(), - DecRefCountMethod()) + RegisterErrorMessageMethod()) } for j := 0; j < len(methods); j++ { method := methods[j] w.Writeln("") - err := writeRustTraitFn(method, w, true, false, false) + err := writeRustTraitFn(method, w, true, false, false, nil) if err != nil { return err } @@ -298,7 +296,7 @@ func writeRustTrait(component ComponentDefinition, class ComponentDefinitionClas return nil } -func writeRustTraitFn(method ComponentDefinitionMethod, w LanguageWriter, hasSelf bool, hasImpl bool, hasImplParent bool) error { +func writeRustTraitFn(method ComponentDefinitionMethod, w LanguageWriter, hasSelf bool, hasImpl bool, hasImplParent bool, impl []string) error { methodName := toSnakeCase(method.MethodName) w.Writeln("// %s", methodName) w.Writeln("//") @@ -352,7 +350,13 @@ func writeRustTraitFn(method ComponentDefinitionMethod, w LanguageWriter, hasSel } w.AddIndentationLevel(1) if !hasImplParent { - w.Writeln("unimplemented!();") + if impl == nil { + w.Writeln("unimplemented!();") + } else { + for i := 0; i < len(impl); i++ { + w.Writeln(impl[i]) + } + } } else { w.Writeln("self.parent.%s(%s)", methodName, parameterNames) } @@ -371,7 +375,7 @@ func writeRustGlobalTrait(component ComponentDefinition, w LanguageWriter) error for j := 0; j < len(methods); j++ { method := methods[j] w.Writeln("") - err := writeRustTraitFn(method, w, false, false, false) + err := writeRustTraitFn(method, w, false, false, false, nil) if err != nil { return err } @@ -395,7 +399,7 @@ func buildRustGlobalStubFile(component ComponentDefinition, w LanguageWriter, In for j := 0; j < len(methods); j++ { method := methods[j] w.Writeln("") - err := writeRustTraitFn(method, w, false, true, false) + err := writeRustTraitFn(method, w, false, true, false, nil) if err != nil { return err } @@ -461,7 +465,11 @@ func buildRustStubFile(component ComponentDefinition, class ComponentDefinitionC w.Writeln("") w.Writeln("// Stub struct to implement the %s trait", Name) if len(parents) == 0 { - w.Writeln("pub struct C%s;", Name) + w.Writeln("pub struct C%s {", Name) + w.AddIndentationLevel(1) + w.Writeln("last_error : Option") + w.ResetIndentationLevel() + w.Writeln("}") } else { w.Writeln("pub struct C%s {", Name) w.AddIndentationLevel(1) @@ -485,14 +493,12 @@ func buildRustStubFile(component ComponentDefinition, class ComponentDefinitionC methods, GetLastErrorMessageMethod(), ClearErrorMessageMethod(), - RegisterErrorMessageMethod(), - IncRefCountMethod(), - DecRefCountMethod()) + RegisterErrorMessageMethod()) } for j := 0; j < len(methods); j++ { method := methods[j] w.Writeln("") - err := writeRustTraitFn(method, w, true, true, true) + err := writeRustTraitFn(method, w, true, true, true, nil) if err != nil { return err } @@ -507,18 +513,14 @@ func buildRustStubFile(component ComponentDefinition, class ComponentDefinitionC w.AddIndentationLevel(1) methods := class.Methods if component.isBaseClass(class) { - methods = append( - methods, - GetLastErrorMessageMethod(), - ClearErrorMessageMethod(), - RegisterErrorMessageMethod(), - IncRefCountMethod(), - DecRefCountMethod()) + writeRustStubGetLastErrorMessageMethod(class, w) + writeRustStubClearErrorMessageMethod(class, w) + writeRustStubRegisterErrorMessageMethod(class, w) } for j := 0; j < len(methods); j++ { method := methods[j] w.Writeln("") - err := writeRustTraitFn(method, w, true, true, false) + err := writeRustTraitFn(method, w, true, true, false, nil) if err != nil { return err } @@ -529,6 +531,31 @@ func buildRustStubFile(component ComponentDefinition, class ComponentDefinitionC return nil } +func writeRustStubGetLastErrorMessageMethod(base ComponentDefinitionClass, w LanguageWriter) { + method := GetLastErrorMessageMethod() + impl := []string{ + "match &self.last_error {", + " None => false,", + " Some(error_val) => {", + " *_error_message = error_val.clone();", + " true", + " }", + "}"} + writeRustTraitFn(method, w, true, true, false, impl) +} +func writeRustStubClearErrorMessageMethod(base ComponentDefinitionClass, w LanguageWriter) { + method := ClearErrorMessageMethod() + impl := []string{ + "self.last_error = None;"} + writeRustTraitFn(method, w, true, true, false, impl) +} +func writeRustStubRegisterErrorMessageMethod(base ComponentDefinitionClass, w LanguageWriter) { + method := RegisterErrorMessageMethod() + impl := []string{ + "self.last_error = Some(_error_message.to_string());"} + writeRustTraitFn(method, w, true, true, false, impl) +} + func buildRustWrapper(component ComponentDefinition, w LanguageWriter, InterfaceMod string) error { // Imports ModName := strings.ToLower(component.NameSpace) @@ -572,6 +599,8 @@ func buildRustHandle(component ComponentDefinition, w LanguageWriter, InterfaceM writeRustHandleAs(component, w, class, true) w.Writeln("") } + writeRustHandleIncRef(component, w) + writeRustHandleDecRef(component, w) w.AddIndentationLevel(-1) w.Writeln("}") return nil @@ -591,9 +620,9 @@ func writeRustHandleAs(component ComponentDefinition, w LanguageWriter, class Co for i := 0; i < len(children); i++ { child := children[i] if !mut { - w.Writeln("HandleImpl::T%s(ptr) => Some(ptr.as_ref()),", child) + w.Writeln("HandleImpl::T%s(_, ptr) => Some(ptr.as_ref()),", child) } else { - w.Writeln("HandleImpl::T%s(ptr) => Some(ptr.as_mut()),", child) + w.Writeln("HandleImpl::T%s(_, ptr) => Some(ptr.as_mut()),", child) } } w.Writeln("_ => None") @@ -604,11 +633,51 @@ func writeRustHandleAs(component ComponentDefinition, w LanguageWriter, class Co return nil } +func writeRustHandleIncRef(component ComponentDefinition, w LanguageWriter) error { + w.Writeln("pub fn inc_ref_count(&mut self) {") + w.AddIndentationLevel(1) + w.Writeln("match self {") + w.AddIndentationLevel(1) + for i := 0; i < len(component.Classes); i++ { + class := component.Classes[i] + w.Writeln("HandleImpl::T%s(count, _) => *count += 1,", class.ClassName) + } + w.AddIndentationLevel(-1) + w.Writeln("}") + w.AddIndentationLevel(-1) + w.Writeln("}") + return nil +} + +func writeRustHandleDecRef(component ComponentDefinition, w LanguageWriter) error { + w.Writeln("pub fn dec_ref_count(&mut self) -> bool {") + w.AddIndentationLevel(1) + w.Writeln("match self {") + w.AddIndentationLevel(1) + for i := 0; i < len(component.Classes); i++ { + class := component.Classes[i] + w.Writeln("HandleImpl::T%s(count, _) => {*count -= 1; *count == 0},", class.ClassName) + } + w.AddIndentationLevel(-1) + w.Writeln("}") + w.AddIndentationLevel(-1) + w.Writeln("}") + return nil +} + func writeGlobalRustWrapper(component ComponentDefinition, w LanguageWriter, cprefix string) error { errorprefix := strings.ToUpper(component.NameSpace) methods := component.Global.Methods for i := 0; i < len(methods); i++ { method := methods[i] + if method.MethodName == component.Global.AcquireMethod { + writeGlobalRustAquireWrapper(w, cprefix, errorprefix) + continue + } + if method.MethodName == component.Global.ReleaseMethod { + writeGlobalRustReleaseWrapper(w, cprefix, errorprefix) + continue + } err := writeRustMethodWrapper(method, nil, w, cprefix, errorprefix) if err != nil { return err @@ -618,6 +687,33 @@ func writeGlobalRustWrapper(component ComponentDefinition, w LanguageWriter, cpr return nil } +func writeGlobalRustAquireWrapper(w LanguageWriter, cprefix string, errorprefix string) { + w.Writeln("#[no_mangle]") + w.Writeln("fn %sacquireinstance(instance : BaseHandle) -> i32 {", cprefix) + w.AddIndentationLevel(1) + w.Writeln("if instance.is_null() { return %s_ERROR_INVALIDPARAM; }", errorprefix) + w.Writeln("let _handle_instance = unsafe {&mut *instance};") + w.Writeln("_handle_instance.inc_ref_count();") + w.Writeln("%s_SUCCESS", errorprefix) + w.AddIndentationLevel(-1) + w.Writeln("}") +} + +func writeGlobalRustReleaseWrapper(w LanguageWriter, cprefix string, errorprefix string) { + w.Writeln("#[no_mangle]") + w.Writeln("fn %sreleaseinstance(instance : BaseHandle) -> i32 {", cprefix) + w.AddIndentationLevel(1) + w.Writeln("if instance.is_null() { return %s_ERROR_INVALIDPARAM; }", errorprefix) + w.Writeln("let _handle_instance = unsafe {&mut *instance};") + w.Writeln("let free = _handle_instance.dec_ref_count();") + w.Writeln("if free {") + w.Writeln(" unsafe { let _ = Box::from_raw(instance); }") + w.Writeln("}") + w.Writeln("%s_SUCCESS", errorprefix) + w.AddIndentationLevel(-1) + w.Writeln("}") +} + func writeClassRustWrapper(component ComponentDefinition, class ComponentDefinitionClass, w LanguageWriter, cprefix string) error { errorprefix := strings.ToUpper(component.NameSpace) methods := class.Methods @@ -945,7 +1041,7 @@ func writeRustParameterConversionReturn(param ComponentDefinitionParam, w Langua HName := "_handle_" + IName RefName := "_ref_" + IName w.Writeln("if %s.is_null() { return %s_ERROR_INVALIDPARAM; }", IName, errorprefix) - w.Writeln("let %s = Box::new(HandleImpl::T%s(%s));", HName, param.ParamClass, RetName) + w.Writeln("let %s = Box::new(HandleImpl::T%s(1, %s));", HName, param.ParamClass, RetName) w.Writeln("let mut %s = unsafe{&mut *%s};", RefName, IName) w.Writeln("*%s = Box::into_raw(%s);", RefName, HName) case "bool": diff --git a/Source/languagerust.go b/Source/languagerust.go index 1c3dbefe..2c97d25c 100644 --- a/Source/languagerust.go +++ b/Source/languagerust.go @@ -99,9 +99,9 @@ func writeRustBaseTypeDefinitions(componentdefinition ComponentDefinition, w Lan for i := 0; i < len(componentdefinition.Classes); i++ { class := componentdefinition.Classes[i] if i != len(componentdefinition.Classes)-1 { - w.Writeln("T%s(Box),", class.ClassName, class.ClassName) + w.Writeln("T%s(u64, Box),", class.ClassName, class.ClassName) } else { - w.Writeln("T%s(Box)", class.ClassName, class.ClassName) + w.Writeln("T%s(u64, Box)", class.ClassName, class.ClassName) } } w.AddIndentationLevel(-1) @@ -424,7 +424,7 @@ func generateRustParameterType(param ComponentDefinitionParam, isPlain bool) (st } else { switch param.ParamPass { case "out": - RustParamTypeName = "&mut str" + RustParamTypeName = "&mut String" case "in": RustParamTypeName = "&str" case "return": diff --git a/Source/languagewriter.go b/Source/languagewriter.go index 4de9b5a3..2e586ae1 100644 --- a/Source/languagewriter.go +++ b/Source/languagewriter.go @@ -42,10 +42,10 @@ import ( // LanguageWriter is a wrapper around a io.Writer that handles indentation type LanguageWriter struct { - Indentation int + Indentation int IndentString string - Writer io.Writer - CurrentLine string + Writer io.Writer + CurrentLine string } func max(x, y int) int { @@ -56,100 +56,99 @@ func max(x, y int) int { } // AddIndentationLevel adds number of indentation the writers output -func (writer *LanguageWriter) AddIndentationLevel (levels int) (error) { - writer.Indentation = max(writer.Indentation + levels, 0) +func (writer *LanguageWriter) AddIndentationLevel(levels int) error { + writer.Indentation = max(writer.Indentation+levels, 0) return nil } // ResetIndentationLevel adds indentation to all output -func (writer *LanguageWriter) ResetIndentationLevel () (error) { +func (writer *LanguageWriter) ResetIndentationLevel() error { writer.Indentation = 0 return nil } // Writeln formats a string and writes it to a line. Pairs of leading spaces will be replaced by the indent IndentString. -func (writer *LanguageWriter) Writeln (format string, a ...interface{}) (int, error) { +func (writer *LanguageWriter) Writeln(format string, a ...interface{}) (int, error) { - leadingSpaces := 0; + leadingSpaces := 0 for _, rune := range format { if rune == ' ' { - leadingSpaces = leadingSpaces + 1; + leadingSpaces = leadingSpaces + 1 } else { - break; + break } } - leadingIndents := leadingSpaces / 2; - - indentedFormat := strings.Repeat (writer.IndentString, leadingIndents + writer.Indentation) + format[leadingIndents * 2:]; - return fmt.Fprintf (writer.Writer, indentedFormat + "\n", a...); + leadingIndents := leadingSpaces / 2 + + indentedFormat := strings.Repeat(writer.IndentString, leadingIndents+writer.Indentation) + format[leadingIndents*2:] + return fmt.Fprintf(writer.Writer, indentedFormat+"\n", a...) } // Writelns writes multiple lines and processes indentation -func (writer *LanguageWriter) Writelns (prefix string, lines []string) (error) { +func (writer *LanguageWriter) Writelns(prefix string, lines []string) error { for idx := 0; idx < len(lines); idx++ { - _, err := writer.Writeln (prefix + lines[idx]); + _, err := writer.Writeln(prefix + lines[idx]) if err != nil { - return err; + return err } } - - return nil; + + return nil } - + // BeginLine clears the CurrentLine buffer -func (writer *LanguageWriter) BeginLine () () { - writer.CurrentLine = ""; +func (writer *LanguageWriter) BeginLine() { + writer.CurrentLine = "" } // Printf formats a string and appends it to the CurrentLine buffer -func (writer *LanguageWriter) Printf (format string, a ...interface{}) () { - writer.CurrentLine = writer.CurrentLine + fmt.Sprintf (format, a...); +func (writer *LanguageWriter) Printf(format string, a ...interface{}) { + writer.CurrentLine = writer.CurrentLine + fmt.Sprintf(format, a...) } // EndLine flushes the CurrentBuffer to the internal writer -func (writer *LanguageWriter) EndLine () (int, error) { - return writer.Writeln (writer.CurrentLine); +func (writer *LanguageWriter) EndLine() (int, error) { + return writer.Writeln(writer.CurrentLine) } - // WriteCMakeLicenseHeader writes a license header into a writer with CMake-style comments -func (writer *LanguageWriter) WriteCMakeLicenseHeader (component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx (writer.Writer, component, abstract, includeVersion, "#[[", "\n]]", ""); +func (writer *LanguageWriter) WriteCMakeLicenseHeader(component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx(writer.Writer, component, abstract, includeVersion, "#[[", "\n]]", "") } // WriteCLicenseHeader writes a license header into a writer with C-style comments -func (writer *LanguageWriter) WriteCLicenseHeader (component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx (writer.Writer, component, abstract, includeVersion, "/*", "*/", ""); +func (writer *LanguageWriter) WriteCLicenseHeader(component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx(writer.Writer, component, abstract, includeVersion, "/*", "*/", "") } // WritePascalLicenseHeader writes a license header into a writer Pascal-style comments -func (writer *LanguageWriter) WritePascalLicenseHeader (component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx (writer.Writer, component, abstract, includeVersion, "(*", "*)", ""); +func (writer *LanguageWriter) WritePascalLicenseHeader(component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx(writer.Writer, component, abstract, includeVersion, "(*", "*)", "") } // WritePythonLicenseHeader writes a license header into a writer Python-style comments -func (writer *LanguageWriter) WritePythonLicenseHeader (component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx (writer.Writer, component, abstract, includeVersion, "'''", "'''", ""); +func (writer *LanguageWriter) WritePythonLicenseHeader(component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx(writer.Writer, component, abstract, includeVersion, "'''", "'''", "") } // WriteJavaLicenseHeader writes a license header into a writer Java-style comments -func (writer *LanguageWriter) WriteJavaLicenseHeader (component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx (writer.Writer, component, abstract, includeVersion, "/*", "*/", ""); +func (writer *LanguageWriter) WriteJavaLicenseHeader(component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx(writer.Writer, component, abstract, includeVersion, "/*", "*/", "") } // WriteTomlLicenseHeader writes a license header into a writer for TOML-style line prefix comments -func (writer *LanguageWriter) WriteTomlLicenseHeader (component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx (writer.Writer, component, abstract, includeVersion, "", "", "//"); +func (writer *LanguageWriter) WriteTomlLicenseHeader(component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx(writer.Writer, component, abstract, includeVersion, "", "", "# ") } // WritePlainLicenseHeader writes a license header into a writer without comments -func (writer *LanguageWriter) WritePlainLicenseHeader (component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx (writer.Writer, component, abstract, includeVersion, "", "", ""); +func (writer *LanguageWriter) WritePlainLicenseHeader(component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx(writer.Writer, component, abstract, includeVersion, "", "", "") } // WriteLicenseHeader writes a license header into a writer with C-style comments -func WriteLicenseHeader (w io.Writer, component ComponentDefinition, abstract string, includeVersion bool) { - writeLicenseHeaderEx (w, component, abstract, includeVersion, "/*", "*/", ""); +func WriteLicenseHeader(w io.Writer, component ComponentDefinition, abstract string, includeVersion bool) { + writeLicenseHeaderEx(w, component, abstract, includeVersion, "/*", "*/", "") } // writeLicenseHeaderEx writes a license header into a writer. @@ -188,16 +187,16 @@ func writeLicenseHeaderEx(w io.Writer, component ComponentDefinition, abstract s } // CreateLanguageFile creates a LanguageWriter and sets its indent string -func CreateLanguageFile (fileName string, indentString string) (LanguageWriter, error) { - var result LanguageWriter; - var err error; - +func CreateLanguageFile(fileName string, indentString string) (LanguageWriter, error) { + var result LanguageWriter + var err error + result.IndentString = indentString result.Indentation = 0 result.Writer, err = os.Create(fileName) if err != nil { - return result, err; + return result, err } - - return result, nil; + + return result, nil } From b60f3ad4a372a2c9f78abdb0462e97e1a1b7771f Mon Sep 17 00:00:00 2001 From: Robert Goss Date: Fri, 29 Sep 2023 23:59:18 +0100 Subject: [PATCH 24/26] Implement the example prime library with rust --- .../Implementations/Rust/Cargo.toml | 16 + .../Interfaces/libprimes_interface_handle.rs | 93 ++++++ .../Interfaces/libprimes_interface_wrapper.rs | 298 ++++++++++++++++++ .../Rust/Interfaces/libprimes_interfaces.rs | 261 +++++++++++++++ .../Implementations/Rust/Stub/libprimes.rs | 92 ++++++ .../Rust/Stub/libprimes_base.rs | 66 ++++ .../Rust/Stub/libprimes_calculator.rs | 104 ++++++ .../libprimes_factorization_calculator.rs | 115 +++++++ .../Rust/Stub/libprimes_sieve_calculator.rs | 115 +++++++ .../Implementations/Rust/lib.rs | 44 +++ 10 files changed, 1204 insertions(+) create mode 100644 Examples/Primes/LibPrimes_component/Implementations/Rust/Cargo.toml create mode 100644 Examples/Primes/LibPrimes_component/Implementations/Rust/Interfaces/libprimes_interface_handle.rs create mode 100644 Examples/Primes/LibPrimes_component/Implementations/Rust/Interfaces/libprimes_interface_wrapper.rs create mode 100644 Examples/Primes/LibPrimes_component/Implementations/Rust/Interfaces/libprimes_interfaces.rs create mode 100644 Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes.rs create mode 100644 Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_base.rs create mode 100644 Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_calculator.rs create mode 100644 Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_factorization_calculator.rs create mode 100644 Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_sieve_calculator.rs create mode 100644 Examples/Primes/LibPrimes_component/Implementations/Rust/lib.rs diff --git a/Examples/Primes/LibPrimes_component/Implementations/Rust/Cargo.toml b/Examples/Primes/LibPrimes_component/Implementations/Rust/Cargo.toml new file mode 100644 index 00000000..914acd5e --- /dev/null +++ b/Examples/Primes/LibPrimes_component/Implementations/Rust/Cargo.toml @@ -0,0 +1,16 @@ +# Copyright (C) 2019 PrimeDevelopers +# +# All rights reserved. +# +# This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.0-develop. +# +# Abstract: This is an autogenerated Cargo file for the development of Prime Numbers Library. +# +# Interface version: 1.2.0 + +[package] + name = "primes" + version = "0.1.0" +[lib] + path = "lib.rs" + crate-type = ["cdylib"] diff --git a/Examples/Primes/LibPrimes_component/Implementations/Rust/Interfaces/libprimes_interface_handle.rs b/Examples/Primes/LibPrimes_component/Implementations/Rust/Interfaces/libprimes_interface_handle.rs new file mode 100644 index 00000000..836fe590 --- /dev/null +++ b/Examples/Primes/LibPrimes_component/Implementations/Rust/Interfaces/libprimes_interface_handle.rs @@ -0,0 +1,93 @@ +/*++ + +Copyright (C) 2019 PrimeDevelopers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.0-develop. + +Abstract: This is an autogenerated Rust implementation file in order to allow easy +development of Prime Numbers Library. The functions in this file need to be implemented. It needs to be generated only once. + +Interface version: 1.2.0 + +*/ + + +// Handle passed through interface define the casting maps needed to extract + +use libprimes_interfaces::*; + +#[allow(dead_code)] +impl HandleImpl { + pub fn as_base(&self) -> Option<&dyn Base> { + match self { + HandleImpl::TCalculator(_, ptr) => Some(ptr.as_ref()), + HandleImpl::TFactorizationCalculator(_, ptr) => Some(ptr.as_ref()), + HandleImpl::TSieveCalculator(_, ptr) => Some(ptr.as_ref()), + _ => None + } + } + pub fn as_mut_base(&mut self) -> Option<&mut dyn Base> { + match self { + HandleImpl::TCalculator(_, ptr) => Some(ptr.as_mut()), + HandleImpl::TFactorizationCalculator(_, ptr) => Some(ptr.as_mut()), + HandleImpl::TSieveCalculator(_, ptr) => Some(ptr.as_mut()), + _ => None + } + } + + pub fn as_calculator(&self) -> Option<&dyn Calculator> { + match self { + HandleImpl::TFactorizationCalculator(_, ptr) => Some(ptr.as_ref()), + HandleImpl::TSieveCalculator(_, ptr) => Some(ptr.as_ref()), + _ => None + } + } + pub fn as_mut_calculator(&mut self) -> Option<&mut dyn Calculator> { + match self { + HandleImpl::TFactorizationCalculator(_, ptr) => Some(ptr.as_mut()), + HandleImpl::TSieveCalculator(_, ptr) => Some(ptr.as_mut()), + _ => None + } + } + + pub fn as_factorization_calculator(&self) -> Option<&dyn FactorizationCalculator> { + match self { + _ => None + } + } + pub fn as_mut_factorization_calculator(&mut self) -> Option<&mut dyn FactorizationCalculator> { + match self { + _ => None + } + } + + pub fn as_sieve_calculator(&self) -> Option<&dyn SieveCalculator> { + match self { + _ => None + } + } + pub fn as_mut_sieve_calculator(&mut self) -> Option<&mut dyn SieveCalculator> { + match self { + _ => None + } + } + + pub fn inc_ref_count(&mut self) { + match self { + HandleImpl::TBase(count, _) => *count += 1, + HandleImpl::TCalculator(count, _) => *count += 1, + HandleImpl::TFactorizationCalculator(count, _) => *count += 1, + HandleImpl::TSieveCalculator(count, _) => *count += 1, + } + } + pub fn dec_ref_count(&mut self) -> bool { + match self { + HandleImpl::TBase(count, _) => {*count -= 1; *count == 0}, + HandleImpl::TCalculator(count, _) => {*count -= 1; *count == 0}, + HandleImpl::TFactorizationCalculator(count, _) => {*count -= 1; *count == 0}, + HandleImpl::TSieveCalculator(count, _) => {*count -= 1; *count == 0}, + } + } +} diff --git a/Examples/Primes/LibPrimes_component/Implementations/Rust/Interfaces/libprimes_interface_wrapper.rs b/Examples/Primes/LibPrimes_component/Implementations/Rust/Interfaces/libprimes_interface_wrapper.rs new file mode 100644 index 00000000..880f6e82 --- /dev/null +++ b/Examples/Primes/LibPrimes_component/Implementations/Rust/Interfaces/libprimes_interface_wrapper.rs @@ -0,0 +1,298 @@ +/*++ + +Copyright (C) 2019 PrimeDevelopers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.0-develop. + +Abstract: This is an autogenerated Rust implementation file in order to allow easy +development of Prime Numbers Library. The functions in this file need to be implemented. It needs to be generated only once. + +Interface version: 1.2.0 + +*/ + + +// Calls from the C-Interface to the Rust traits via the CWrapper +// These are the symbols exposed in the shared object interface + +use libprimes_interfaces::*; +use libprimes::CWrapper; +use std::ffi::{c_char, CStr}; + +#[no_mangle] +pub fn libprimes_getversion(major : *mut u32, minor : *mut u32, micro : *mut u32) -> i32 { + // Convert parameter major to be used as an argument + if major.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _major = unsafe {&mut *major}; + + // Convert parameter minor to be used as an argument + if minor.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _minor = unsafe {&mut *minor}; + + // Convert parameter micro to be used as an argument + if micro.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _micro = unsafe {&mut *micro}; + + // Call into wrapper for global + CWrapper::get_version(_major, _minor, _micro); + + // All ok - return success + LIBPRIMES_SUCCESS +} + +#[no_mangle] +pub fn libprimes_getlasterror(instance : BaseHandle, error_message_buffer_size : usize, error_message_needed_chars : *mut usize, error_message_buffer : *mut u8, has_error : *mut u8) -> i32 { + // Convert parameter instance to be used as an argument + if instance.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _handle_instance = unsafe {&*instance}; + let _optional_instance = _handle_instance.as_base(); + if _optional_instance.is_none() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _instance = _optional_instance.unwrap(); + + // Convert parameter error_message to be used as an argument + let mut _string_error_message = String::new(); + let _error_message = &mut _string_error_message; + + // Call into wrapper for global + let _return_has_error = CWrapper::get_last_error(_instance, _error_message); + + // Pass the string error_message via output parameters + let _slice_error_message = _string_error_message.as_bytes(); + if error_message_buffer_size > _slice_error_message.len() { return LIBPRIMES_ERROR_BUFFERTOOSMALL; } + if error_message_buffer.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + if error_message_needed_chars.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let mut _buffer_slice_error_message = unsafe { std::slice::from_raw_parts_mut(error_message_buffer, _slice_error_message.len()) }; + _buffer_slice_error_message.clone_from_slice(_slice_error_message); + let mut _error_message_needed_chars = unsafe { &mut *error_message_needed_chars }; + *_error_message_needed_chars = _slice_error_message.len(); + + // Pass the return value has_error via output parameters + if has_error.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let mut _ref_has_error = unsafe{&mut *has_error}; + *_ref_has_error = _return_has_error as u8; + + // All ok - return success + LIBPRIMES_SUCCESS +} + +#[no_mangle] +fn libprimes_acquireinstance(instance : BaseHandle) -> i32 { + if instance.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _handle_instance = unsafe {&mut *instance}; + _handle_instance.inc_ref_count(); + LIBPRIMES_SUCCESS +} +#[no_mangle] +fn libprimes_releaseinstance(instance : BaseHandle) -> i32 { + if instance.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _handle_instance = unsafe {&mut *instance}; + let free = _handle_instance.dec_ref_count(); + if free { + unsafe { let _ = Box::from_raw(instance); } + } + LIBPRIMES_SUCCESS +} +#[no_mangle] +pub fn libprimes_createfactorizationcalculator(instance : *mut FactorizationCalculatorHandle) -> i32 { + // Call into wrapper for global + let _return_instance = CWrapper::create_factorization_calculator(); + + // Pass the return value instance via output parameters + if instance.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _handle_instance = Box::new(HandleImpl::TFactorizationCalculator(1, _return_instance)); + let mut _ref_instance = unsafe{&mut *instance}; + *_ref_instance = Box::into_raw(_handle_instance); + + // All ok - return success + LIBPRIMES_SUCCESS +} + +#[no_mangle] +pub fn libprimes_createsievecalculator(instance : *mut SieveCalculatorHandle) -> i32 { + // Call into wrapper for global + let _return_instance = CWrapper::create_sieve_calculator(); + + // Pass the return value instance via output parameters + if instance.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _handle_instance = Box::new(HandleImpl::TSieveCalculator(1, _return_instance)); + let mut _ref_instance = unsafe{&mut *instance}; + *_ref_instance = Box::into_raw(_handle_instance); + + // All ok - return success + LIBPRIMES_SUCCESS +} + +#[no_mangle] +pub fn libprimes_setjournal(file_name : *const c_char) -> i32 { + // Convert parameter file_name to be used as an argument + if file_name.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _str_file_name = unsafe{ CStr::from_ptr(file_name) }; + let _optional_file_name = _str_file_name.to_str(); + if _optional_file_name.is_err() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _file_name = _optional_file_name.unwrap(); + + // Call into wrapper for global + CWrapper::set_journal(_file_name); + + // All ok - return success + LIBPRIMES_SUCCESS +} + +#[no_mangle] +pub fn libprimes_base_classtypeid(self_ : BaseHandle, class_type_id : *mut u64) -> i32 { + // Convert parameter self_ to be used as an argument + if self_.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _handle_self_ = unsafe {&mut *self_}; + let _optional_self_ = _handle_self_.as_mut_base(); + if _optional_self_.is_none() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _self_ = _optional_self_.unwrap(); + + // Call into trait class + let _return_class_type_id = _self_.class_type_id(); + + // Pass the return value class_type_id via output parameters + if class_type_id.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let mut _ref_class_type_id = unsafe{&mut *class_type_id}; + *_ref_class_type_id = _return_class_type_id; + + // All ok - return success + LIBPRIMES_SUCCESS +} + +#[no_mangle] +pub fn libprimes_calculator_getvalue(self_ : CalculatorHandle, value : *mut u64) -> i32 { + // Convert parameter self_ to be used as an argument + if self_.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _handle_self_ = unsafe {&mut *self_}; + let _optional_self_ = _handle_self_.as_mut_calculator(); + if _optional_self_.is_none() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _self_ = _optional_self_.unwrap(); + + // Call into trait class + let _return_value = _self_.get_value(); + + // Pass the return value value via output parameters + if value.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let mut _ref_value = unsafe{&mut *value}; + *_ref_value = _return_value; + + // All ok - return success + LIBPRIMES_SUCCESS +} + +#[no_mangle] +pub fn libprimes_calculator_setvalue(self_ : CalculatorHandle, value : u64) -> i32 { + // Convert parameter self_ to be used as an argument + if self_.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _handle_self_ = unsafe {&mut *self_}; + let _optional_self_ = _handle_self_.as_mut_calculator(); + if _optional_self_.is_none() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _self_ = _optional_self_.unwrap(); + + // Convert parameter value to be used as an argument + let _value = value; + + // Call into trait class + _self_.set_value(_value); + + // All ok - return success + LIBPRIMES_SUCCESS +} + +#[no_mangle] +pub fn libprimes_calculator_calculate(self_ : CalculatorHandle) -> i32 { + // Convert parameter self_ to be used as an argument + if self_.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _handle_self_ = unsafe {&mut *self_}; + let _optional_self_ = _handle_self_.as_mut_calculator(); + if _optional_self_.is_none() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _self_ = _optional_self_.unwrap(); + + // Call into trait class + _self_.calculate(); + + // All ok - return success + LIBPRIMES_SUCCESS +} + +#[no_mangle] +pub fn libprimes_calculator_setprogresscallback(self_ : CalculatorHandle, progress_callback : ProgressCallback) -> i32 { + // Convert parameter self_ to be used as an argument + if self_.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _handle_self_ = unsafe {&mut *self_}; + let _optional_self_ = _handle_self_.as_mut_calculator(); + if _optional_self_.is_none() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _self_ = _optional_self_.unwrap(); + + // Convert parameter progress_callback to be used as an argument + let _progress_callback = progress_callback; + + // Call into trait class + _self_.set_progress_callback(_progress_callback); + + // All ok - return success + LIBPRIMES_SUCCESS +} + +#[no_mangle] +pub fn libprimes_factorizationcalculator_getprimefactors(self_ : FactorizationCalculatorHandle, prime_factors_buffer_size : usize, prime_factors_count : *mut usize, prime_factors_buffer : *mut PrimeFactor) -> i32 { + // Convert parameter self_ to be used as an argument + if self_.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _handle_self_ = unsafe {&mut *self_}; + let _optional_self_ = _handle_self_.as_mut_factorization_calculator(); + if _optional_self_.is_none() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _self_ = _optional_self_.unwrap(); + + // Convert parameter prime_factors to be used as an argument + let mut _array_prime_factors : Vec = Vec::new(); + let _prime_factors = &mut _array_prime_factors; + + // Call into trait class + _self_.get_prime_factors(_prime_factors); + + // Pass the array prime_factors via output parameters + let _slice_prime_factors = _array_prime_factors.as_slice(); + if prime_factors_buffer_size > _slice_prime_factors.len() { return LIBPRIMES_ERROR_BUFFERTOOSMALL; } + if prime_factors_buffer.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + if prime_factors_count.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let mut _buffer_slice_prime_factors = unsafe { std::slice::from_raw_parts_mut(prime_factors_buffer, _slice_prime_factors.len()) }; + _buffer_slice_prime_factors.clone_from_slice(_slice_prime_factors); + let mut _prime_factors_count = unsafe { &mut *prime_factors_count }; + *_prime_factors_count = _slice_prime_factors.len(); + + // All ok - return success + LIBPRIMES_SUCCESS +} + +#[no_mangle] +pub fn libprimes_sievecalculator_getprimes(self_ : SieveCalculatorHandle, primes_buffer_size : usize, primes_count : *mut usize, primes_buffer : *mut u64) -> i32 { + // Convert parameter self_ to be used as an argument + if self_.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _handle_self_ = unsafe {&mut *self_}; + let _optional_self_ = _handle_self_.as_mut_sieve_calculator(); + if _optional_self_.is_none() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let _self_ = _optional_self_.unwrap(); + + // Convert parameter primes to be used as an argument + let mut _array_primes : Vec = Vec::new(); + let _primes = &mut _array_primes; + + // Call into trait class + _self_.get_primes(_primes); + + // Pass the array primes via output parameters + let _slice_primes = _array_primes.as_slice(); + if primes_buffer_size > _slice_primes.len() { return LIBPRIMES_ERROR_BUFFERTOOSMALL; } + if primes_buffer.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + if primes_count.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } + let mut _buffer_slice_primes = unsafe { std::slice::from_raw_parts_mut(primes_buffer, _slice_primes.len()) }; + _buffer_slice_primes.clone_from_slice(_slice_primes); + let mut _primes_count = unsafe { &mut *primes_count }; + *_primes_count = _slice_primes.len(); + + // All ok - return success + LIBPRIMES_SUCCESS +} + diff --git a/Examples/Primes/LibPrimes_component/Implementations/Rust/Interfaces/libprimes_interfaces.rs b/Examples/Primes/LibPrimes_component/Implementations/Rust/Interfaces/libprimes_interfaces.rs new file mode 100644 index 00000000..08d8b6d3 --- /dev/null +++ b/Examples/Primes/LibPrimes_component/Implementations/Rust/Interfaces/libprimes_interfaces.rs @@ -0,0 +1,261 @@ +/*++ + +Copyright (C) 2019 PrimeDevelopers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.0-develop. + +Abstract: This is an autogenerated rust file in order to allow easy +development of Prime Numbers Library. The implementer of Prime Numbers Library needs to +derive concrete classes from the abstract classes in this header. + +Interface version: 1.2.0 + +*/ + +#[allow(unused_imports)] +use std::ffi::c_void; + +/************************************************************************************************************************* + Version definition for LibPrimes +**************************************************************************************************************************/ + +#[allow(dead_code)] +pub const LIBPRIMES_VERSION_MAJOR : usize = 1; +#[allow(dead_code)] +pub const LIBPRIMES_VERSION_MINOR : usize = 2; +#[allow(dead_code)] +pub const LIBPRIMES_VERSION_MICRO : usize= 0; +#[allow(dead_code)] +pub const LIBPRIMES_VERSION_PRERELEASEINFO : &str = ""; +#[allow(dead_code)] +pub const LIBPRIMES_VERSION_BUILDINFO : &str = ""; + + +/************************************************************************************************************************* + Error constants for LibPrimes +**************************************************************************************************************************/ + +#[allow(dead_code)] +pub const LIBPRIMES_SUCCESS : i32 = 0; +#[allow(dead_code)] +pub const LIBPRIMES_ERROR_NOTIMPLEMENTED : i32 = 1; /** functionality not implemented */ +#[allow(dead_code)] +pub const LIBPRIMES_ERROR_INVALIDPARAM : i32 = 2; /** an invalid parameter was passed */ +#[allow(dead_code)] +pub const LIBPRIMES_ERROR_INVALIDCAST : i32 = 3; /** a type cast failed */ +#[allow(dead_code)] +pub const LIBPRIMES_ERROR_BUFFERTOOSMALL : i32 = 4; /** a provided buffer is too small */ +#[allow(dead_code)] +pub const LIBPRIMES_ERROR_GENERICEXCEPTION : i32 = 5; /** a generic exception occurred */ +#[allow(dead_code)] +pub const LIBPRIMES_ERROR_COULDNOTLOADLIBRARY : i32 = 6; /** the library could not be loaded */ +#[allow(dead_code)] +pub const LIBPRIMES_ERROR_COULDNOTFINDLIBRARYEXPORT : i32 = 7; /** a required exported symbol could not be found in the library */ +#[allow(dead_code)] +pub const LIBPRIMES_ERROR_INCOMPATIBLEBINARYVERSION : i32 = 8; /** the version of the binary interface does not match the bindings interface */ +#[allow(dead_code)] +pub const LIBPRIMES_ERROR_NORESULTAVAILABLE : i32 = 9; /** no result is available */ +#[allow(dead_code)] +pub const LIBPRIMES_ERROR_CALCULATIONABORTED : i32 = 10; /** a calculation has been aborted */ + + +/************************************************************************************************************************* + Handle definiton for LibPrimes +**************************************************************************************************************************/ + +// Enum of all traits - this acts as a handle as we pass trait pointers through the interface + +#[allow(dead_code)] +pub enum HandleImpl { + TBase(u64, Box), + TCalculator(u64, Box), + TFactorizationCalculator(u64, Box), + TSieveCalculator(u64, Box) +} + +pub type Handle = *mut HandleImpl; +pub type BaseHandle =Handle; +pub type CalculatorHandle =Handle; +pub type FactorizationCalculatorHandle =Handle; +pub type SieveCalculatorHandle =Handle; +/************************************************************************************************************************* + Interface Struct definitions for LibPrimes +**************************************************************************************************************************/ + +#[repr(C)] +#[derive(Clone)] +pub struct PrimeFactor { + pub prime: u64, + pub multiplicity: u32 +} + +/************************************************************************************************************************* + Function type definitions for LibPrimes +**************************************************************************************************************************/ + +// Callback to report calculation progress and query whether it should be aborted +// +// * @param[in] progress_percentage - How far has the calculation progressed? +// * @param[out] should_abort - Should the calculation be aborted? +// +pub type ProgressCallback = unsafe extern "C" fn(progress_percentage : f32, should_abort : *mut u8); +/************************************************************************************************************************* + Traits defined for LibPrimes +**************************************************************************************************************************/ + +// Trait for interface Base +// +pub trait Base { + + // class_type_id + // + // Get Class Type Id + // * @param[return] class_type_id - Class type as a 64 bits integer + // + fn class_type_id(&mut self) -> u64; + + // get_last_error_message + // + // Returns the last error registered of this class instance + // * @param[out] error_message - Message of the last error registered + // * @param[return] has_last_error - Has an error been registered already + // + fn get_last_error_message(&mut self, error_message : &mut String) -> bool; + + // clear_error_messages + // + // Clears all registered messages of this class instance + // + fn clear_error_messages(&mut self); + + // register_error_message + // + // Registers an error message with this class instance + // * @param[in] error_message - Error message to register + // + fn register_error_message(&mut self, error_message : &str); +} + + +// Trait for interface Calculator +// +pub trait Calculator : Base { + + // get_value + // + // Returns the current value of this Calculator + // * @param[return] value - The current value of this Calculator + // + fn get_value(&mut self) -> u64; + + // set_value + // + // Sets the value to be factorized + // * @param[in] value - The value to be factorized + // + fn set_value(&mut self, value : u64); + + // calculate + // + // Performs the specific calculation of this Calculator + // + fn calculate(&mut self); + + // set_progress_callback + // + // Sets the progress callback function + // * @param[in] progress_callback - The progress callback + // + fn set_progress_callback(&mut self, progress_callback : ProgressCallback); +} + + +// Trait for interface FactorizationCalculator +// +pub trait FactorizationCalculator : Calculator { + + // get_prime_factors + // + // Returns the prime factors of this number (without multiplicity) + // * @param[out] prime_factors - The prime factors of this number + // + fn get_prime_factors(&mut self, prime_factors : &mut Vec); +} + + +// Trait for interface SieveCalculator +// +pub trait SieveCalculator : Calculator { + + // get_primes + // + // Returns all prime numbers lower or equal to the sieve's value + // * @param[out] primes - The primes lower or equal to the sieve's value + // + fn get_primes(&mut self, primes : &mut Vec); +} + + +/************************************************************************************************************************* + Trait defined for global methods of LibPrimes +**************************************************************************************************************************/ + +// Wrapper trait for global methods +// +pub trait Wrapper { + + // get_version + // + // retrieves the binary version of this library. + // * @param[out] major - returns the major version of this library + // * @param[out] minor - returns the minor version of this library + // * @param[out] micro - returns the micro version of this library + // + fn get_version(major : &mut u32, minor : &mut u32, micro : &mut u32); + + // get_last_error + // + // Returns the last error recorded on this object + // * @param[in] instance - Instance Handle + // * @param[out] error_message - Message of the last error + // * @param[return] has_error - Is there a last error to query + // + fn get_last_error(instance : & dyn Base, error_message : &mut String) -> bool; + + // acquire_instance + // + // Acquire shared ownership of an Instance + // * @param[in] instance - Instance Handle + // + fn acquire_instance(instance : & dyn Base); + + // release_instance + // + // Releases shared ownership of an Instance + // * @param[in] instance - Instance Handle + // + fn release_instance(instance : & dyn Base); + + // create_factorization_calculator + // + // Creates a new FactorizationCalculator instance + // * @param[return] instance - New FactorizationCalculator instance + // + fn create_factorization_calculator() -> Box; + + // create_sieve_calculator + // + // Creates a new SieveCalculator instance + // * @param[return] instance - New SieveCalculator instance + // + fn create_sieve_calculator() -> Box; + + // set_journal + // + // Handles Library Journaling + // * @param[in] file_name - Journal FileName + // + fn set_journal(file_name : &str); +} diff --git a/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes.rs b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes.rs new file mode 100644 index 00000000..1fa0a5ef --- /dev/null +++ b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes.rs @@ -0,0 +1,92 @@ +/*++ + +Copyright (C) 2019 PrimeDevelopers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.0-develop. + +Abstract: This is an autogenerated Rust implementation file in order to allow easy +development of Prime Numbers Library. It needs to be generated only once. + +Interface version: 1.2.0 + +*/ + + +use libprimes_interfaces::*; + +// Wrapper struct to implement the wrapper trait for global methods +pub struct CWrapper; + +impl Wrapper for CWrapper { + + + // get_version + // + // retrieves the binary version of this library. + // * @param[out] major - returns the major version of this library + // * @param[out] minor - returns the minor version of this library + // * @param[out] micro - returns the micro version of this library + // + fn get_version(_major : &mut u32, _minor : &mut u32, _micro : &mut u32) { + unimplemented!(); + } + + // get_last_error + // + // Returns the last error recorded on this object + // * @param[in] instance - Instance Handle + // * @param[out] error_message - Message of the last error + // * @param[return] has_error - Is there a last error to query + // + fn get_last_error(_instance : & dyn Base, _error_message : &mut String) -> bool { + unimplemented!(); + } + + // acquire_instance + // + // Acquire shared ownership of an Instance + // * @param[in] instance - Instance Handle + // + fn acquire_instance(_instance : & dyn Base) { + unimplemented!(); + } + + // release_instance + // + // Releases shared ownership of an Instance + // * @param[in] instance - Instance Handle + // + fn release_instance(_instance : & dyn Base) { + unimplemented!(); + } + + // create_factorization_calculator + // + // Creates a new FactorizationCalculator instance + // * @param[return] instance - New FactorizationCalculator instance + // + fn create_factorization_calculator() -> Box { + unimplemented!(); + } + + // create_sieve_calculator + // + // Creates a new SieveCalculator instance + // * @param[return] instance - New SieveCalculator instance + // + fn create_sieve_calculator() -> Box { + unimplemented!(); + } + + // set_journal + // + // Handles Library Journaling + // * @param[in] file_name - Journal FileName + // + fn set_journal(_file_name : &str) { + unimplemented!(); + } +} + diff --git a/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_base.rs b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_base.rs new file mode 100644 index 00000000..4cb747bd --- /dev/null +++ b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_base.rs @@ -0,0 +1,66 @@ +/*++ + +Copyright (C) 2019 PrimeDevelopers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.0-develop. + +Abstract: This is an autogenerated Rust implementation file in order to allow easy +development of Prime Numbers Library. It needs to be generated only once. + +Interface version: 1.2.0 + +*/ + + +use libprimes_interfaces::*; + +// Stub struct to implement the Base trait +pub struct CBase { + last_error : Option +} + +impl Base for CBase { + + // get_last_error_message + // + // Returns the last error registered of this class instance + // * @param[out] error_message - Message of the last error registered + // * @param[return] has_last_error - Has an error been registered already + // + fn get_last_error_message(&mut self, _error_message : &mut String) -> bool { + match &self.last_error { + None => false, + Some(error_val) => { + *_error_message = error_val.clone(); + true + } + } + } + // clear_error_messages + // + // Clears all registered messages of this class instance + // + fn clear_error_messages(&mut self) { + self.last_error = None; + } + // register_error_message + // + // Registers an error message with this class instance + // * @param[in] error_message - Error message to register + // + fn register_error_message(&mut self, _error_message : &str) { + self.last_error = Some(_error_message.to_string()); + } + + // class_type_id + // + // Get Class Type Id + // * @param[return] class_type_id - Class type as a 64 bits integer + // + fn class_type_id(&mut self) -> u64 { + unimplemented!(); + } +} + diff --git a/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_calculator.rs b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_calculator.rs new file mode 100644 index 00000000..f2167a55 --- /dev/null +++ b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_calculator.rs @@ -0,0 +1,104 @@ +/*++ + +Copyright (C) 2019 PrimeDevelopers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.0-develop. + +Abstract: This is an autogenerated Rust implementation file in order to allow easy +development of Prime Numbers Library. It needs to be generated only once. + +Interface version: 1.2.0 + +*/ + + +use libprimes_interfaces::*; +use libprimes_base::CBase; + +// Stub struct to implement the Calculator trait +pub struct CCalculator { + parent : CBase +} + +// Implementation of parent traits via parent + +impl Base for CCalculator { + + // class_type_id + // + // Get Class Type Id + // * @param[return] class_type_id - Class type as a 64 bits integer + // + fn class_type_id(&mut self) -> u64 { + self.parent.class_type_id() + } + + // get_last_error_message + // + // Returns the last error registered of this class instance + // * @param[out] error_message - Message of the last error registered + // * @param[return] has_last_error - Has an error been registered already + // + fn get_last_error_message(&mut self, error_message : &mut String) -> bool { + self.parent.get_last_error_message(error_message) + } + + // clear_error_messages + // + // Clears all registered messages of this class instance + // + fn clear_error_messages(&mut self) { + self.parent.clear_error_messages() + } + + // register_error_message + // + // Registers an error message with this class instance + // * @param[in] error_message - Error message to register + // + fn register_error_message(&mut self, error_message : &str) { + self.parent.register_error_message(error_message) + } +} + +impl Calculator for CCalculator { + + + // get_value + // + // Returns the current value of this Calculator + // * @param[return] value - The current value of this Calculator + // + fn get_value(&mut self) -> u64 { + unimplemented!(); + } + + // set_value + // + // Sets the value to be factorized + // * @param[in] value - The value to be factorized + // + fn set_value(&mut self, _value : u64) { + unimplemented!(); + } + + // calculate + // + // Performs the specific calculation of this Calculator + // + fn calculate(&mut self) { + unimplemented!(); + } + + // set_progress_callback + // + // Sets the progress callback function + // * @param[in] progress_callback - The progress callback + // + fn set_progress_callback(&mut self, _progress_callback : ProgressCallback) { + unimplemented!(); + } +} + diff --git a/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_factorization_calculator.rs b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_factorization_calculator.rs new file mode 100644 index 00000000..149c9da5 --- /dev/null +++ b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_factorization_calculator.rs @@ -0,0 +1,115 @@ +/*++ + +Copyright (C) 2019 PrimeDevelopers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.0-develop. + +Abstract: This is an autogenerated Rust implementation file in order to allow easy +development of Prime Numbers Library. It needs to be generated only once. + +Interface version: 1.2.0 + +*/ + + +use libprimes_interfaces::*; +use libprimes_calculator::CCalculator; + +// Stub struct to implement the FactorizationCalculator trait +pub struct CFactorizationCalculator { + parent : CCalculator +} + +// Implementation of parent traits via parent + +impl Calculator for CFactorizationCalculator { + + // get_value + // + // Returns the current value of this Calculator + // * @param[return] value - The current value of this Calculator + // + fn get_value(&mut self) -> u64 { + self.parent.get_value() + } + + // set_value + // + // Sets the value to be factorized + // * @param[in] value - The value to be factorized + // + fn set_value(&mut self, value : u64) { + self.parent.set_value(value) + } + + // calculate + // + // Performs the specific calculation of this Calculator + // + fn calculate(&mut self) { + self.parent.calculate() + } + + // set_progress_callback + // + // Sets the progress callback function + // * @param[in] progress_callback - The progress callback + // + fn set_progress_callback(&mut self, progress_callback : ProgressCallback) { + self.parent.set_progress_callback(progress_callback) + } +} +impl Base for CFactorizationCalculator { + + // class_type_id + // + // Get Class Type Id + // * @param[return] class_type_id - Class type as a 64 bits integer + // + fn class_type_id(&mut self) -> u64 { + self.parent.class_type_id() + } + + // get_last_error_message + // + // Returns the last error registered of this class instance + // * @param[out] error_message - Message of the last error registered + // * @param[return] has_last_error - Has an error been registered already + // + fn get_last_error_message(&mut self, error_message : &mut String) -> bool { + self.parent.get_last_error_message(error_message) + } + + // clear_error_messages + // + // Clears all registered messages of this class instance + // + fn clear_error_messages(&mut self) { + self.parent.clear_error_messages() + } + + // register_error_message + // + // Registers an error message with this class instance + // * @param[in] error_message - Error message to register + // + fn register_error_message(&mut self, error_message : &str) { + self.parent.register_error_message(error_message) + } +} + +impl FactorizationCalculator for CFactorizationCalculator { + + + // get_prime_factors + // + // Returns the prime factors of this number (without multiplicity) + // * @param[out] prime_factors - The prime factors of this number + // + fn get_prime_factors(&mut self, _prime_factors : &mut Vec) { + unimplemented!(); + } +} + diff --git a/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_sieve_calculator.rs b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_sieve_calculator.rs new file mode 100644 index 00000000..fccd0480 --- /dev/null +++ b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_sieve_calculator.rs @@ -0,0 +1,115 @@ +/*++ + +Copyright (C) 2019 PrimeDevelopers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.0-develop. + +Abstract: This is an autogenerated Rust implementation file in order to allow easy +development of Prime Numbers Library. It needs to be generated only once. + +Interface version: 1.2.0 + +*/ + + +use libprimes_interfaces::*; +use libprimes_calculator::CCalculator; + +// Stub struct to implement the SieveCalculator trait +pub struct CSieveCalculator { + parent : CCalculator +} + +// Implementation of parent traits via parent + +impl Calculator for CSieveCalculator { + + // get_value + // + // Returns the current value of this Calculator + // * @param[return] value - The current value of this Calculator + // + fn get_value(&mut self) -> u64 { + self.parent.get_value() + } + + // set_value + // + // Sets the value to be factorized + // * @param[in] value - The value to be factorized + // + fn set_value(&mut self, value : u64) { + self.parent.set_value(value) + } + + // calculate + // + // Performs the specific calculation of this Calculator + // + fn calculate(&mut self) { + self.parent.calculate() + } + + // set_progress_callback + // + // Sets the progress callback function + // * @param[in] progress_callback - The progress callback + // + fn set_progress_callback(&mut self, progress_callback : ProgressCallback) { + self.parent.set_progress_callback(progress_callback) + } +} +impl Base for CSieveCalculator { + + // class_type_id + // + // Get Class Type Id + // * @param[return] class_type_id - Class type as a 64 bits integer + // + fn class_type_id(&mut self) -> u64 { + self.parent.class_type_id() + } + + // get_last_error_message + // + // Returns the last error registered of this class instance + // * @param[out] error_message - Message of the last error registered + // * @param[return] has_last_error - Has an error been registered already + // + fn get_last_error_message(&mut self, error_message : &mut String) -> bool { + self.parent.get_last_error_message(error_message) + } + + // clear_error_messages + // + // Clears all registered messages of this class instance + // + fn clear_error_messages(&mut self) { + self.parent.clear_error_messages() + } + + // register_error_message + // + // Registers an error message with this class instance + // * @param[in] error_message - Error message to register + // + fn register_error_message(&mut self, error_message : &str) { + self.parent.register_error_message(error_message) + } +} + +impl SieveCalculator for CSieveCalculator { + + + // get_primes + // + // Returns all prime numbers lower or equal to the sieve's value + // * @param[out] primes - The primes lower or equal to the sieve's value + // + fn get_primes(&mut self, _primes : &mut Vec) { + unimplemented!(); + } +} + diff --git a/Examples/Primes/LibPrimes_component/Implementations/Rust/lib.rs b/Examples/Primes/LibPrimes_component/Implementations/Rust/lib.rs new file mode 100644 index 00000000..629c58a9 --- /dev/null +++ b/Examples/Primes/LibPrimes_component/Implementations/Rust/lib.rs @@ -0,0 +1,44 @@ +/*++ + +Copyright (C) 2019 PrimeDevelopers + +All rights reserved. + +This file has been generated by the Automatic Component Toolkit (ACT) version 1.8.0-develop. + +Abstract: This is an autogenerated Rust implementation file in order to allow easy +development of Prime Numbers Library. It needs to be generated only once. + +Interface version: 1.2.0 + +*/ + + +#![feature(trait_upcasting)] +#![allow(incomplete_features)] +#![feature(vec_into_raw_parts)] + +#[path = "Interfaces/libprimes_interfaces.rs"] +mod libprimes_interfaces; + +#[path = "Interfaces/libprimes_interface_wrapper.rs"] +mod libprimes_interface_wrapper; + +#[path = "Interfaces/libprimes_interface_handle.rs"] +mod libprimes_interface_handle; + +#[path = "Stub/libprimes.rs"] +mod libprimes; + +#[path = "Stub/libprimes_base.rs"] +mod libprimes_base; + +#[path = "Stub/libprimes_calculator.rs"] +mod libprimes_calculator; + +#[path = "Stub/libprimes_factorization_calculator.rs"] +mod libprimes_factorization_calculator; + +#[path = "Stub/libprimes_sieve_calculator.rs"] +mod libprimes_sieve_calculator; + From edd7b418da3636f8f50ef28b4a46769c5d533b64 Mon Sep 17 00:00:00 2001 From: Robert Goss Date: Sat, 30 Sep 2023 01:09:56 +0100 Subject: [PATCH 25/26] Add primes implementation to test --- .../Interfaces/libprimes_interface_wrapper.rs | 4 +-- .../Rust/Interfaces/libprimes_interfaces.rs | 16 +-------- .../Implementations/Rust/Stub/libprimes.rs | 32 +++++------------ .../Rust/Stub/libprimes_base.rs | 10 +++++- .../Rust/Stub/libprimes_calculator.rs | 34 +++++++++++++++--- .../libprimes_factorization_calculator.rs | 36 ++++++++++++++++--- .../Rust/Stub/libprimes_sieve_calculator.rs | 35 +++++++++++++++--- 7 files changed, 113 insertions(+), 54 deletions(-) diff --git a/Examples/Primes/LibPrimes_component/Implementations/Rust/Interfaces/libprimes_interface_wrapper.rs b/Examples/Primes/LibPrimes_component/Implementations/Rust/Interfaces/libprimes_interface_wrapper.rs index 880f6e82..12baa7af 100644 --- a/Examples/Primes/LibPrimes_component/Implementations/Rust/Interfaces/libprimes_interface_wrapper.rs +++ b/Examples/Primes/LibPrimes_component/Implementations/Rust/Interfaces/libprimes_interface_wrapper.rs @@ -46,8 +46,8 @@ pub fn libprimes_getversion(major : *mut u32, minor : *mut u32, micro : *mut u32 pub fn libprimes_getlasterror(instance : BaseHandle, error_message_buffer_size : usize, error_message_needed_chars : *mut usize, error_message_buffer : *mut u8, has_error : *mut u8) -> i32 { // Convert parameter instance to be used as an argument if instance.is_null() { return LIBPRIMES_ERROR_INVALIDPARAM; } - let _handle_instance = unsafe {&*instance}; - let _optional_instance = _handle_instance.as_base(); + let _handle_instance = unsafe {&mut *instance}; + let _optional_instance = _handle_instance.as_mut_base(); if _optional_instance.is_none() { return LIBPRIMES_ERROR_INVALIDPARAM; } let _instance = _optional_instance.unwrap(); diff --git a/Examples/Primes/LibPrimes_component/Implementations/Rust/Interfaces/libprimes_interfaces.rs b/Examples/Primes/LibPrimes_component/Implementations/Rust/Interfaces/libprimes_interfaces.rs index 08d8b6d3..fcdfa51d 100644 --- a/Examples/Primes/LibPrimes_component/Implementations/Rust/Interfaces/libprimes_interfaces.rs +++ b/Examples/Primes/LibPrimes_component/Implementations/Rust/Interfaces/libprimes_interfaces.rs @@ -222,21 +222,7 @@ pub trait Wrapper { // * @param[out] error_message - Message of the last error // * @param[return] has_error - Is there a last error to query // - fn get_last_error(instance : & dyn Base, error_message : &mut String) -> bool; - - // acquire_instance - // - // Acquire shared ownership of an Instance - // * @param[in] instance - Instance Handle - // - fn acquire_instance(instance : & dyn Base); - - // release_instance - // - // Releases shared ownership of an Instance - // * @param[in] instance - Instance Handle - // - fn release_instance(instance : & dyn Base); + fn get_last_error(instance : &mut dyn Base, error_message : &mut String) -> bool; // create_factorization_calculator // diff --git a/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes.rs b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes.rs index 1fa0a5ef..ed68033e 100644 --- a/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes.rs +++ b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes.rs @@ -15,6 +15,8 @@ Interface version: 1.2.0 use libprimes_interfaces::*; +use libprimes_sieve_calculator::CSieveCalculator; +use libprimes_factorization_calculator::CFactorizationCalculator; // Wrapper struct to implement the wrapper trait for global methods pub struct CWrapper; @@ -30,7 +32,9 @@ impl Wrapper for CWrapper { // * @param[out] micro - returns the micro version of this library // fn get_version(_major : &mut u32, _minor : &mut u32, _micro : &mut u32) { - unimplemented!(); + *_major = 1; + *_minor = 1; + *_micro = 1; } // get_last_error @@ -40,26 +44,8 @@ impl Wrapper for CWrapper { // * @param[out] error_message - Message of the last error // * @param[return] has_error - Is there a last error to query // - fn get_last_error(_instance : & dyn Base, _error_message : &mut String) -> bool { - unimplemented!(); - } - - // acquire_instance - // - // Acquire shared ownership of an Instance - // * @param[in] instance - Instance Handle - // - fn acquire_instance(_instance : & dyn Base) { - unimplemented!(); - } - - // release_instance - // - // Releases shared ownership of an Instance - // * @param[in] instance - Instance Handle - // - fn release_instance(_instance : & dyn Base) { - unimplemented!(); + fn get_last_error(_instance : &mut dyn Base, _error_message : &mut String) -> bool { + _instance.get_last_error_message(_error_message) } // create_factorization_calculator @@ -68,7 +54,7 @@ impl Wrapper for CWrapper { // * @param[return] instance - New FactorizationCalculator instance // fn create_factorization_calculator() -> Box { - unimplemented!(); + Box::new(CFactorizationCalculator::new()) } // create_sieve_calculator @@ -77,7 +63,7 @@ impl Wrapper for CWrapper { // * @param[return] instance - New SieveCalculator instance // fn create_sieve_calculator() -> Box { - unimplemented!(); + Box::new(CSieveCalculator::new()) } // set_journal diff --git a/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_base.rs b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_base.rs index 4cb747bd..67541471 100644 --- a/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_base.rs +++ b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_base.rs @@ -60,7 +60,15 @@ impl Base for CBase { // * @param[return] class_type_id - Class type as a 64 bits integer // fn class_type_id(&mut self) -> u64 { - unimplemented!(); + 0 } } + +impl CBase { + pub fn new() -> CBase { + CBase { + last_error : None + } + } +} \ No newline at end of file diff --git a/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_calculator.rs b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_calculator.rs index f2167a55..762fadbe 100644 --- a/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_calculator.rs +++ b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_calculator.rs @@ -19,7 +19,9 @@ use libprimes_base::CBase; // Stub struct to implement the Calculator trait pub struct CCalculator { - parent : CBase + parent : CBase, + value : u64, + progress_callback : ProgressCallback } // Implementation of parent traits via parent @@ -32,7 +34,7 @@ impl Base for CCalculator { // * @param[return] class_type_id - Class type as a 64 bits integer // fn class_type_id(&mut self) -> u64 { - self.parent.class_type_id() + 1 } // get_last_error_message @@ -72,7 +74,7 @@ impl Calculator for CCalculator { // * @param[return] value - The current value of this Calculator // fn get_value(&mut self) -> u64 { - unimplemented!(); + self.value } // set_value @@ -81,7 +83,7 @@ impl Calculator for CCalculator { // * @param[in] value - The value to be factorized // fn set_value(&mut self, _value : u64) { - unimplemented!(); + self.value = _value; } // calculate @@ -98,7 +100,29 @@ impl Calculator for CCalculator { // * @param[in] progress_callback - The progress callback // fn set_progress_callback(&mut self, _progress_callback : ProgressCallback) { - unimplemented!(); + self.progress_callback = _progress_callback } } + +unsafe extern "C" fn dummy_progress(_par : f32, _val : *mut u8) { + +} + +impl CCalculator { + pub fn new() -> CCalculator { + CCalculator { + parent : CBase::new(), + value : 0, + progress_callback : dummy_progress + } + } + + pub fn progress_abort(&self) -> bool { + let mut res : u8 = 0; + unsafe { + (self.progress_callback)(0_f32, &mut res as *mut u8) + } + res != 0 + } +} \ No newline at end of file diff --git a/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_factorization_calculator.rs b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_factorization_calculator.rs index 149c9da5..2a850705 100644 --- a/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_factorization_calculator.rs +++ b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_factorization_calculator.rs @@ -19,7 +19,8 @@ use libprimes_calculator::CCalculator; // Stub struct to implement the FactorizationCalculator trait pub struct CFactorizationCalculator { - parent : CCalculator + parent : CCalculator, + prime_factors : Vec } // Implementation of parent traits via parent @@ -49,7 +50,26 @@ impl Calculator for CFactorizationCalculator { // Performs the specific calculation of this Calculator // fn calculate(&mut self) { - self.parent.calculate() + self.prime_factors.clear(); + let mut val = self.get_value(); + let mut n = 0_u64; + while n < val { + if self.parent.progress_abort() { + return; + } + let mut prime_factor = PrimeFactor { + prime : n, + multiplicity : 0 + }; + while val % n == 0 { + val = val / n; + prime_factor.multiplicity += 1; + } + if prime_factor.multiplicity > 0 { + self.prime_factors.push(prime_factor) + } + n += 1 + } } // set_progress_callback @@ -69,7 +89,7 @@ impl Base for CFactorizationCalculator { // * @param[return] class_type_id - Class type as a 64 bits integer // fn class_type_id(&mut self) -> u64 { - self.parent.class_type_id() + 2 } // get_last_error_message @@ -109,7 +129,15 @@ impl FactorizationCalculator for CFactorizationCalculator { // * @param[out] prime_factors - The prime factors of this number // fn get_prime_factors(&mut self, _prime_factors : &mut Vec) { - unimplemented!(); + *_prime_factors = self.prime_factors.clone() } } +impl CFactorizationCalculator { + pub fn new() -> CFactorizationCalculator { + CFactorizationCalculator { + parent : CCalculator::new(), + prime_factors : Vec::new() + } + } +} \ No newline at end of file diff --git a/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_sieve_calculator.rs b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_sieve_calculator.rs index fccd0480..7e2c515f 100644 --- a/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_sieve_calculator.rs +++ b/Examples/Primes/LibPrimes_component/Implementations/Rust/Stub/libprimes_sieve_calculator.rs @@ -19,7 +19,8 @@ use libprimes_calculator::CCalculator; // Stub struct to implement the SieveCalculator trait pub struct CSieveCalculator { - parent : CCalculator + parent : CCalculator, + primes : Vec } // Implementation of parent traits via parent @@ -49,7 +50,25 @@ impl Calculator for CSieveCalculator { // Performs the specific calculation of this Calculator // fn calculate(&mut self) { - self.parent.calculate() + self.primes.clear(); + let val = self.get_value(); + let mut sieved : Vec = vec![false; (val+1) as usize]; + sieved[0] = true; + sieved[1] = true; + let val_sqrt = (val as f64).sqrt() as u64 + 1; + for i in 2_u64..val_sqrt { + if self.parent.progress_abort() { + return + } + if !sieved[i as usize] { + self.primes.push(i); + let mut mul : u64 = i*i; + while mul <= val { + sieved[mul as usize] = true; + mul *= i; + } + } + } } // set_progress_callback @@ -69,7 +88,7 @@ impl Base for CSieveCalculator { // * @param[return] class_type_id - Class type as a 64 bits integer // fn class_type_id(&mut self) -> u64 { - self.parent.class_type_id() + 3 } // get_last_error_message @@ -109,7 +128,15 @@ impl SieveCalculator for CSieveCalculator { // * @param[out] primes - The primes lower or equal to the sieve's value // fn get_primes(&mut self, _primes : &mut Vec) { - unimplemented!(); + *_primes = self.primes.clone() } } +impl CSieveCalculator { + pub fn new() -> CSieveCalculator { + CSieveCalculator { + parent : CCalculator::new(), + primes : Vec::new() + } + } +} \ No newline at end of file From 15e1a3b8631c427eb1f514f819cc53435c6136b2 Mon Sep 17 00:00:00 2001 From: Robert Goss Date: Mon, 2 Oct 2023 00:01:00 +0100 Subject: [PATCH 26/26] Avoid CutPrefix not available in all go versions --- Source/buildimplementationrust.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/buildimplementationrust.go b/Source/buildimplementationrust.go index f5980f9c..1aaf437f 100644 --- a/Source/buildimplementationrust.go +++ b/Source/buildimplementationrust.go @@ -245,7 +245,9 @@ func buildRustInterfaces(component ComponentDefinition, w LanguageWriter) error func buildCargoForRustImplementation(component ComponentDefinition, w LanguageWriter, path string) error { projectName := strings.ToLower(component.NameSpace) - projectName, _ = strings.CutPrefix(projectName, "lib") + if strings.HasPrefix(projectName, "lib") { + projectName = projectName[3:] + } w.Writeln("[package]") w.Writeln(" name = \"%s\"", projectName) w.Writeln(" version = \"0.1.0\"")