2024-08-06 01:03:23 +00:00
package main
import (
2024-08-15 07:53:01 +00:00
"C"
2024-08-07 06:51:51 +00:00
"go/format"
"log"
2024-08-09 22:28:02 +00:00
"sort"
2024-08-06 02:29:12 +00:00
"strings"
2024-08-18 07:01:36 +00:00
"unsafe"
2024-08-06 01:03:23 +00:00
)
2024-08-15 07:49:38 +00:00
func goReservedWord ( s string ) bool {
switch s {
2024-08-19 07:42:34 +00:00
case "default" , "const" , "func" , "var" , "type" , "len" , "new" , "copy" , "import" , "range" , "string" , "map" , "int" , "select" :
2024-08-15 07:49:38 +00:00
return true
default :
return false
}
}
2024-08-08 06:54:13 +00:00
func ( p CppParameter ) RenderTypeGo ( ) string {
if p . Pointer && p . ParameterType == "char" {
return "string"
}
2024-08-15 07:49:05 +00:00
if p . ParameterType == "char" {
return "byte"
}
2024-08-08 06:54:13 +00:00
if p . ParameterType == "QString" {
return "string"
}
2024-08-11 04:54:23 +00:00
if p . ParameterType == "uintptr_t" {
return "uintptr"
}
2024-08-08 06:54:13 +00:00
2024-08-11 04:37:18 +00:00
if t , ok := p . QListOf ( ) ; ok {
return "[]" + t . RenderTypeGo ( )
}
2024-08-08 06:54:13 +00:00
ret := ""
if p . ByRef || p . Pointer {
ret += "*"
}
2024-08-16 23:27:02 +00:00
switch p . ParameterType {
2024-08-17 02:10:17 +00:00
case "char" , "qint8" , "unsigned char" , "uchar" , "quint8" :
ret += "byte" // Strictly speaking, Go byte is unsigned and char may be signed
2024-08-16 23:27:02 +00:00
case "short" , "qint16" :
ret += "int16"
2024-08-19 07:12:37 +00:00
case "ushort" , "quint16" , "unsigned short" :
2024-08-16 23:27:02 +00:00
ret += "uint16"
case "long" :
// Windows ILP32 - 32-bits
// Linux LP64 - 64-bits
if C . sizeof_long == 4 {
ret += "int32"
} else {
ret += "int64"
}
case "ulong" , "unsigned long" :
if C . sizeof_long == 4 {
ret += "uint32"
} else {
ret += "uint64"
}
case "QRgb" :
if C . sizeof_int == 4 {
ret += "uint32"
} else {
ret += "uint64"
}
2024-08-19 07:12:37 +00:00
case "unsigned int" :
return "uint"
2024-08-16 23:27:02 +00:00
case "qint32" :
ret += "int32"
case "quint32" :
ret += "uint32"
2024-08-19 07:12:37 +00:00
case "qlonglong" , "qint64" , "long long" :
2024-08-16 23:27:02 +00:00
ret += "int64"
2024-08-19 07:12:37 +00:00
case "qulonglong" , "quint64" , "unsigned long long" :
2024-08-16 23:27:02 +00:00
ret += "uint64"
case "float" :
ret += "float32"
2024-08-18 06:56:09 +00:00
case "double" , "qreal" :
2024-08-16 23:27:02 +00:00
ret += "float64"
2024-08-23 23:45:36 +00:00
case "qsizetype" , "size_t" :
2024-08-17 02:10:17 +00:00
if C . sizeof_size_t == 4 {
ret += "uint32"
} else {
ret += "uint64"
}
2024-08-18 07:01:36 +00:00
case "qintptr" :
2024-08-23 23:45:36 +00:00
var ptr * int
if unsafe . Sizeof ( ptr ) == 8 {
ret += "int64"
} else {
ret += "int32"
}
case "quintptr" :
2024-08-18 07:01:36 +00:00
var ptr * int
if unsafe . Sizeof ( ptr ) == 8 {
ret += "uint64"
} else {
ret += "uint32"
}
2024-08-16 23:27:02 +00:00
default :
// Do not transform this type
ret += p . ParameterType
}
2024-08-08 06:54:13 +00:00
return ret // ignore const
}
2024-08-16 23:28:49 +00:00
func ( p CppParameter ) parameterTypeCgo ( ) string {
2024-08-17 00:39:45 +00:00
if p . ParameterType == "QString" {
return "C.char"
}
2024-08-20 08:19:38 +00:00
tmp := strings . Replace ( p . RenderTypeCabi ( ) , ` * ` , "" , - 1 )
2024-08-16 23:28:49 +00:00
if strings . HasPrefix ( tmp , "unsigned " ) {
tmp = "u" + tmp [ 9 : ] // Cgo uses uchar, uint instead of full name
}
2024-08-23 23:45:36 +00:00
tmp = strings . Replace ( tmp , ` long long ` , ` longlong ` , - 1 )
2024-08-16 23:28:49 +00:00
return "C." + strings . Replace ( tmp , " " , "_" , - 1 )
}
2024-08-07 06:56:14 +00:00
func emitParametersGo ( params [ ] CppParameter ) string {
2024-08-06 02:29:12 +00:00
tmp := make ( [ ] string , 0 , len ( params ) )
2024-08-17 02:10:59 +00:00
2024-08-18 03:24:04 +00:00
skipNext := false
2024-08-17 02:10:59 +00:00
for i , p := range params {
2024-08-18 03:57:29 +00:00
if IsArgcArgv ( params , i ) {
2024-08-18 03:24:04 +00:00
skipNext = true
2024-08-17 02:10:59 +00:00
tmp = append ( tmp , "args []string" )
2024-08-18 03:24:04 +00:00
} else if skipNext {
2024-08-17 02:10:59 +00:00
// Skip this parameter, already handled
} else {
// Ordinary parameter
tmp = append ( tmp , p . ParameterName + " " + p . RenderTypeGo ( ) )
}
2024-08-06 02:29:12 +00:00
}
return strings . Join ( tmp , ", " )
}
2024-08-23 23:46:47 +00:00
type goFileState struct {
imports map [ string ] struct { }
}
func ( gfs * goFileState ) emitParametersGo2CABIForwarding ( m CppMethod ) ( preamble string , fowarding string ) {
2024-08-09 22:33:12 +00:00
tmp := make ( [ ] string , 0 , len ( m . Parameters ) + 2 )
2024-08-18 03:24:04 +00:00
if ! m . IsStatic {
tmp = append ( tmp , "this.h" )
}
2024-08-09 06:41:29 +00:00
2024-08-18 03:24:04 +00:00
skipNext := false
2024-08-17 02:10:59 +00:00
for i , p := range m . Parameters {
2024-08-18 03:57:29 +00:00
if IsArgcArgv ( m . Parameters , i ) {
2024-08-18 03:24:04 +00:00
skipNext = true
2024-08-17 02:10:59 +00:00
// QApplication constructor. Convert 'args' into Qt's wanted types
// Qt has a warning in the docs saying these pointers must be valid
// for the entire lifetype of QApplication, so, malloc + never free
preamble += "// Convert []string to long-lived int& argc, char** argv, never call free()\n"
preamble += "argc := (*C.int)(C.malloc(8))\n"
preamble += "*argc = len(args)\n"
preamble += "argv := (*[0xffff]*C.char)(C.malloc(c.ulong(8 * len(args))))\n"
preamble += "for i := range args {\n"
preamble += "argv[i] = C.CString(" + p . ParameterName + "[i])\n"
preamble += "}\n"
tmp = append ( tmp , "argc, argv" )
2024-08-18 03:24:04 +00:00
} else if skipNext {
2024-08-17 02:10:59 +00:00
// Skip this parameter, already handled
2024-08-18 03:24:04 +00:00
skipNext = false
2024-08-17 02:10:59 +00:00
} else if p . ParameterType == "QString" {
2024-08-08 06:54:13 +00:00
// Go: convert string -> char* and len
// CABI: convert char* and len -> real QString
2024-08-23 23:46:47 +00:00
gfs . imports [ "unsafe" ] = struct { } { }
2024-08-08 06:54:13 +00:00
preamble += p . ParameterName + "_Cstring := C.CString(" + p . ParameterName + ")\n"
2024-08-23 23:46:47 +00:00
preamble += "defer C.free(unsafe.Pointer(" + p . ParameterName + "_Cstring))\n"
tmp = append ( tmp , p . ParameterName + "_Cstring, C.ulong(len(" + p . ParameterName + "))" ) // Second parameter cast to size_t projected type
2024-08-08 06:54:13 +00:00
2024-08-11 04:37:18 +00:00
} else if listType , ok := p . QListOf ( ) ; ok {
// QList<T>
// Go: convert T[] -> t* and len
// CABI: create a real QList<>
2024-08-15 07:50:30 +00:00
if listType . ParameterType == "QString" {
// Combo
2024-08-23 23:46:47 +00:00
gfs . imports [ "unsafe" ] = struct { } { }
2024-08-11 04:37:18 +00:00
2024-08-15 07:50:30 +00:00
preamble += "// For the C ABI, malloc two C arrays; raw char* pointers and their lengths\n"
2024-08-16 23:28:49 +00:00
preamble += p . ParameterName + "_CArray := (*[0xffff]*" + listType . parameterTypeCgo ( ) + ")(C.malloc(c.ulong(8 * len(" + p . ParameterName + "))))\n"
preamble += p . ParameterName + "_Lengths := (*[0xffff]*C.size_t)(C.malloc(c.ulong(8 * len(" + p . ParameterName + "))))\n"
2024-08-15 07:50:30 +00:00
preamble += "defer C.free(" + p . ParameterName + "_CArray)\n"
preamble += "defer C.free(" + p . ParameterName + "_Lengths)\n"
preamble += "for i := range " + p . ParameterName + "{\n"
preamble += "single_cstring := C.CString(" + p . ParameterName + "[i])\n"
2024-08-23 23:46:47 +00:00
preamble += "defer C.free(unsafe.Pointer(single_cstring))\n"
2024-08-15 07:50:30 +00:00
preamble += p . ParameterName + "_CArray[i] = single_cstring\n"
preamble += p . ParameterName + "__Lengths[i] = len(" + p . ParameterName + "[i])\n"
preamble += "}\n"
2024-08-11 04:37:18 +00:00
2024-08-15 07:50:30 +00:00
tmp = append ( tmp , p . ParameterName + "_CArray, " + p . ParameterName + "_Lengths, len(" + p . ParameterName + ")" )
} else {
preamble += "// For the C ABI, malloc a C array of raw pointers\n"
2024-08-16 23:28:49 +00:00
preamble += p . ParameterName + "_CArray := (*[0xffff]*" + listType . parameterTypeCgo ( ) + ")(C.malloc(c.ulong(8 * len(" + p . ParameterName + "))))\n"
2024-08-15 07:50:30 +00:00
preamble += "defer C.free(" + p . ParameterName + "_CArray)\n"
preamble += "for i := range " + p . ParameterName + "{\n"
preamble += p . ParameterName + "_CArray[i] = " + p . ParameterName + "[i].cPointer()\n"
preamble += "}\n"
tmp = append ( tmp , p . ParameterName + "_CArray, len(" + p . ParameterName + ")" )
}
2024-08-11 04:37:18 +00:00
2024-08-08 06:54:13 +00:00
} else if p . Pointer && p . ParameterType == "char" {
// Single char* argument
2024-08-23 23:46:47 +00:00
gfs . imports [ "unsafe" ] = struct { } { }
2024-08-08 06:54:13 +00:00
preamble += p . ParameterName + "_Cstring := C.CString(" + p . ParameterName + ")\n"
2024-08-23 23:46:47 +00:00
preamble += "defer C.free(unsafe.Pointer(" + p . ParameterName + "_Cstring))\n"
2024-08-08 06:54:13 +00:00
tmp = append ( tmp , p . ParameterName + "_Cstring" )
2024-08-08 07:06:14 +00:00
} else if ( p . Pointer || p . ByRef ) && p . QtClassType ( ) {
2024-08-08 06:54:13 +00:00
// The C++ type is a pointer to Qt class
// We want our functions to accept the Go wrapper type, and forward as cPointer()
tmp = append ( tmp , p . ParameterName + ".cPointer()" )
2024-08-23 23:45:36 +00:00
} else if p . IntType ( ) || p . ParameterType == "bool" {
tmp = append ( tmp , "(" + p . parameterTypeCgo ( ) + ")(" + p . ParameterName + ")" )
2024-08-23 23:46:47 +00:00
2024-08-08 06:54:13 +00:00
} else {
// Default
tmp = append ( tmp , p . ParameterName )
}
}
2024-08-09 06:41:29 +00:00
if m . ReturnType . ParameterType == "QString" {
tmp = append ( tmp , "&_out, &_out_Strlen" )
2024-08-11 04:37:18 +00:00
} else if _ , ok := m . ReturnType . QListOf ( ) ; ok {
tmp = append ( tmp , "&_out, &_out_len" )
2024-08-09 06:41:29 +00:00
}
2024-08-08 06:54:13 +00:00
return preamble , strings . Join ( tmp , ", " )
}
2024-08-08 06:54:36 +00:00
func emitGo ( src * CppParsedHeader , headerName string ) ( string , error ) {
2024-08-07 06:51:51 +00:00
2024-08-06 02:29:12 +00:00
ret := strings . Builder { }
2024-08-18 05:48:17 +00:00
ret . WriteString ( ` package qt
2024-08-06 02:29:12 +00:00
2024-08-07 06:51:51 +00:00
/ *
2024-08-06 02:29:12 +00:00
2024-08-07 06:51:51 +00:00
# cgo CFLAGS : - fPIC
# cgo pkg - config : Qt5Widgets
2024-08-08 06:54:36 +00:00
# include "gen_` + headerName + `"
# include < stdlib . h >
2024-08-06 02:29:12 +00:00
2024-08-07 06:51:51 +00:00
* /
import "C"
2024-08-06 02:29:12 +00:00
2024-08-09 22:28:02 +00:00
% % _IMPORTLIBS_ % %
2024-08-06 02:29:12 +00:00
` )
2024-08-23 23:46:47 +00:00
gfs := goFileState {
imports : map [ string ] struct { } { } ,
}
2024-08-09 22:28:02 +00:00
2024-08-07 06:56:14 +00:00
for _ , c := range src . Classes {
2024-08-06 02:29:12 +00:00
2024-08-07 06:51:51 +00:00
ret . WriteString ( `
2024-08-07 06:56:14 +00:00
type ` + c.ClassName + ` struct {
2024-08-14 05:31:10 +00:00
h * C . ` + c.ClassName + `
2024-08-10 00:54:26 +00:00
` )
// Embed all inherited types to directly allow calling inherited methods
for _ , base := range c . Inherits {
2024-08-23 23:46:47 +00:00
ret . WriteString ( "*" + base + "\n" )
2024-08-10 00:54:26 +00:00
}
ret . WriteString ( `
2024-08-07 06:51:51 +00:00
}
2024-08-14 05:31:10 +00:00
func ( this * ` + c.ClassName + ` ) cPointer ( ) * C . ` + c.ClassName + ` {
2024-08-07 06:51:51 +00:00
if this == nil {
return nil
2024-08-06 02:29:12 +00:00
}
2024-08-07 06:51:51 +00:00
return this . h
2024-08-06 02:29:12 +00:00
}
2024-08-07 06:51:51 +00:00
` )
2024-08-06 02:29:12 +00:00
2024-08-23 23:46:47 +00:00
localInit := "h: h"
2024-08-10 00:54:26 +00:00
for _ , base := range c . Inherits {
2024-08-23 23:46:47 +00:00
gfs . imports [ "unsafe" ] = struct { } { }
localInit += ", " + base + ": new" + base + "_U(unsafe.Pointer(h))"
2024-08-10 00:54:26 +00:00
}
2024-08-11 04:37:06 +00:00
ret . WriteString ( `
2024-08-23 23:46:47 +00:00
func new ` + c.ClassName + ` ( h * C . ` + c.ClassName + ` ) * ` + c.ClassName + ` {
2024-08-11 04:37:06 +00:00
return & ` + c.ClassName + ` { ` + localInit + ` }
}
` )
2024-08-23 23:46:47 +00:00
// CGO types only exist within the same Go file, so other Go files can't
// call this same private ctor function, unless it goes through unsafe.Pointer{}
gfs . imports [ "unsafe" ] = struct { } { }
ret . WriteString ( `
func new ` + c.ClassName + ` _U ( h unsafe . Pointer ) * ` + c.ClassName + ` {
return new ` + c.ClassName + ` ( ( * C . ` + c.ClassName + ` ) ( h ) )
}
` )
2024-08-07 06:56:14 +00:00
for i , ctor := range c . Ctors {
2024-08-23 23:46:47 +00:00
preamble , forwarding := gfs . emitParametersGo2CABIForwarding ( ctor )
2024-08-07 06:51:51 +00:00
ret . WriteString ( `
2024-08-07 06:56:14 +00:00
// New` + c.ClassName + maybeSuffix(i) + ` constructs a new ` + c.ClassName + ` object.
2024-08-23 23:46:47 +00:00
func New ` + c.ClassName + maybeSuffix(i) + ` ( ` + emitParametersGo(ctor.Parameters) + ` ) * ` + c.ClassName + ` {
2024-08-08 06:54:13 +00:00
` + preamble + ` ret := C . ` + c.ClassName + ` _new ` + maybeSuffix(i) + ` ( ` + forwarding + ` )
2024-08-23 23:46:47 +00:00
return new ` + c.ClassName + ` ( ret )
2024-08-07 06:51:51 +00:00
}
` )
2024-08-06 02:29:12 +00:00
}
2024-08-07 06:56:14 +00:00
for _ , m := range c . Methods {
2024-08-23 23:46:47 +00:00
preamble , forwarding := gfs . emitParametersGo2CABIForwarding ( m )
2024-08-06 02:29:12 +00:00
2024-08-07 06:51:51 +00:00
shouldReturn := "return "
2024-08-09 06:41:29 +00:00
afterword := ""
2024-08-15 07:53:01 +00:00
returnTypeDecl := m . ReturnType . RenderTypeGo ( ) // FIXME handle byRef/const here too
2024-08-09 06:41:29 +00:00
2024-08-09 22:35:40 +00:00
if m . ReturnType . ParameterType == "void" && ! m . ReturnType . Pointer {
2024-08-07 06:51:51 +00:00
shouldReturn = ""
returnTypeDecl = ""
2024-08-06 02:29:12 +00:00
2024-08-09 23:46:07 +00:00
} else if m . ReturnType . ParameterType == "void" && m . ReturnType . Pointer {
returnTypeDecl = "interface{}"
2024-08-15 07:53:01 +00:00
} else if m . ReturnType . ParameterType == "char" && m . ReturnType . Pointer {
// Qt functions normally return QString - anything returning char*
// is something like QByteArray.Data() where it returns an unsafe
// internal pointer
2024-08-23 23:46:47 +00:00
gfs . imports [ "unsafe" ] = struct { } { }
2024-08-15 07:53:01 +00:00
returnTypeDecl = "unsafe.Pointer"
2024-08-09 06:41:29 +00:00
} else if m . ReturnType . ParameterType == "QString" {
shouldReturn = ""
returnTypeDecl = "string"
2024-08-23 23:46:47 +00:00
gfs . imports [ "unsafe" ] = struct { } { }
2024-08-09 06:41:29 +00:00
preamble += "var _out *C.char = nil\n"
2024-08-23 23:45:36 +00:00
preamble += "var _out_Strlen C.int = 0\n" // I think size_t is "better" but GoStringN() requires C.int
2024-08-09 06:41:29 +00:00
afterword += "ret := C.GoStringN(_out, _out_Strlen)\n"
2024-08-23 23:46:47 +00:00
afterword += "C.free(unsafe.Pointer(_out))\n"
2024-08-09 06:41:29 +00:00
afterword += "return ret"
2024-08-09 22:34:54 +00:00
2024-08-11 04:37:18 +00:00
} else if t , ok := m . ReturnType . QListOf ( ) ; ok {
shouldReturn = ""
returnTypeDecl = "[]" + t . RenderTypeGo ( )
2024-08-16 23:28:49 +00:00
preamble += "var _out **" + t . parameterTypeCgo ( ) + " = nil\n"
2024-08-11 04:37:18 +00:00
preamble += "var _out_len C.size_t = 0\n"
afterword += "ret := make([]" + t . RenderTypeGo ( ) + ", _out_Strlen)\n"
afterword += "for i := 0; i < _out_len; i++ {\n"
afterword += "ret[i] = new" + t . ParameterType + "(_out[i])\n"
afterword += "}\n"
afterword += "C.free(_out)\n"
2024-08-09 22:34:54 +00:00
} else if m . ReturnType . QtClassType ( ) {
// Construct our Go type based on this inner CABI type
shouldReturn = "ret := "
2024-08-23 23:46:47 +00:00
if m . ReturnType . Pointer || m . ReturnType . ParameterType == "QSize" { // FIXME QSize has a deleted destructor, so we can't delete our heap copy with a finalizer(!!)
gfs . imports [ "unsafe" ] = struct { } { }
afterword = "return new" + m . ReturnType . ParameterType + "_U(unsafe.Pointer(ret))"
2024-08-09 22:34:54 +00:00
} else {
// This is return by value, but CABI has new'd it into a
// heap type for us
// To preserve Qt's approximate semantics, add a runtime
// finalizer to automatically Delete once the type goes out
// of Go scope
2024-08-23 23:46:47 +00:00
returnTypeDecl = "*" + returnTypeDecl
gfs . imports [ "runtime" ] = struct { } { }
2024-08-09 22:34:54 +00:00
afterword = "// Qt uses pass-by-value semantics for this type. Mimic with finalizer\n"
2024-08-23 23:46:47 +00:00
afterword += "ret1 := new" + m . ReturnType . ParameterType + "(ret)\n"
2024-08-09 22:34:54 +00:00
afterword += "runtime.SetFinalizer(ret1, func(ret2 *" + m . ReturnType . ParameterType + ") {\n"
afterword += "ret2.Delete()\n"
afterword += "runtime.KeepAlive(ret2.h)\n"
afterword += "})\n"
2024-08-23 23:46:47 +00:00
afterword += "return ret1\n"
2024-08-09 22:34:54 +00:00
}
2024-08-23 23:45:36 +00:00
} else if m . ReturnType . IntType ( ) || m . ReturnType . ParameterType == "bool" {
// Need to cast Cgo type to Go int type
shouldReturn = "ret := "
afterword += "return (" + m . ReturnType . RenderTypeGo ( ) + ")(ret)\n"
2024-08-09 06:41:29 +00:00
}
2024-08-08 06:54:13 +00:00
2024-08-18 03:24:04 +00:00
receiverAndMethod := ` (this * ` + c . ClassName + ` ) ` + m . SafeMethodName ( )
if m . IsStatic {
receiverAndMethod = c . ClassName + ` _ ` + m . SafeMethodName ( )
}
2024-08-07 06:51:51 +00:00
ret . WriteString ( `
2024-08-18 03:24:04 +00:00
func ` + receiverAndMethod + ` ( ` + emitParametersGo(m.Parameters) + ` ) ` + returnTypeDecl + ` {
2024-08-09 06:41:29 +00:00
` + preamble +
shouldReturn + ` C. ` + c . ClassName + ` _ ` + m . SafeMethodName ( ) + ` ( ` + forwarding + ` )
` + afterword + ` }
2024-08-07 06:51:51 +00:00
` )
2024-08-18 05:48:17 +00:00
// Add Connect() wrappers for signal functions
2024-08-18 06:55:28 +00:00
if m . IsSignal && ! m . HasHiddenParams {
2024-08-23 23:46:47 +00:00
gfs . imports [ "unsafe" ] = struct { } { }
gfs . imports [ "runtime/cgo" ] = struct { } { }
2024-08-18 05:48:17 +00:00
ret . WriteString ( ` func (this * ` + c . ClassName + ` ) On ` + m . SafeMethodName ( ) + ` ( slot func ( ) ) {
var slotWrapper miqtCallbackFunc = func ( argc C . int , args * C . void ) {
slot ( )
}
C . ` + c.ClassName + ` _connect_ ` + m.SafeMethodName() + ` ( this . h , unsafe . Pointer ( uintptr ( cgo . NewHandle ( slotWrapper ) ) ) )
}
` )
}
2024-08-07 06:51:51 +00:00
}
2024-08-06 02:29:12 +00:00
2024-08-20 08:10:57 +00:00
if c . CanDelete {
2024-08-18 05:47:19 +00:00
ret . WriteString ( `
func ( this * ` + c.ClassName + ` ) Delete ( ) {
C . ` + c.ClassName + ` _Delete ( this . h )
}
` )
2024-08-09 22:32:57 +00:00
}
2024-08-07 06:51:51 +00:00
}
2024-08-06 02:29:12 +00:00
2024-08-09 22:28:02 +00:00
goSrc := ret . String ( )
// Fixup imports
2024-08-23 23:46:47 +00:00
if len ( gfs . imports ) > 0 {
allImports := make ( [ ] string , 0 , len ( gfs . imports ) )
for k , _ := range gfs . imports {
2024-08-09 22:28:02 +00:00
allImports = append ( allImports , ` " ` + k + ` " ` )
}
sort . Strings ( allImports )
goSrc = strings . Replace ( goSrc , ` %%_IMPORTLIBS_%% ` , "import (\n\t" + strings . Join ( allImports , "\n\t" ) + "\n)" , 1 )
} else {
goSrc = strings . Replace ( goSrc , ` %%_IMPORTLIBS_%% ` , "" , 1 )
}
2024-08-07 06:51:51 +00:00
// Run gofmt over the result
2024-08-09 22:28:02 +00:00
formattedSrc , err := format . Source ( [ ] byte ( goSrc ) )
2024-08-07 06:51:51 +00:00
if err != nil {
log . Printf ( "gofmt failure: %v" , err )
2024-08-10 02:04:52 +00:00
formattedSrc = [ ] byte ( goSrc )
2024-08-06 02:29:12 +00:00
}
2024-08-07 06:51:51 +00:00
return string ( formattedSrc ) , nil
2024-08-06 01:03:23 +00:00
}