github.com/status-im/status-go@v1.1.0/cmd/library/main.go (about)

     1  package main
     2  
     3  import (
     4  	"fmt"
     5  	"go/ast"
     6  	"go/parser"
     7  	"go/token"
     8  	"os"
     9  	"strings"
    10  	"unicode"
    11  )
    12  
    13  func isCodeFile(info os.FileInfo) bool {
    14  	return !strings.HasSuffix(info.Name(), "test.go")
    15  }
    16  
    17  func main() {
    18  	var output = prelude
    19  
    20  	fset := token.NewFileSet()
    21  
    22  	// Parse the whole `mobile/` directory, excluding test files
    23  	parsedAST, err := parser.ParseDir(fset, "mobile/", isCodeFile, parser.AllErrors)
    24  	if err != nil {
    25  		fmt.Printf("Error parsing directory: %+v\n", err)
    26  		os.Exit(1)
    27  	}
    28  
    29  	for _, a := range parsedAST {
    30  		for _, file := range a.Files {
    31  			// handle each file and append the output
    32  			output += handleFile(file)
    33  		}
    34  	}
    35  
    36  	// To free memory allocated to strings
    37  	output += "//export Free\n"
    38  	output += "func Free (param unsafe.Pointer){\n"
    39  	output += "C.free(param);\n"
    40  	output += "}\n"
    41  
    42  	fmt.Println(output)
    43  }
    44  
    45  func handleFunction(name string, funcDecl *ast.FuncDecl) string {
    46  	params := funcDecl.Type.Params.List
    47  	results := funcDecl.Type.Results
    48  
    49  	// add export tag
    50  	output := fmt.Sprintf("//export %s\n", name)
    51  	// add initial func declaration
    52  	output += fmt.Sprintf("func %s (", name)
    53  
    54  	// iterate over parameters and correctly add the C type
    55  	paramCount := 0
    56  	for _, p := range params {
    57  		for _, paramIdentity := range p.Names {
    58  			if paramCount != 0 {
    59  				output += ", "
    60  			}
    61  			paramCount++
    62  			output += paramIdentity.Name
    63  
    64  			typeString := fmt.Sprint(paramIdentity.Obj.Decl.(*ast.Field).Type)
    65  			// We match against the stringified type,
    66  			// could not find a better way to match this
    67  			switch typeString {
    68  			case stringType:
    69  				output += " *C.char"
    70  			case intType, boolType:
    71  				output += " C.int"
    72  			case unsafePointerType:
    73  				output += " unsafe.Pointer"
    74  			default:
    75  				// ignore if the type is any different
    76  				return ""
    77  			}
    78  		}
    79  	}
    80  
    81  	output += ")"
    82  
    83  	// check if it has a return value, convert to CString if so and return
    84  	if results != nil {
    85  		output += " *C.char {\nreturn C.CString("
    86  	} else {
    87  		output += " {\n"
    88  
    89  	}
    90  
    91  	// call the mobile equivalent function
    92  	output += fmt.Sprintf("mobile.%s(", name)
    93  
    94  	// iterate through the parameters, convert to go types and close
    95  	// the function call
    96  	paramCount = 0
    97  	for _, p := range params {
    98  		for _, paramIdentity := range p.Names {
    99  			if paramCount != 0 {
   100  				output += ", "
   101  			}
   102  			paramCount++
   103  			typeString := fmt.Sprint(paramIdentity.Obj.Decl.(*ast.Field).Type)
   104  			switch typeString {
   105  			case stringType:
   106  				output += fmt.Sprintf("C.GoString(%s)", paramIdentity.Name)
   107  			case intType:
   108  				output += fmt.Sprintf("int(%s)", paramIdentity.Name)
   109  			case unsafePointerType:
   110  				output += paramIdentity.Name
   111  			case boolType:
   112  				output += paramIdentity.Name
   113  				// convert int to bool
   114  				output += " == 1"
   115  			default:
   116  				// ignore otherwise
   117  				return ""
   118  			}
   119  		}
   120  	}
   121  
   122  	// close function call
   123  	output += ")"
   124  
   125  	// close conversion to CString
   126  	if results != nil {
   127  		output += ")\n"
   128  	}
   129  
   130  	// close function declaration
   131  	output += "}\n"
   132  	return output
   133  }
   134  
   135  func handleFile(parsedAST *ast.File) string {
   136  	output := ""
   137  	for name, obj := range parsedAST.Scope.Objects {
   138  		// Ignore non-functions or non exported fields
   139  		if obj.Kind != ast.Fun || !unicode.IsUpper(rune(name[0])) {
   140  			continue
   141  		}
   142  		output += handleFunction(name, obj.Decl.(*ast.FuncDecl))
   143  	}
   144  
   145  	return output
   146  }