Skip to content

Commit

Permalink
Merge pull request #125 from widmogrod/feature/june-2024-fix-docs
Browse files Browse the repository at this point in the history
Documentation on mkunion and storage and resiliency
  • Loading branch information
widmogrod authored Jul 13, 2024
2 parents ca5eaab + 07f70cd commit 1949a35
Show file tree
Hide file tree
Showing 82 changed files with 6,013 additions and 5,443 deletions.
115 changes: 55 additions & 60 deletions cmd/mkunion/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,54 +90,6 @@ func main() {
return nil
},
Commands: []*cli.Command{
{
Name: "match",
Description: "Generate custom pattern matching function",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "name",
Required: true,
},
},
Action: func(c *cli.Context) error {
cwd, _ := syscall.Getwd()
sourceName := path.Base(os.Getenv("GOFILE"))
sourcePath := path.Join(cwd, sourceName)

baseName := strings.TrimSuffix(sourceName, path.Ext(sourceName))

// file name without extension
inferred, err := generators.InferDeriveFuncMatchFromFile(sourcePath)
if err != nil {
return err
}

specName := c.String("name")
spec, err := inferred.MatchSpec(specName)
if err != nil {
return err
}

derived := generators.DeriveFuncMatchGenerator{
Header: "// Code generated by mkunion. DO NOT EDIT.",
PackageName: inferred.PackageName,
MatchSpec: *spec,
}

b, err := derived.Generate()
if err != nil {
return err
}
err = os.WriteFile(path.Join(
cwd,
baseName+"_match_"+strings.ToLower(derived.MatchSpec.Name)+".go"), b, 0644)
if err != nil {
return fmt.Errorf("failed to write %s for %s in %s: %w", "gen", derived.MatchSpec.Name, sourcePath, err)
}

return nil
},
},
{
Name: "shape-export",
Description: "Generate typescript types from golang types, and enable end-to-end type safety.",
Expand Down Expand Up @@ -256,33 +208,41 @@ func main() {
return
}

// is .go file?
if filepath.Ext(event.Name) != ".go" {
continue
}

if event.Op&fsnotify.Chmod == fsnotify.Chmod {
continue
}

// extract path name from event.Name
pathName := strings.Trim(event.Name, `"`)

if event.Op&fsnotify.Remove == fsnotify.Remove {
// if the file was removed, regenerate type registry
savedFiles, err := GenerateTypeRegistryForDir([]string{filepath.Dir(event.Name)})
dir := filepath.Dir(event.Name)

backLevel := log.GetLevel()
log.SetLevel(log.ErrorLevel)
savedFiles, err := GenerateTypeRegistryForDir([]string{dir})
log.SetLevel(backLevel)

if err != nil {
log.Warnf("failed to generate type registry for %s: %s", filepath.Dir(event.Name), err)
log.Warnf("failed to generate type registry for %s: %s", dir, err)
continue
}

for _, x := range savedFiles {
log.Infof("re-generated:\t%s", x)

justGenerated.Store(x, true)
}
}

// is .go file?
if filepath.Ext(event.Name) != ".go" {
// and, since file is deleted, skip the rest
continue
}

// extract path name from event.Name
pathName := strings.Trim(event.Name, `"`)

// if the file was generated by watch process, skip it
if _, ok := justGenerated.Load(pathName); ok {
// but to prevent removing it to fast and resulting in infinit-generation loop
Expand Down Expand Up @@ -532,7 +492,6 @@ func GenerateMain(sourcePaths []string, typeRegistry bool) ([]string, error) {
if err != nil {
return savedFiles, fmt.Errorf("failed generating union in %s: %w", sourcePath, err)
}

savedFile, err := SaveFile(contents, sourcePath, "union_gen")
if err != nil {
return savedFiles, fmt.Errorf("failed saving union in %s: %w", sourcePath, err)
Expand All @@ -545,7 +504,6 @@ func GenerateMain(sourcePaths []string, typeRegistry bool) ([]string, error) {
if err != nil {
return savedFiles, fmt.Errorf("failed generating serde in %s: %w", sourcePath, err)
}

savedFile, err = SaveFile(contents, sourcePath, "serde_gen")
if err != nil {
return savedFiles, fmt.Errorf("failed saving serde in %s: %w", sourcePath, err)
Expand All @@ -558,12 +516,22 @@ func GenerateMain(sourcePaths []string, typeRegistry bool) ([]string, error) {
if err != nil {
return savedFiles, fmt.Errorf("failed generating shape in %s: %w", sourcePath, err)
}

savedFile, err = SaveFile(contents, sourcePath, "shape_gen")
if err != nil {
return savedFiles, fmt.Errorf("failed saving shape in %s: %w", sourcePath, err)
}
if len(savedFile) > 0 {
savedFiles = append(savedFiles, savedFile)
}

contents, err = GenerateMatch(inferred)
if err != nil {
return savedFiles, fmt.Errorf("failed generating match in %s: %w", sourcePath, err)
}
savedFile, err = SaveFile(contents, sourcePath, "match_gen")
if err != nil {
return savedFiles, fmt.Errorf("failed saving match in %s: %w", sourcePath, err)
}
if len(savedFile) > 0 {
savedFiles = append(savedFiles, savedFile)
}
Expand Down Expand Up @@ -956,3 +924,30 @@ func GenerateTypeRegistry(inferred *shape.IndexedTypeWalker) (bytes.Buffer, erro

return contents, nil
}

func GenerateMatch(inferred *shape.InferredInfo) (bytes.Buffer, error) {
result := bytes.Buffer{}

match := generators.NewMkMatchTaggedNodeVisitor()
match.FromInferredInfo(inferred)

specs := match.Specs()
if len(specs) == 0 {
return result, nil
}

derived := generators.MkMatchGenerator{
Header: "// Code generated by mkunion. DO NOT EDIT.",
PackageName: inferred.PackageName(),
MatchSpecs: specs,
}

b, err := derived.Generate()

if err != nil {
return result, fmt.Errorf("GenerateMatch: failed to generate match: %w", err)
}

result.Write(b)
return result, nil
}
2 changes: 1 addition & 1 deletion docs/examples/type_script.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ title: End-to-End types between Go and TypeScript
TODO description of generating TypeScript Definitions from using MkUnion

```go title="example/my-app/server.go"
--8<-- "example/my-app/server.go:37:55"
--8<-- "example/my-app/server.go:34:55"
```
6 changes: 6 additions & 0 deletions docs/getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ Alternatively you can run `mkunion` command directly
mkunion -i example/vehicle.go
```


#### What order you should run `mkunion watch` and `go generate`?
First run `mkunion watch ./...` to generate union types, and then run `go generate ./...` to generate code that uses union types.

I found that this order works best, especially with extension like `moq` that will fail to generate mocks when an type is not defined, which is the case for union types, unit you run `mkunion watch`.

### Match over union type
When you run `mkunion` command, it will generate file alongside your original file with `union_gen.go` suffix (example [vehicle_union_gen.go](https://github.com/widmogrod/mkunion/tree/main/example/vehicle_union_gen.go))

Expand Down
3 changes: 3 additions & 0 deletions docs/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

- [ ] **docs**: document simple state machine and how to use `mkunion` for it
- [x] **feature**: `mkunion watch ./...` command that watches for changes in files and runs faster than `go generate ./...`
- [x] **feature**: `go:tag mkmatch` to generate pattern matching functions
- [ ] **docs**: document how to write custom pattern matching functions
- [ ] **docs**: document other packages in `x/` directory
- [ ] **docs**: document typescript types generation and end-to-end typs concepts (from backend to frontend)
- [ ] **feature**: expose functions to extract `go:tag` metadata
Expand All @@ -14,3 +16,4 @@
- [ ] **prototype**: http & gRPC client for end-to-end types.
- [ ] **experiment**: allow to derive behaviour for types, like derive(Map), would generated union type with Map() method
- [ ] **experiment**: consider adding explicit discriminator type names like `example.Branch[int]` instead of `example.Branch`. This may complicate TypeScript codegen but it could increase end-to-end type safety.
- [ ] **refactor**: `x/storage` instead of generic, leverage schema information to remove lookup of schemas (overhead), eventually generate storage code
Loading

0 comments on commit 1949a35

Please sign in to comment.