/
bsave.go
130 lines (114 loc) · 3.3 KB
/
bsave.go
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
package zygo
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"github.com/glycerine/greenpack/msgp"
)
// (bsave value path) writes value as greenpack to file.
//
// (greenpack value) writes value as greenpack to SexpRaw in memory.
//
// bsave converts to binary with (togo) then saves the binary to file.
func WriteShadowGreenpackToFileFunction(name string) ZlispUserFunction {
return func(env *Zlisp, _ string, args []Sexp) (Sexp, error) {
narg := len(args)
if narg < 1 || narg > 2 {
return SexpNull, WrongNargs
}
// check arg[0]
var asHash *SexpHash
switch x := args[0].(type) {
default:
return SexpNull, fmt.Errorf("%s error: top value must be a hash or defmap; we see '%T'", name, args[0])
case *SexpHash:
// okay, good
asHash = x
}
switch name {
case "bsave":
if narg != 2 {
return SexpNull, WrongNargs
}
case "greenpack":
if narg != 1 {
return SexpNull, WrongNargs
}
var buf bytes.Buffer
_, err := toGreenpackHelper(env, asHash, &buf, "memory")
if err != nil {
return SexpNull, err
}
return &SexpRaw{Val: buf.Bytes()}, nil
}
// check arg[1]
var fn string
switch fna := args[1].(type) {
case *SexpStr:
fn = fna.S
default:
return SexpNull, fmt.Errorf("error: %s requires a string (SexpStr) path to write to as the second argument. we got type %T / value = %v", name, args[1], args[1])
}
// don't overwrite existing file
if FileExists(fn) {
return SexpNull, fmt.Errorf("error: %s refusing to write to existing file '%s'",
name, fn)
}
f, err := os.Create(fn)
if err != nil {
return SexpNull, fmt.Errorf("error: %s sees error trying to create file '%s': '%v'", name, fn, err)
}
defer f.Close()
_, err = toGreenpackHelper(env, asHash, f, fn)
return SexpNull, err
}
}
func toGreenpackHelper(env *Zlisp, asHash *SexpHash, f io.Writer, fn string) (Sexp, error) {
// create shadow structs
_, err := ToGoFunction(env, "togo", []Sexp{asHash})
if err != nil {
return SexpNull, fmt.Errorf("ToGo call sees error: '%v'", err)
}
if asHash.GoShadowStruct == nil {
return SexpNull, fmt.Errorf("GoShadowStruct was nil, on attempt to write to '%s'", fn)
}
enc, ok := interface{}(asHash.GoShadowStruct).(msgp.Encodable)
if !ok {
return SexpNull, fmt.Errorf("error: GoShadowStruct was not greenpack Encodable -- run `go generate` or add greenpack to the source file for type '%T'. on attempt to save to '%s'", asHash.GoShadowStruct, fn)
}
w := msgp.NewWriter(f)
err = msgp.Encode(w, enc)
if err != nil {
return SexpNull, fmt.Errorf("error: greenpack encoding to file '%s' of type '%T' sees error '%v'", fn, asHash.GoShadowStruct, err)
}
err = w.Flush()
return SexpNull, err
}
func ReadGreenpackFromFileFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
narg := len(args)
if narg != 1 {
return SexpNull, WrongNargs
}
var fn string
switch fna := args[0].(type) {
case *SexpStr:
fn = fna.S
default:
return SexpNull, fmt.Errorf("%s requires a string path to read. we got type %T / value = %v", name, args[0], args[0])
}
if !FileExists(string(fn)) {
return SexpNull, fmt.Errorf("file '%s' does not exist", fn)
}
f, err := os.Open(fn)
if err != nil {
return SexpNull, err
}
defer f.Close()
by, err := ioutil.ReadAll(f)
if err != nil {
return SexpNull, err
}
return MsgpackToSexp(by, env)
}