-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexport.go
More file actions
130 lines (113 loc) · 3.56 KB
/
export.go
File metadata and controls
130 lines (113 loc) · 3.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// SPDX-License-Identifier: EUPL-1.2
package api
import (
"encoding/json"
"fmt"
"io"
"iter"
"os"
"path/filepath"
"strings"
"gopkg.in/yaml.v3"
coreio "dappco.re/go/core/io"
coreerr "dappco.re/go/core/log"
)
// ExportSpec generates the OpenAPI spec and writes it to w.
// Format must be "json" or "yaml".
//
// Example:
//
// _ = api.ExportSpec(os.Stdout, "yaml", builder, engine.Groups())
func ExportSpec(w io.Writer, format string, builder *SpecBuilder, groups []RouteGroup) error {
data, err := builder.Build(groups)
if err != nil {
return coreerr.E("ExportSpec", "build spec", err)
}
return writeSpec(w, format, data, "ExportSpec")
}
// ExportSpecIter generates the OpenAPI spec from an iterator and writes it to w.
// Format must be "json" or "yaml".
//
// Example:
//
// _ = api.ExportSpecIter(os.Stdout, "json", builder, api.RegisteredSpecGroupsIter())
func ExportSpecIter(w io.Writer, format string, builder *SpecBuilder, groups iter.Seq[RouteGroup]) error {
data, err := builder.BuildIter(groups)
if err != nil {
return coreerr.E("ExportSpecIter", "build spec", err)
}
return writeSpec(w, format, data, "ExportSpecIter")
}
func writeSpec(w io.Writer, format string, data []byte, op string) error {
switch strings.ToLower(strings.TrimSpace(format)) {
case "json":
_, err := w.Write(data)
return err
case "yaml":
// Unmarshal JSON then re-marshal as YAML.
var obj any
if err := json.Unmarshal(data, &obj); err != nil {
return coreerr.E(op, "unmarshal spec", err)
}
enc := yaml.NewEncoder(w)
enc.SetIndent(2)
if err := enc.Encode(obj); err != nil {
return coreerr.E(op, "encode yaml", err)
}
return enc.Close()
default:
return coreerr.E(op, fmt.Sprintf("unsupported format %s: use %q or %q", format, "json", "yaml"), nil)
}
}
// ExportSpecToFile writes the spec to the given path.
// The parent directory is created if it does not exist.
//
// Example:
//
// _ = api.ExportSpecToFile("./api/openapi.yaml", "yaml", builder, engine.Groups())
func ExportSpecToFile(path, format string, builder *SpecBuilder, groups []RouteGroup) error {
return exportSpecToFile(path, "ExportSpecToFile", func(w io.Writer) error {
return ExportSpec(w, format, builder, groups)
})
}
// ExportSpecToFileIter writes the OpenAPI spec from an iterator to the given path.
// The parent directory is created if it does not exist.
//
// Example:
//
// _ = api.ExportSpecToFileIter("./api/openapi.json", "json", builder, api.RegisteredSpecGroupsIter())
func ExportSpecToFileIter(path, format string, builder *SpecBuilder, groups iter.Seq[RouteGroup]) error {
return exportSpecToFile(path, "ExportSpecToFileIter", func(w io.Writer) error {
return ExportSpecIter(w, format, builder, groups)
})
}
func exportSpecToFile(path, op string, write func(io.Writer) error) (err error) {
dir := filepath.Dir(path)
if err := coreio.Local.EnsureDir(dir); err != nil {
return coreerr.E(op, "create directory", err)
}
// Write to a temp file in the same directory so the rename is atomic on
// most filesystems. The destination is never truncated unless the full
// export succeeds.
f, err := os.CreateTemp(dir, ".export-*.tmp")
if err != nil {
return coreerr.E(op, "create temp file", err)
}
tmpPath := f.Name()
defer func() {
if err != nil {
_ = os.Remove(tmpPath)
}
}()
if writeErr := write(f); writeErr != nil {
_ = f.Close()
return writeErr
}
if closeErr := f.Close(); closeErr != nil {
return coreerr.E(op, "close temp file", closeErr)
}
if renameErr := os.Rename(tmpPath, path); renameErr != nil {
return coreerr.E(op, "rename temp file", renameErr)
}
return nil
}