@@ -2,18 +2,17 @@ package meta
2
2
3
3
import (
4
4
"bytes"
5
- "fmt"
6
5
"io"
7
6
"net/url"
8
7
"os"
9
8
"reflect"
10
9
"strconv"
10
+ "strings"
11
11
"time"
12
12
13
13
// Packages
14
14
server "github.com/mutablelogic/go-server"
15
15
httpresponse "github.com/mutablelogic/go-server/pkg/httpresponse"
16
- ast "github.com/mutablelogic/go-server/pkg/parser/ast"
17
16
types "github.com/mutablelogic/go-server/pkg/types"
18
17
)
19
18
@@ -27,8 +26,16 @@ type Meta struct {
27
26
Type reflect.Type
28
27
Index []int
29
28
Fields []* Meta
29
+
30
+ // Private fields
31
+ label []string
32
+ parent * Meta
30
33
}
31
34
35
+ const (
36
+ labelSeparator = "."
37
+ )
38
+
32
39
////////////////////////////////////////////////////////////////////////////////
33
40
// LIFECYCLE
34
41
@@ -46,6 +53,7 @@ func New(v server.Plugin) (*Meta, error) {
46
53
return nil , httpresponse .ErrInternalError .Withf ("expected struct, got %T" , v )
47
54
} else {
48
55
meta .Name = v .Name ()
56
+ meta .label = []string {}
49
57
meta .Description = v .Description ()
50
58
meta .Type = rt
51
59
}
@@ -54,7 +62,7 @@ func New(v server.Plugin) (*Meta, error) {
54
62
fields := reflect .VisibleFields (rt )
55
63
meta .Fields = make ([]* Meta , 0 , len (fields ))
56
64
for _ , field := range fields {
57
- if field , err := newMetaField (field ); err != nil {
65
+ if field , err := newMetaField (field , meta ); err != nil {
58
66
return nil , httpresponse .ErrInternalError .With (err .Error ())
59
67
} else if field != nil {
60
68
meta .Fields = append (meta .Fields , field )
@@ -65,6 +73,26 @@ func New(v server.Plugin) (*Meta, error) {
65
73
return meta , nil
66
74
}
67
75
76
+ // Return a new metadata object, with a label
77
+ func (m * Meta ) WithLabel (label string ) * Meta {
78
+ // Copy object
79
+ meta := new (Meta )
80
+ meta .Name = m .Name
81
+ meta .Description = m .Description
82
+ meta .Default = m .Default
83
+ meta .Type = m .Type
84
+ meta .Index = m .Index
85
+ meta .Fields = m .Fields
86
+
87
+ // Make a copy of the label
88
+ meta .label = make ([]string , len (m .label ))
89
+ copy (meta .label , m .label )
90
+ meta .label = append (meta .label , label )
91
+
92
+ // Return copy of meta
93
+ return meta
94
+ }
95
+
68
96
////////////////////////////////////////////////////////////////////////////////
69
97
// STRINGIFY
70
98
@@ -78,48 +106,57 @@ func (m *Meta) String() string {
78
106
79
107
func (m * Meta ) Write (w io.Writer ) error {
80
108
var buf bytes.Buffer
109
+ m .writeBlock (& buf , 2 )
110
+ _ , err := w .Write (buf .Bytes ())
111
+ return err
112
+ }
81
113
114
+ func (m * Meta ) writeBlock (buf * bytes.Buffer , indent int ) {
115
+ prefix := strings .Repeat (" " , indent )
82
116
if m .Description != "" {
83
- buf .WriteString ("// " )
84
- buf .WriteString (m .Description )
85
- buf .WriteString ("\n " )
117
+ buf .WriteString (prefix + "// " + m .Description + "\n " )
86
118
}
87
- buf .WriteString (m .Name )
88
- buf .WriteString (" \" label\" {\n " )
89
-
119
+ buf .WriteString (prefix + m .Name )
120
+ if label := m .Label (); label != "" {
121
+ buf .WriteString (" " + strconv .Quote (label ))
122
+ }
123
+ buf .WriteString (" {\n " )
90
124
for _ , field := range m .Fields {
91
- buf .WriteString (" " )
92
- buf .WriteString (field .Name )
93
- buf .WriteString (" = " )
94
- buf .WriteString ("<" + typeName (field .Type ) + ">" )
125
+ // Block
126
+ if field .Type == nil {
127
+ buf .WriteString ("\n " )
128
+ field .writeBlock (buf , indent + 2 )
129
+ continue
130
+ }
95
131
132
+ // Field
133
+ buf .WriteString (prefix + prefix + field .Name + " = " + "<" + typeName (field .Type ) + ">" )
96
134
if field .Description != "" {
97
- buf .WriteString (" // " )
98
- buf .WriteString (field .Description )
135
+ buf .WriteString (" // " + field .Description )
99
136
}
137
+ buf .WriteString (prefix + "// " + m .Label () + "\n " )
100
138
if field .Default != "" {
101
139
buf .WriteString (" (default: " + types .Quote (field .Default ) + ")" )
102
140
}
103
-
104
141
buf .WriteString ("\n " )
105
142
}
106
-
107
- buf .WriteString ("}\n " )
108
- _ , err := w .Write (buf .Bytes ())
109
- return err
143
+ buf .WriteString (prefix + "}\n " )
110
144
}
111
145
112
146
////////////////////////////////////////////////////////////////////////////////
113
147
// PUBLIC METHODS
114
148
115
- func (m * Meta ) Validate (values any ) error {
116
- dict := values .(map [string ]ast.Node )
117
- for _ , field := range m .Fields {
118
- fmt .Println (field .Name , "=>" , dict [field .Name ])
149
+ // Return the label
150
+ func (m * Meta ) Label () string {
151
+ var parts []string
152
+ for meta := m ; meta != nil ; meta = meta .parent {
153
+ parts = append (parts , meta .label ... )
119
154
}
120
- return nil
155
+ return strings . Join ( parts , labelSeparator )
121
156
}
122
157
158
+ // Create a new plugin instance and set default values, then validate any
159
+ // required fields
123
160
func (m * Meta ) New () server.Plugin {
124
161
obj := reflect .New (m .Type )
125
162
for _ , field := range m .Fields {
@@ -129,10 +166,25 @@ func (m *Meta) New() server.Plugin {
129
166
return obj .Interface ().(server.Plugin )
130
167
}
131
168
169
+ // Get a metadata field by name
170
+ func (m * Meta ) Get (label string ) * Meta {
171
+ for _ , field := range m .Fields {
172
+ if field .Label () == label {
173
+ return field
174
+ }
175
+ if field .Type == nil {
176
+ if field := field .Get (label ); field != nil {
177
+ return field
178
+ }
179
+ }
180
+ }
181
+ return nil
182
+ }
183
+
132
184
////////////////////////////////////////////////////////////////////////////////
133
185
// PRIVATE METHODS
134
186
135
- func newMetaField (rf reflect.StructField ) (* Meta , error ) {
187
+ func newMetaField (rf reflect.StructField , parent * Meta ) (* Meta , error ) {
136
188
meta := new (Meta )
137
189
meta .Index = rf .Index
138
190
@@ -142,14 +194,31 @@ func newMetaField(rf reflect.StructField) (*Meta, error) {
142
194
return nil , nil
143
195
} else {
144
196
meta .Name = name
197
+ meta .label = []string {name }
198
+ meta .parent = parent
145
199
}
146
200
147
201
// Description
148
- if description , _ := valueForField (rf , "description" , "help" ); description != "" {
202
+ if description , _ := valueForField (rf , "description" , "desc" , " help" ); description != "" {
149
203
meta .Description = description
150
204
}
151
205
152
- // Env - needs to be an identififer
206
+ // Block type
207
+ if rf .Type .Kind () == reflect .Struct && rf .Type != timeType {
208
+ // Get visible fields
209
+ fields := reflect .VisibleFields (rf .Type )
210
+ meta .Fields = make ([]* Meta , 0 , len (fields ))
211
+ for _ , field := range fields {
212
+ if field , err := newMetaField (field , meta ); err != nil {
213
+ return nil , httpresponse .ErrInternalError .With (err .Error ())
214
+ } else if field != nil {
215
+ meta .Fields = append (meta .Fields , field )
216
+ }
217
+ }
218
+ return meta , nil
219
+ }
220
+
221
+ // Set default for the field
153
222
if env , _ := valueForField (rf , "env" ); types .IsIdentifier (env ) {
154
223
meta .Default = "${" + env + "}"
155
224
} else if def , _ := valueForField (rf , "default" ); def != "" {
@@ -158,7 +227,7 @@ func newMetaField(rf reflect.StructField) (*Meta, error) {
158
227
159
228
// Type
160
229
if t := typeName (rf .Type ); t == "" {
161
- return nil , httpresponse .ErrInternalError .Withf ("unsupported type: %s " , rf .Type )
230
+ return nil , httpresponse .ErrInternalError .Withf ("unsupported type: %v " , rf .Type )
162
231
} else {
163
232
meta .Type = rf .Type
164
233
}
@@ -228,7 +297,7 @@ func setValue(rv reflect.Value, str string) error {
228
297
}
229
298
fallthrough
230
299
default :
231
- // TODO: Datetime
300
+ // TODO: time.Time
232
301
return httpresponse .ErrBadRequest .Withf ("invalid value for %s: %q" , rv .Type (), str )
233
302
}
234
303
0 commit comments