Created
June 5, 2025 08:03
-
-
Save Gurpartap/ee5c6e8a52a81f397d3a0426f5aa5caf to your computer and use it in GitHub Desktop.
futurize: simple async "futures" generator in Go (2018)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package {{ .PackageName }} | |
type future_{{ .StructName }}_{{ .MethodName }} struct { | |
task func() ({{ .ReturnTypes | csvTypes }}) | |
} | |
func (f future_{{ .StructName }}_{{ .MethodName }}) Await() ({{ .ReturnTypes | csvTypes }}) { | |
return f.task() | |
} | |
func newFuture_{{ .StructName }}_{{ .MethodName }}(f func() ({{ .ReturnTypes | csvTypes }})) future_{{ .StructName }}_{{ .MethodName }} { | |
{{ range .ReturnTypes }} | |
var {{ .Name }} {{ .Type }} | |
{{ end }} | |
c := make(chan struct{}, 1) | |
go func() { | |
defer close(c) | |
{{ .ReturnTypes | csvNames }} = f() | |
}() | |
return future_{{ .StructName }}_{{ .MethodName }}{ | |
task: func() ({{ .ReturnTypes | csvTypes }}) { | |
<-c | |
return {{ .ReturnTypes | csvNames }} | |
}, | |
} | |
} | |
func ({{ .PackageName }} *{{ .StructName }}) Async{{ .MethodName }}({{ .InputArgs | withTypes }}) future_{{ .StructName }}_{{ .MethodName }} { | |
return newFuture_{{ .StructName }}_{{ .MethodName }}(func() ({{ .ReturnTypes | csvTypes }}) { | |
return {{ .PackageName }}.{{ .MethodName }}({{ .InputArgs | withoutTypes }}) | |
}) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bytes" | |
"flag" | |
"fmt" | |
"go/build" | |
"go/format" | |
"html/template" | |
"io/ioutil" | |
"log" | |
"os" | |
"strings" | |
"time" | |
"unicode" | |
) | |
type generator struct { | |
packageName string | |
structName string | |
methodName string | |
returnTypes []string | |
} | |
func (g *generator) generate() ([]byte, error) { | |
filename := "./futures.go.tmpl" | |
funcMap := template.FuncMap{"title": strings.Title} | |
t := template.Must(template.New("optional.go.tmpl").Funcs(funcMap).ParseFiles(filename)) | |
data := struct { | |
Timestamp time.Time | |
PackageName string | |
StructName string | |
MethodName string | |
ReturnTypes []string | |
}{ | |
time.Now().UTC(), | |
g.packageName, | |
g.structName, | |
g.methodName, | |
g.returnTypes, | |
} | |
var buf bytes.Buffer | |
err := t.Execute(&buf, data) | |
if err != nil { | |
return nil, err | |
} | |
src, err := format.Source(buf.Bytes()) | |
if err != nil { | |
return nil, err | |
} | |
return src, nil | |
} | |
func main() { | |
log.SetFlags(0) | |
log.SetPrefix("futurize: ") | |
structName := flag.String("s", "", "name of the struct") | |
methodName := flag.String("f", "", "name of the method") | |
flag.Parse() | |
if len(*methodName) == 0 { | |
flag.Usage() | |
os.Exit(2) | |
} | |
pkg, err := build.Default.ImportDir(".", 0) | |
if err != nil { | |
log.Fatal(err) | |
} | |
var ( | |
filename string | |
g generator | |
) | |
g.packageName = pkg.Name | |
g.methodName = *methodName | |
if len(*structName) == 0 { | |
// no output specified, use default optional_<type> | |
// TODO: may not be the most reliable method | |
exported := strings.Title(g.methodName) == g.methodName | |
if exported { | |
g.structName = "Optional" + strings.Title(g.methodName) | |
} else { | |
g.structName = "optional" + strings.Title(g.methodName) | |
} | |
filename = fmt.Sprintf("%s.go", to_snake_case(g.structName)) | |
} else { | |
g.structName = *structName | |
filename = to_snake_case(g.structName) + ".go" | |
} | |
filename = strings.Replace(filename, "u_int", "uint", -1) | |
if strings.Contains(g.methodName, "timestamp.Timestamp") { | |
g.returnTypes = append(g.returnTypes, "github.com/golang/protobuf/ptypes/timestamp") | |
} | |
src, err := g.generate() | |
if err != nil { | |
log.Fatal(err) | |
} | |
err = ioutil.WriteFile(filename, src, 0644) | |
if err != nil { | |
log.Fatalf("writing output: %s", err) | |
} | |
} | |
// to_snake_case convert the given string to snake case following the Golang format: | |
// acronyms are converted to lower-case and preceded by an underscore. | |
func to_snake_case(in string) string { | |
runes := []rune(in) | |
length := len(runes) | |
var out []rune | |
for i := 0; i < length; i++ { | |
if i > 0 && unicode.IsUpper(runes[i]) && ((i+1 < length && unicode.IsLower(runes[i+1])) || unicode.IsLower(runes[i-1])) { | |
out = append(out, '_') | |
} | |
out = append(out, unicode.ToLower(runes[i])) | |
} | |
return string(out) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment