Skip to content

Instantly share code, notes, and snippets.

@podhmo
Last active May 15, 2020 22:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save podhmo/a7da30b807271c50211fb65ffd2e6edc to your computer and use it in GitHub Desktop.
Save podhmo/a7da30b807271c50211fb65ffd2e6edc to your computer and use it in GitHub Desktop.
from __future__ import annotations
import typing as t
from egoist.app import App, SettingsDict, parse_args
from egoist.generators.structkit.runtime import metadata, field
from egoist.generators.structkit.runtime import set_metadata_handler, Metadata
settings: SettingsDict = {"rootdir": "", "here": __file__}
app = App(settings)
app.include("egoist.directives.define_struct_set")
app.include("egoist.directives.define_cli")
class Author:
name: str
# createdAt: datetime.date
class Article:
title: str
author: t.Optional[Author]
content: str
comments: t.List[Comment] = field(metadata=metadata(required=False))
class Comment:
author: Author
content: str
@app.define_struct_set("egoist.generators.structkit:walk")
def model__objects() -> None:
from egoist.generators.structkit import runtime, structkit
@runtime.set_metadata_handler
def metadata_handler(
cls: t.Type[t.Any], *, name: str, info: t.Any, metadata: runtime.Metadata
) -> None:
"""Yaml also added"""
metadata["tags"] = {"json": [name.rstrip("_")], "yaml": [name.rstrip("_")]}
with runtime.generate(structkit, classes=[Article]) as m:
m.package("model")
# cli
@app.define_cli("egoist.generators.clikit:walk")
def cmd__load(*, filename: str) -> None:
from egoist.generators.clikit import runtime, clikit
with runtime.generate(clikit) as m:
os_pkg = m.import_("os")
load_pkg = m.import_("m/load")
model_pkg = m.import_("m/model")
f = m.symbol("f")
ob = m.symbol("ob")
err = m.symbol("err")
m.stmt(f"{f}, {err} := {os_pkg.Open(filename)}")
m.stmt(f"if {err} != nil {{")
with m.scope():
m.return_(err)
m.stmt("}")
m.stmt(f"defer {f}.Close()")
m.stmt(f"var {ob} {model_pkg.Article}")
p = m.let("p", f"&{ob}")
m.return_(load_pkg.LoadAndPrint(f, p))
if __name__ == "__main__":
for argv in parse_args(sep="-"):
app.run(argv)
module m
go 1.14
require (
github.com/k0kubun/pp v3.0.1+incompatible
github.com/mattn/go-colorable v0.1.6 // indirect
github.com/podhmo/maperr v0.2.4
)
github.com/k0kubun/pp v1.3.0 h1:r9td75hcmetrcVbmsZRjnxcIbI9mhm+/N6iWyG4TWe0=
github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd9g2S9Z40=
github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg=
github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/podhmo/maperr v0.2.4 h1:9nOl0g3nKwGTiPEU+LLZ8hUnTqCHkgWd+EAG6cnllaA=
github.com/podhmo/maperr v0.2.4/go.mod h1:wG7W0ko8CN+bhVeBcRUc+E6zp2j/SoBvm3fvt9sGFFU=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
package load
import (
"encoding/json"
"io"
"github.com/k0kubun/pp"
)
func Load(r io.Reader, ob interface{}) error {
decoder := json.NewDecoder(r)
return decoder.Decode(ob)
}
func LoadAndPrint(r io.Reader, ob interface{}) error {
if err := Load(r, ob); err != nil {
return err
}
pp.Println(ob)
return nil
}
package main
import (
"flag"
"os"
"log"
"m/load"
"m/model"
)
// this file is generated by egoist.generators.clikit
// Option ...
type Option struct {
Filename string // for `-filename`
Args []string // cmd.Args
}
func main() {
opt := &Option{}
cmd := flag.NewFlagSet("cmd__load", flag.ContinueOnError)
cmd.StringVar(&opt.Filename, "filename", "", "-")
if err := cmd.Parse(os.Args[1:]); err != nil {
if err != flag.ErrHelp {
cmd.Usage()
}
os.Exit(1)
}
opt.Args = cmd.Args()
if err := run(opt); err != nil {
log.Fatalf("!!%+v", err)
}
}
func run(opt *Option) error {
f, err := os.Open(opt.Filename)
if err != nil {
return err
}
defer f.Close()
var ob model.Article
p := &ob
return load.LoadAndPrint(f, p)
return nil
}
SHELL := $(shell which bash)
gen:
python definitions.py generate
# ng
00:
go run cmd/load/main.go --filename <(echo '{}')
# ok
01:
go run cmd/load/main.go --filename <(echo '{"title": "hello world", "content": "Hello worlld, this is my first article. ...", "comments": []}')
# ng
02:
go run cmd/load/main.go --filename <(echo '{"comments": [{}], "title": "hello world", "content": "Hello worlld, this is my first article. ..."}')
# ok
03:
go run cmd/load/main.go --filename <(echo '{"title": "hello world", "content": "Hello worlld, this is my first article. ..."}')
package model
import (
"github.com/podhmo/maperr"
"encoding/json"
)
// this file is generated by egoist.generators.structkit
type Article struct {
Title string `json:"title" yaml:"title"`
Author *Author `json:"author" yaml:"author"`
Content string `json:"content" yaml:"content"`
Comments []Comment `json:"comments" yaml:"comments"`
}
func (a *Article) UnmarshalJSON(b []byte) error {
var err *maperr.Error
// loading internal data
var inner struct {
Title *string `json:"title"`// required
Author *json.RawMessage `json:"author"`
Content *string `json:"content"`// required
Comments *json.RawMessage `json:"comments"`
}
if rawErr := json.Unmarshal(b, &inner); rawErr != nil {
return err.AddSummary(rawErr.Error())
}
// binding field value and required check
{
if inner.Title != nil {
a.Title = *inner.Title
} else {
err = err.Add("title", maperr.Message{Text: "required"})
}
if inner.Author != nil {
a.Author = &Author{}
if rawerr := json.Unmarshal(*inner.Author, a.Author); rawerr != nil {
err = err.Add("author", maperr.Message{Error: rawerr})
}
}
if inner.Content != nil {
a.Content = *inner.Content
} else {
err = err.Add("content", maperr.Message{Text: "required"})
}
if inner.Comments != nil {
a.Comments = []Comment{}
if rawerr := json.Unmarshal(*inner.Comments, &a.Comments); rawerr != nil {
err = err.Add("comments", maperr.Message{Error: rawerr})
}
}
}
return err.Untyped()
}
type Author struct {
Name string `json:"name" yaml:"name"`
}
func (a *Author) UnmarshalJSON(b []byte) error {
var err *maperr.Error
// loading internal data
var inner struct {
Name *string `json:"name"`// required
}
if rawErr := json.Unmarshal(b, &inner); rawErr != nil {
return err.AddSummary(rawErr.Error())
}
// binding field value and required check
{
if inner.Name != nil {
a.Name = *inner.Name
} else {
err = err.Add("name", maperr.Message{Text: "required"})
}
}
return err.Untyped()
}
type Comment struct {
Author Author `json:"author" yaml:"author"`
Content string `json:"content" yaml:"content"`
}
func (c *Comment) UnmarshalJSON(b []byte) error {
var err *maperr.Error
// loading internal data
var inner struct {
Author *json.RawMessage `json:"author"`// required
Content *string `json:"content"`// required
}
if rawErr := json.Unmarshal(b, &inner); rawErr != nil {
return err.AddSummary(rawErr.Error())
}
// binding field value and required check
{
if inner.Author != nil {
if rawerr := json.Unmarshal(*inner.Author, &c.Author); rawerr != nil {
err = err.Add("author", maperr.Message{Error: rawerr})
}
} else {
err = err.Add("author", maperr.Message{Text: "required"})
}
if inner.Content != nil {
c.Content = *inner.Content
} else {
err = err.Add("content", maperr.Message{Text: "required"})
}
}
return err.Untyped()
}
.
├── Makefile
├── cmd
│   └── load
│   └── main.go
├── definitions.py
├── go.mod
├── go.sum
├── load
│   └── load.go
├── model
│   └── objects.go
└── structure.txt
4 directories, 8 files
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment