Skip to content

Commit fe34825

Browse files
committed
Initial commit
Initial commit of chisel. Doesn't do anything yet, just setting up config, serving, and some basics.
0 parents  commit fe34825

File tree

7 files changed

+1472
-0
lines changed

7 files changed

+1472
-0
lines changed

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/bin
2+
/sql.1
3+
4+
.*.sw[a-z]
5+
Session.vim
6+
7+
*.db

COPYING

Lines changed: 674 additions & 0 deletions
Large diffs are not rendered by default.

config.go

Lines changed: 369 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,369 @@
1+
// chisel - A tool to serve fetch, transform, and serve data.
2+
// Copyright (C) 2021 Noel Cower
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU General Public License
15+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
17+
package main
18+
19+
import (
20+
"context"
21+
"database/sql"
22+
"encoding/json"
23+
"errors"
24+
"fmt"
25+
"sort"
26+
"strconv"
27+
"strings"
28+
29+
"github.com/hashicorp/go-sockaddr"
30+
"github.com/itchyny/gojq"
31+
"go.spiff.io/sql/vdb"
32+
)
33+
34+
type Config struct {
35+
Bind []sockaddr.SockAddrMarshaler `json:"bind"`
36+
Databases map[string]*DatabaseDef `json:"databases"`
37+
Modules map[string]*ModuleDef `json:"modules"`
38+
Endpoints []*EndpointDef `json:"endpoints"`
39+
}
40+
41+
type DatabaseDef struct {
42+
URL string `json:"url"`
43+
Options vdb.QueryOptions `json:"options"`
44+
}
45+
46+
type ModuleDef struct {
47+
}
48+
49+
type IsolationLevel sql.IsolationLevel
50+
51+
func (i *IsolationLevel) UnmarshalText(src []byte) error {
52+
switch s := string(src); s {
53+
case "none":
54+
*i = -1
55+
case "default":
56+
*i = IsolationLevel(sql.LevelDefault)
57+
case "read_uncommitted":
58+
*i = IsolationLevel(sql.LevelReadUncommitted)
59+
case "read_committed":
60+
*i = IsolationLevel(sql.LevelReadCommitted)
61+
case "write_committed":
62+
*i = IsolationLevel(sql.LevelWriteCommitted)
63+
case "repeatable_read":
64+
*i = IsolationLevel(sql.LevelRepeatableRead)
65+
case "snapshot":
66+
*i = IsolationLevel(sql.LevelSnapshot)
67+
case "serializable":
68+
*i = IsolationLevel(sql.LevelSerializable)
69+
case "linearizable":
70+
*i = IsolationLevel(sql.LevelLinearizable)
71+
default:
72+
return fmt.Errorf("unrecognized isolation level %q", s)
73+
}
74+
return nil
75+
}
76+
77+
func (i IsolationLevel) MarshalText() ([]byte, error) {
78+
switch i {
79+
case -1:
80+
return []byte("none"), nil
81+
case IsolationLevel(sql.LevelDefault):
82+
return []byte("default"), nil
83+
case IsolationLevel(sql.LevelReadUncommitted):
84+
return []byte("read_uncommitted"), nil
85+
case IsolationLevel(sql.LevelReadCommitted):
86+
return []byte("read_committed"), nil
87+
case IsolationLevel(sql.LevelWriteCommitted):
88+
return []byte("write_committed"), nil
89+
case IsolationLevel(sql.LevelRepeatableRead):
90+
return []byte("repeatable_read"), nil
91+
case IsolationLevel(sql.LevelSnapshot):
92+
return []byte("snapshot"), nil
93+
case IsolationLevel(sql.LevelSerializable):
94+
return []byte("serializable"), nil
95+
case IsolationLevel(sql.LevelLinearizable):
96+
return []byte("linearizable"), nil
97+
default:
98+
return nil, fmt.Errorf("unrecognized isolation level %d", i)
99+
}
100+
}
101+
102+
func (i IsolationLevel) RequiresTranscation() bool {
103+
return i != -1
104+
}
105+
106+
func (i IsolationLevel) Level() sql.IsolationLevel {
107+
return sql.IsolationLevel(i)
108+
}
109+
110+
type EndpointDef struct {
111+
Bind IntSet `json:"bind"`
112+
Method string `json:"method"`
113+
Path string `json:"path"`
114+
QueryParams map[string]*ParamMapping `json:"query_params"`
115+
PathParams map[string]*ParamMapping `json:"path_params"`
116+
117+
TransactionIsolation IsolationLevel `json:"isolation"`
118+
Transaction []*TransactionDef `json:"transaction"`
119+
}
120+
121+
type ParamMapping struct {
122+
Map Mapping `json:"map"`
123+
}
124+
125+
type TransactionDef struct {
126+
Query string `json:"query"`
127+
Args ArgDefs `json:"args"`
128+
}
129+
130+
type ArgDefs []ArgDef
131+
132+
func (ads *ArgDefs) UnmarshalJSON(src []byte) error {
133+
var defs []json.RawMessage
134+
if err := json.Unmarshal(src, &defs); err != nil {
135+
return err
136+
}
137+
138+
args := make(ArgDefs, len(defs))
139+
for i, def := range defs {
140+
ad, err := UnmarshalArgDef(def)
141+
if err != nil {
142+
return fmt.Errorf("error unmarshaling arg %d: %w", i, err)
143+
}
144+
args[i] = ad
145+
}
146+
*ads = args
147+
return nil
148+
}
149+
150+
type ArgDef interface {
151+
Value() (interface{}, error)
152+
}
153+
154+
var ErrBadArgDef = errors.New("invalid arg def: must be a scalar, null, or contain a single key of 'path' or 'query'")
155+
156+
func UnmarshalArgDef(blob json.RawMessage) (ArgDef, error) {
157+
var m map[string]json.RawMessage
158+
if json.Unmarshal(blob, &m) == nil {
159+
if m == nil {
160+
return &ArgLiteral{Literal: nil}, nil
161+
}
162+
if len(m) != 1 {
163+
return nil, ErrBadArgDef
164+
}
165+
var key string
166+
var value json.RawMessage
167+
for k, v := range m {
168+
key = k
169+
value = v
170+
}
171+
switch key {
172+
case "path":
173+
var ref PathParamRef
174+
if err := json.Unmarshal(value, &ref.Name); err != nil {
175+
return nil, fmt.Errorf("error unmarshaling path arg def: %w", err)
176+
}
177+
return &ref, nil
178+
case "query":
179+
var ref QueryParamRef
180+
if err := json.Unmarshal(value, &ref.Name); err != nil {
181+
return nil, fmt.Errorf("error unmarshaling query arg def: %w", err)
182+
}
183+
return &ref, nil
184+
default:
185+
return nil, ErrBadArgDef
186+
}
187+
}
188+
189+
lit := &ArgLiteral{}
190+
if err := json.Unmarshal(blob, &lit.Literal); err != nil {
191+
return nil, fmt.Errorf("error unmarshaling arg def as literal: %w", err)
192+
}
193+
194+
if _, ok := lit.Literal.([]interface{}); ok {
195+
return nil, ErrBadArgDef
196+
}
197+
198+
return lit, nil
199+
}
200+
201+
type ArgLiteral struct {
202+
Literal interface{}
203+
}
204+
205+
func (a ArgLiteral) Value() (interface{}, error) {
206+
return a.Literal, nil
207+
}
208+
209+
func (a ArgLiteral) MarshalJSON() ([]byte, error) {
210+
return json.Marshal(a.Literal)
211+
}
212+
213+
type PathParamRef struct {
214+
Name string `json:"path"`
215+
}
216+
217+
func (p PathParamRef) Value() (interface{}, error) {
218+
return nil, errors.New("unimplemented")
219+
}
220+
221+
type QueryParamRef struct {
222+
Name string `json:"query"`
223+
}
224+
225+
func (q QueryParamRef) Value() (interface{}, error) {
226+
return nil, errors.New("unimplemented")
227+
}
228+
229+
type Expr struct {
230+
ID string
231+
Options []gojq.CompilerOption
232+
Query *gojq.Query
233+
Code *gojq.Code
234+
}
235+
236+
func (e *Expr) UnmarshalText(src []byte) error {
237+
q, err := gojq.Parse(string(src))
238+
if err != nil {
239+
return fmt.Errorf("error parsing expression %s: %w", e.ID, err)
240+
}
241+
242+
c, err := gojq.Compile(q)
243+
if err != nil {
244+
return fmt.Errorf("error compiling expression %s: %w", e.ID, err)
245+
}
246+
247+
dup := *e
248+
dup.Query = q
249+
dup.Code = c
250+
*e = dup
251+
return nil
252+
}
253+
254+
func (e *Expr) MarshalText() ([]byte, error) {
255+
return []byte(e.Query.String()), nil
256+
}
257+
258+
type Mapping struct {
259+
ID string
260+
Exprs []*Expr
261+
}
262+
263+
var ErrNoMapping = errors.New("no result from output mapping")
264+
var ErrMultipleMapping = errors.New("mapping produced multiple values")
265+
266+
func (m *Mapping) Apply(ctx context.Context, str string) (interface{}, error) {
267+
if len(m.Exprs) == 0 {
268+
return str, nil
269+
}
270+
271+
var v interface{} = str
272+
var ok bool
273+
for _, e := range m.Exprs {
274+
iter := e.Code.RunWithContext(ctx, v)
275+
v, ok = iter.Next()
276+
if !ok {
277+
return nil, fmt.Errorf("no value returned by mapping %s: %w", e.ID, ErrNoMapping)
278+
}
279+
if err, ok := v.(error); ok {
280+
return nil, fmt.Errorf("error in mapping %s: %w", e.ID, err)
281+
}
282+
_, ok = iter.Next()
283+
if ok {
284+
return nil, fmt.Errorf("unexpected results from mapping %s: %w", e.ID, ErrMultipleMapping)
285+
}
286+
}
287+
288+
return v, nil
289+
}
290+
291+
func (m *Mapping) UnmarshalJSON(src []byte) error {
292+
var strs []string
293+
if err := json.Unmarshal(src, &strs); err != nil {
294+
return fmt.Errorf("error unmarshaling expression list %s: %w", m.ID, err)
295+
}
296+
297+
exprs := make([]*Expr, len(strs))
298+
for i, es := range strs {
299+
e := &Expr{
300+
ID: indexID(m.ID, "map", i),
301+
}
302+
if err := e.UnmarshalText([]byte(es)); err != nil {
303+
return fmt.Errorf("error unmarshaling mapping expression %s: %w", e.ID, err)
304+
}
305+
exprs[i] = e
306+
}
307+
308+
dup := *m
309+
dup.Exprs = exprs
310+
*m = dup
311+
312+
return nil
313+
}
314+
315+
func (m *Mapping) MarshalJSON() ([]byte, error) {
316+
return json.Marshal(m.Exprs)
317+
}
318+
319+
func nameID(prefix, name string) string {
320+
var sb strings.Builder
321+
if prefix != "" {
322+
_, _ = sb.WriteString(prefix)
323+
_, _ = sb.WriteString("/")
324+
}
325+
_, _ = sb.WriteString(name)
326+
return sb.String()
327+
}
328+
329+
func indexID(prefix, name string, index int) string {
330+
var sb strings.Builder
331+
if prefix != "" {
332+
_, _ = sb.WriteString(prefix)
333+
_ = sb.WriteByte('.')
334+
}
335+
_, _ = sb.WriteString(name)
336+
_ = sb.WriteByte('[')
337+
_, _ = sb.WriteString(strconv.Itoa(index))
338+
_ = sb.WriteByte(']')
339+
return sb.String()
340+
}
341+
342+
type IntSet map[int]struct{}
343+
344+
func (is IntSet) Contains(i int) bool {
345+
_, ok := is[i]
346+
return ok
347+
}
348+
349+
func (is IntSet) MarshalJSON() ([]byte, error) {
350+
ints := make([]int, len(is))
351+
for i := range is {
352+
ints = append(ints, i)
353+
}
354+
sort.Ints(ints)
355+
return json.Marshal(ints)
356+
}
357+
358+
func (is *IntSet) UnmarshalJSON(src []byte) error {
359+
var ints []int
360+
if err := json.Unmarshal(src, &ints); err != nil {
361+
return err
362+
}
363+
m := make(IntSet, len(ints))
364+
for _, i := range ints {
365+
m[i] = struct{}{}
366+
}
367+
*is = m
368+
return nil
369+
}

0 commit comments

Comments
 (0)