Skip to content

Commit

Permalink
v1.0.0
Browse files Browse the repository at this point in the history
Fixes #5
  • Loading branch information
OneOfOne committed May 30, 2018
1 parent 9f00749 commit 02f3727
Show file tree
Hide file tree
Showing 11 changed files with 791 additions and 310 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
*.txt
*.pprof
cmap2/
cache/
debug
171 changes: 85 additions & 86 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,120 +25,119 @@ Inspired by [tkrajina/typescriptify-golang-structs](https://github.com/tkrajina/

#### Example struct

* Input:

```go
type OtherStruct struct {
T time.Time `json:"t,omitempty"`
}

type ComplexStruct struct {
S string `json:"s,omitempty"`
I int `json:"i,omitempty"`
F float64 `json:"f,omitempty"`
TS *int64 `json:"ts,omitempty" ts:"date,null"`
T time.Time `json:"t,omitempty"` // automatically handled
NullOther *OtherStruct `json:"o,omitempty"`
NoNullOther *OtherStruct `json:"nno,omitempty" ts:",no-null"`
}
```
type S struct{
Date int64 `json:"date" ts:"date"`
GoOnly string `json:"server-side-only" ts:"-"`
NoNull *OtherStruct `ts:"no-null"` // the generated TS code will always use new OtherStruct instead of null for the default value.
Null int64 `ts:"null"` // the default value in the generated TS code will be null instead of 0.

* Output:

```ts
// ... helpers...
// struct2ts:github.com/OneOfOne/struct2ts_test.ComplexStructOtherStruct
class ComplexStructOtherStruct {
t: Date;

constructor(data?: any) {
const d: any = (data && typeof data === 'object') ? ToObject(data) : {};
this.t = ('t' in d) ? ParseDate(d.t) : new Date();
}

toObject(): any {
const cfg: any = {};
cfg.t = 'string';
return ToObject(this, cfg);
}
}

// struct2ts:github.com/OneOfOne/struct2ts_test.ComplexStruct
class ComplexStruct {
s: string;
i: number;
f: number;
ts: Date | null;
t: Date;
o: ComplexStructOtherStruct | null;
nno: ComplexStructOtherStruct;

constructor(data?: any) {
const d: any = (data && typeof data === 'object') ? ToObject(data) : {};
this.s = ('s' in d) ? d.s as string : '';
this.i = ('i' in d) ? d.i as number : 0;
this.f = ('f' in d) ? d.f as number : 0;
this.ts = ('ts' in d) ? ParseDate(d.ts) : null;
this.t = ('t' in d) ? ParseDate(d.t) : new Date();
this.o = ('o' in d) ? new ComplexStructOtherStruct(d.o) : null;
this.nno = new ComplexStructOtherStruct(d.nno);
}

toObject(): any {
const cfg: any = {};
cfg.i = 'number';
cfg.f = 'number';
cfg.t = 'string';
return ToObject(this, cfg);
}
}
// ...exports...

```

## Example
## Command Line Usage

```
┏━ oneofone@Ava ❨✪/O/struct2ts❩ ❨master ⚡❩
┗━━➤ struct2ts -h
usage: struct2ts [<flags>] <pkg.struct>...
usage: struct2ts [<flags>] [<pkg.struct>...]
Flags:
-h, --help Show context-sensitive help (also try --help-long and --help-man).
-h, --help Show context-sensitive help (also try --help-long
and --help-man).
--indent="\t" Output indentation.
-m, --mark-optional-fields Add `?` to fields with omitempty.
-6, --es6 generate es6 code
-C, --no-ctor Don't generate a ctor.
-T, --no-toObject Don't generate a Class.toObject() method.
-D, --no-date Don't automatically handle time.Unix () <-> JS Date().
-E, --no-exports Don't automatically export the generated types.
-D, --no-date Don't automatically handle time.Unix () <-> JS
Date().
-H, --no-helpers Don't output the helpers.
-N, --no-default-values Don't assign default/zero values in the ctor.
-i, --interface Only generate an interface (disables all the other options).
-s, --src-only Only output the Go code (helpful if you want to edit it yourself).
-i, --interface Only generate an interface (disables all the other
options).
-s, --src-only Only output the Go code (helpful if you want to
edit it yourself).
-p, --package-name="main" the package name to use if --src-only is set.
-k, --keep-temp Keep the generated Go file, ignored if --src-only is set.
-k, --keep-temp Keep the generated Go file, ignored if --src-only
is set.
-o, --out="-" Write the output to a file instead of stdout.
-V, --version Show application version.
Args:
<pkg.struct> List of structs to convert (github.com/you/auth/users.User or just users.User).
[<pkg.struct>] List of structs to convert (github.com/you/auth/users.User,
users.User or users.User:AliasUser).
┏━ oneofone@Ava ❨✪/O/struct2ts❩ ❨master ⚡❩
┗━━➤ struct2ts struct2ts.Options
main.go:75: executing: go run /tmp/s2ts_gen_725543931.go
// struct2ts:github.com/OneOfOne/struct2ts.Options
export class Options {
Indent: string = '';
NoAssignDefaults: boolean = false;
InterfaceOnly: boolean = false;
MarkOptional: boolean = false;
NoConstructor: boolean = false;
NoToObject: boolean = false;
NoDate: boolean = false;
constructor(data?: any) {
if (typeof data !== 'object') return;
if ('Indent' in data) this.Indent = data.Indent as string;
if ('NoAssignDefaults' in data) this.NoAssignDefaults = data.NoAssignDefaults as boolean;
if ('InterfaceOnly' in data) this.InterfaceOnly = data.InterfaceOnly as boolean;
if ('MarkOptional' in data) this.MarkOptional = data.MarkOptional as boolean;
if ('NoConstructor' in data) this.NoConstructor = data.NoConstructor as boolean;
if ('NoToObject' in data) this.NoToObject = data.NoToObject as boolean;
if ('NoDate' in data) this.NoDate = data.NoDate as boolean;
}
toObject(): { [key:string]: any } {
const data: { [key:string]: any } = {};
if (this.Indent) data.Indent = this.Indent;
if (this.NoAssignDefaults) data.NoAssignDefaults = this.NoAssignDefaults;
if (this.InterfaceOnly) data.InterfaceOnly = this.InterfaceOnly;
if (this.MarkOptional) data.MarkOptional = this.MarkOptional;
if (this.NoConstructor) data.NoConstructor = this.NoConstructor;
if (this.NoToObject) data.NoToObject = this.NoToObject;
if (this.NoDate) data.NoDate = this.NoDate;
return data;
}
}
┏━ oneofone@Ava ❨✪/O/struct2ts❩ ❨master ⚡❩
┗━━➤ struct2ts --src-only struct2ts.Options
// this file was automatically generated using struct2ts --src-only struct2ts.Options
package main
import (
"os"
"github.com/OneOfOne/struct2ts"
)
func main() {
if err := runStruct2TS(); err != nil {
panic(err)
}
}
func runStruct2TS() error {
s := struct2ts.New(&struct2ts.Options{
Indent: " ",
NoAssignDefaults: false,
InterfaceOnly: false,
NoConstructor: false,
MarkOptional: false,
NoToObject: false,
NoDate: false,
})
s.Add(struct2ts.Options{})
return s.RenderTo(os.Stdout)
}
```

## TODO

* Use [xast](https://github.com/OneOfOne/struct2ts) to skip reflection.
* Support ES6.
* ~~Support ES6.~~
* Support annoymous structs.

## License
Expand Down
18 changes: 18 additions & 0 deletions cmd/genHelpers.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/sh

IN=$1
INJS=${IN/\.ts/\.js}
OUT=$2
tsc -t es6 -m es6 $IN || exit 1

echo package struct2ts > $OUT
echo >> $OUT
echo "const ts_helpers = \`" >> $OUT
cat $IN >> $OUT
echo "\`" >> $OUT

echo >> $OUT
echo "const es6_helpers = \`" >> $OUT
perl -pe 's/\s{4}/\t/g' < $INJS >> $OUT
rm $INJS
echo "\`" >> $OUT
59 changes: 43 additions & 16 deletions cmd/struct2ts/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
KP "gopkg.in/alecthomas/kingpin.v2"
)

const version = "v0.0.3"
const version = "v1.0.0"

var (
opts struct2ts.Options
Expand All @@ -37,9 +37,12 @@ var (
func init() {
KP.Flag("indent", "Output indentation.").Default("\t").StringVar(&opts.Indent)
KP.Flag("mark-optional-fields", "Add `?` to fields with omitempty.").Short('m').BoolVar(&opts.MarkOptional)
KP.Flag("es6", "generate es6 code").Short('6').BoolVar(&opts.ES6)
KP.Flag("no-ctor", "Don't generate a ctor.").Short('C').BoolVar(&opts.NoConstructor)
KP.Flag("no-toObject", "Don't generate a Class.toObject() method.").Short('T').BoolVar(&opts.NoToObject)
KP.Flag("no-exports", "Don't automatically export the generated types.").Short('E').BoolVar(&opts.NoExports)
KP.Flag("no-date", "Don't automatically handle time.Unix () <-> JS Date().").Short('D').BoolVar(&opts.NoDate)
KP.Flag("no-helpers", "Don't output the helpers.").Short('H').BoolVar(&opts.NoHelpers)
KP.Flag("no-default-values", "Don't assign default/zero values in the ctor.").Short('N').BoolVar(&opts.NoAssignDefaults)
KP.Flag("interface", "Only generate an interface (disables all the other options).").Short('i').BoolVar(&opts.InterfaceOnly)

Expand All @@ -51,8 +54,9 @@ func init() {

KP.Flag("out", "Write the output to a file instead of stdout.").Short('o').Default("-").StringVar(&outFile)

KP.Arg("pkg.struct", "List of structs to convert (github.com/you/auth/users.User or just users.User).").
Required().StringsVar(&types)
KP.Arg("pkg.struct", "List of structs to convert (github.com/you/auth/users.User, users.User or users.User:AliasUser).").
StringsVar(&types)

}

type M map[string]interface{}
Expand All @@ -64,6 +68,7 @@ func main() {
KP.Parse()

out := os.Stdout

if outFile != "-" && outFile != "/dev/stdout" {
of, err := os.Create(outFile)
if err != nil {
Expand Down Expand Up @@ -117,9 +122,10 @@ func main() {

func render() ([]byte, error) {
var (
buf bytes.Buffer
imports []string
ttypes = types[:0]
buf bytes.Buffer
imports []string
ttypes = types[:0]
typesWithNames [][2]string
)

for _, t := range types {
Expand All @@ -132,15 +138,20 @@ func render() ([]byte, error) {
imports = append(imports, t[:dotIdx])
t = t[idx+1:]
}
ttypes = append(ttypes, t)
if idx := strings.LastIndexByte(t, ':'); idx != -1 {
typesWithNames = append(typesWithNames, [2]string{t[:idx], t[idx+1:]})
} else {
ttypes = append(ttypes, t)
}
}

err := tmpl.Execute(&buf, M{
"pkgName": pkgName,
"cmd": strings.Join(os.Args[1:], " "),
"opts": opts,
"imports": imports,
"types": ttypes,
"pkgName": pkgName,
"cmd": strings.Join(os.Args[1:], " "),
"opts": opts,
"imports": imports,
"types": ttypes,
"typesWithNames": typesWithNames,
})

return buf.Bytes(), err
Expand All @@ -156,11 +167,18 @@ const fileTmpl = `// this file was automatically generated using struct2ts {{.cm
package {{.pkgName}}
import (
"flag"
"log"
"io"
"os"
"github.com/OneOfOne/struct2ts"
{{ range $_, $imp := .imports }}"{{$imp}}"{{ end }}
)
{{ if eq .pkgName "main" }}func main() {
{{- if eq .pkgName "main" }}
func main() {
log.SetFlags(log.Lshortfile)
var (
out = flag.String("o", "-", "output")
f = os.Stdout
Expand All @@ -177,7 +195,8 @@ import (
if err = runStruct2TS(f); err != nil {
panic(err)
}
}{{ end }}
}
{{- end }}
func runStruct2TS(w io.Writer) error {
s := struct2ts.New(&struct2ts.Options{
Expand All @@ -189,13 +208,21 @@ func runStruct2TS(w io.Writer) error {
NoConstructor: {{ .opts.NoConstructor }},
MarkOptional: {{ .opts.MarkOptional }},
NoToObject: {{ .opts.NoToObject }},
NoExports: {{ .opts.NoExports }},
NoHelpers: {{ .opts.NoHelpers }},
NoDate: {{ .opts.NoDate }},
ES6: {{ .opts.ES6 }},
})
{{ range $_, $t := .types }}
s.Add({{$t}}{}){{ end }}
s.Add({{$t}}{})
{{- end }}
{{ range $_, $t := .typesWithNames }}
s.AddWithName({{index $t 0}}{}, "{{index $t 1}}")
{{- end }}
io.WriteString(w, "// this file was automatically generated, DO NOT EDIT\n\n")
io.WriteString(w, "// this file was automatically generated, DO NOT EDIT\n")
return s.RenderTo(w)
}
`
Loading

0 comments on commit 02f3727

Please sign in to comment.