Skip to content

Commit 734e5bb

Browse files
committed
Add scrap push to upload scraps to a configurable server
1 parent 1593b3e commit 734e5bb

File tree

4 files changed

+88
-16
lines changed

4 files changed

+88
-16
lines changed

cmd/scrap/main.go

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,46 @@ import (
1111
"github.com/Victorystick/scrapscript/yards"
1212
)
1313

14-
type Command func(args []string)
14+
type Command struct {
15+
name string
16+
desc string
17+
fn func(args []string)
18+
}
1519

16-
var commands = map[string]Command{
17-
"eval": evaluate,
18-
"type": inferType,
20+
var commands = []Command{
21+
{name: "eval", desc: "evaluates it", fn: evaluate},
22+
{name: "type", desc: "infers its type", fn: inferType},
23+
{name: "push", desc: "pushes it to the server", fn: pushScrap},
1924
}
2025

26+
var (
27+
server = flag.String("server", "https://scraps.oseg.dev/", "The scrapyard server to use")
28+
)
29+
2130
func main() {
2231
flag.Parse()
2332

24-
cmd, ok := commands[flag.Arg(0)]
25-
if !ok {
26-
flag.Usage()
27-
for name := range commands {
28-
fmt.Fprintln(os.Stderr, name)
33+
name := flag.Arg(0)
34+
var cmd *Command
35+
for i := range commands {
36+
if commands[i].name == name {
37+
cmd = &commands[i]
38+
break
39+
}
40+
}
41+
42+
if cmd == nil {
43+
fmt.Fprintln(os.Stderr, os.Args[0], "reads a script from stdin, parses it and does one of", len(commands), "things:")
44+
fmt.Fprintln(os.Stderr)
45+
for _, cmd := range commands {
46+
fmt.Fprintf(os.Stderr, "%s %s - %s\n", os.Args[0], cmd.name, cmd.desc)
2947
}
48+
fmt.Fprintln(os.Stderr, "\nFlags:")
49+
flag.PrintDefaults()
3050
os.Exit(2)
3151
}
3252

33-
cmd(flag.Args()[1:])
53+
cmd.fn(flag.Args()[1:])
3454
}
3555

3656
func must[T any](val T, err error) T {
@@ -43,12 +63,13 @@ func must[T any](val T, err error) T {
4363

4464
func makeEnv() *eval.Environment {
4565
env := eval.NewEnvironment()
66+
67+
pusher := yards.ByHttp(*server)
68+
env.UsePusher(pusher)
4669
env.UseFetcher(must(yards.NewDefaultCacheFetcher(
4770
// Don't cache invalid scraps, but trust the local cache for now.
48-
yards.Validate(
49-
// TODO: make configurable
50-
yards.ByHttp("https://scraps.oseg.dev/")),
51-
)))
71+
yards.Validate(pusher)),
72+
))
5273
return env
5374
}
5475

@@ -73,3 +94,11 @@ func inferType(args []string) {
7394
scrap := must(env.Read(input))
7495
fmt.Println(must(env.Infer(scrap)))
7596
}
97+
98+
func pushScrap(args []string) {
99+
input := must(io.ReadAll(os.Stdin))
100+
env := makeEnv()
101+
scrap := must(env.Read(input))
102+
key := must(env.Push(scrap))
103+
fmt.Println(key)
104+
}

eval/env.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type Scrap struct {
2020
type Sha256Hash = [32]byte
2121

2222
type Environment struct {
23+
pusher yards.Pusher
2324
fetcher yards.Fetcher
2425
reg types.Registry
2526
// The TypeScope and Variables match each other's contents.
@@ -54,6 +55,10 @@ func NewEnvironment() *Environment {
5455
return env
5556
}
5657

58+
func (e *Environment) UsePusher(pusher yards.Pusher) {
59+
e.pusher = pusher
60+
}
61+
5762
func (e *Environment) UseFetcher(fetcher yards.Fetcher) {
5863
e.fetcher = fetcher
5964
}
@@ -132,3 +137,11 @@ func (e *Environment) Scrap(value Value) string {
132137
}
133138
return value.String()
134139
}
140+
141+
func (e *Environment) Push(scrap *Scrap) (string, error) {
142+
if e.pusher == nil {
143+
return "", fmt.Errorf("cannot push without a pusher")
144+
}
145+
146+
return e.pusher.PushScrap(scrap.expr.Source.Bytes())
147+
}

yards/http.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package yards
22

33
import (
4+
"bytes"
45
"fmt"
56
"io"
67
"net/http"
@@ -11,11 +12,11 @@ type httpFetcher struct {
1112
hostname string
1213
}
1314

14-
func ByHttp(hostname string) Fetcher {
15+
func ByHttp(hostname string) FetchPusher {
1516
return ByHttpWithClient(hostname, http.DefaultClient)
1617
}
1718

18-
func ByHttpWithClient(hostname string, client *http.Client) Fetcher {
19+
func ByHttpWithClient(hostname string, client *http.Client) FetchPusher {
1920
return httpFetcher{client, hostname}
2021
}
2122

@@ -24,6 +25,7 @@ func (h httpFetcher) FetchSha256(key string) ([]byte, error) {
2425
if err != nil {
2526
return nil, err
2627
}
28+
req.Header.Add("Accept", "application/scrap")
2729

2830
resp, err := h.client.Do(req)
2931
if err != nil {
@@ -36,3 +38,20 @@ func (h httpFetcher) FetchSha256(key string) ([]byte, error) {
3638

3739
return io.ReadAll(resp.Body)
3840
}
41+
42+
func (h httpFetcher) PushScrap(data []byte) (key string, err error) {
43+
req, err := http.NewRequest("POST", string(h.hostname), bytes.NewReader(data))
44+
if err != nil {
45+
return
46+
}
47+
req.Header.Add("Content-Type", "application/scrap")
48+
49+
resp, err := h.client.Do(req)
50+
if err != nil {
51+
return
52+
}
53+
54+
bytes, err := io.ReadAll(resp.Body)
55+
key = string(bytes)
56+
return
57+
}

yards/yard.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,17 @@ type Fetcher interface {
1212
FetchSha256(key string) ([]byte, error)
1313
}
1414

15+
// Pusher is the interface for storing scraps, returning their SHA hashes.
16+
type Pusher interface {
17+
PushScrap(data []byte) (key string, err error)
18+
}
19+
20+
// A FetchPusher is both a Fetcher and a Pusher.
21+
type FetchPusher interface {
22+
Fetcher
23+
Pusher
24+
}
25+
1526
// ByDirectory returns a Fetcher that looks in the given directory.
1627
func ByDirectory(fs fs.FS) Fetcher {
1728
return &directoryFetcher{fs}

0 commit comments

Comments
 (0)