-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbagit.go
161 lines (132 loc) · 4.07 KB
/
bagit.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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package bagit
import (
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"github.com/artefactual-labs/bagit-gython/internal/dist/data"
"github.com/artefactual-labs/bagit-gython/internal/runner"
"github.com/kluctl/go-embed-python/embed_util"
"github.com/kluctl/go-embed-python/python"
)
var (
// ErrInvalid indicates that bag validation failed. If there is a validation
// error message, ErrInvalid will be wrapped so make sure to use
// `errors.Is(err, ErrInvalid)` to test equivalency.
ErrInvalid = errors.New("invalid")
// ErrBusy is returned when an operation is attempted on BagIt while it is
// already processing another command. This ensures that only one command is
// processed at a time, preventing race conditions and ensuring the
// integrity of the shared resources.
ErrBusy = errors.New("runner is busy")
)
// BagIt is an abstraction to work with BagIt packages that embeds Python and
// the bagit-python.
type BagIt struct {
tmpDir string // Top-level container for embedded files.
embedPython *python.EmbeddedPython // Python files.
embedBagit *embed_util.EmbeddedFiles // bagit-python library files.
embedRunner *embed_util.EmbeddedFiles // bagit-python wrapper files (runner).
runner *pyRunner
}
// NewBagIt creates and initializes a new BagIt instance. This constructor is
// computationally expensive as it sets up an embedded Python environment and
// extracts necessary libraries. It's recommended to create a single instance
// and share it across your application to avoid repeatedly installing Python.
func NewBagIt() (*BagIt, error) {
b := &BagIt{}
var err error
b.tmpDir, err = os.MkdirTemp("", "bagit-gython-*")
if err != nil {
return nil, fmt.Errorf("make tmpDir: %v", err)
}
b.embedPython, err = python.NewEmbeddedPythonWithTmpDir(filepath.Join(b.tmpDir, "python"), true)
if err != nil {
return nil, fmt.Errorf("embed python: %v", err)
}
b.embedBagit, err = embed_util.NewEmbeddedFilesWithTmpDir(data.Data, filepath.Join(b.tmpDir, "bagit-lib"), true)
if err != nil {
return nil, fmt.Errorf("embed bagit: %v", err)
}
b.embedPython.AddPythonPath(b.embedBagit.GetExtractedPath())
b.embedRunner, err = embed_util.NewEmbeddedFilesWithTmpDir(runner.Source, filepath.Join(b.tmpDir, "bagit-runner"), true)
if err != nil {
return nil, fmt.Errorf("embed runner: %v", err)
}
b.runner = createRunner(
b.embedPython,
filepath.Join(b.embedRunner.GetExtractedPath(), "main.py"),
)
return b, nil
}
type validateRequest struct {
Path string `json:"path"`
}
type validateResponse struct {
Valid bool `json:"valid"`
Err string `json:"err"`
}
func (b *BagIt) Validate(path string) error {
blob, err := b.runner.send("validate", &validateRequest{
Path: path,
})
if err != nil {
return err
}
r := validateResponse{}
err = json.Unmarshal(blob, &r)
if err != nil {
return fmt.Errorf("decode response: %v", err)
}
if r.Err != "" {
return fmt.Errorf("%w: %s", ErrInvalid, r.Err)
}
if !r.Valid {
return ErrInvalid
}
return nil
}
type makeRequest struct {
Path string `json:"path"`
}
type makeResponse struct {
Version string `json:"version"`
Err string `json:"err"`
}
func (b *BagIt) Make(path string) error {
blob, err := b.runner.send("make", &makeRequest{
Path: path,
})
if err != nil {
return err
}
r := makeResponse{}
err = json.Unmarshal(blob, &r)
if err != nil {
return fmt.Errorf("decode response: %v", err)
}
if r.Err != "" {
return fmt.Errorf("make: %s", r.Err)
}
return nil
}
func (b *BagIt) Cleanup() error {
var e error
if err := b.runner.stop(); err != nil {
e = errors.Join(e, fmt.Errorf("stop runner: %v", err))
}
if err := b.embedRunner.Cleanup(); err != nil {
e = errors.Join(e, fmt.Errorf("clean up runner: %v", err))
}
if err := b.embedBagit.Cleanup(); err != nil {
e = errors.Join(e, fmt.Errorf("clean up bagit: %v", err))
}
if err := b.embedPython.Cleanup(); err != nil {
e = errors.Join(e, fmt.Errorf("clean up python: %v", err))
}
if err := os.RemoveAll(b.tmpDir); err != nil {
e = errors.Join(e, fmt.Errorf("clean up tmpDir: %v", err))
}
return e
}