/
root.go
150 lines (125 loc) · 3.69 KB
/
root.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
package hvrt
import (
"database/sql"
"errors"
"fmt"
"io/fs"
"net/url"
)
type Thunk func()
type ThunkErr func() error
type ThunkAny func() any
func NilThunk() {}
func NilThunkErr() error { return nil }
func NilThunkAny() any { return nil }
const (
WorkTreeConfigDir = ".hvrt"
)
var (
NotImplementedError error = errors.New("feature not yet implemented")
MissingDSNError error = errors.New("DSN is empty")
)
// Struct definition that contains all state required to run an instance of Havarti
type HavartiState struct {
// FS to interact with for worktree. May be nil or readonly
workTreeFS fs.FS
// String to the original working directory
originalWorkDir *string
// The directory in which the worktree is located. May be nil
workTree *string
// the base URI to connect to database
dsn_url *url.URL
// Driver name for repository DB
_DBDriverName string
// Flag to allow potentially unsafe operations that could lose history
AllowUnsafe bool
// Verbosity level
Verbosity int
}
// Function to return a new HavartiState
func NewHavartiState(workTreeFS fs.FS, cwd *string, workTree *string, dataSourceName string, dbDriverName string) (*HavartiState, error) {
var err error
// FIXME: Stat worktree against the FS that was passed in
if workTreeFS != nil && workTree != nil {
if _, err = fs.Stat(workTreeFS, *workTree); errors.Is(err, fs.ErrNotExist) {
return nil, fmt.Errorf("workTree must be a valid directory: %w", err)
}
}
var dsn_url *url.URL = nil
if dataSourceName != "" {
if dsn_url, err = url.Parse(dataSourceName); err != nil {
return nil, err
}
}
// It is currently not an error for both workTree and dsn_url to be nil.
hvrtState := &HavartiState{
workTreeFS: workTreeFS,
originalWorkDir: cwd,
workTree: workTree,
dsn_url: dsn_url,
_DBDriverName: dbDriverName,
}
return hvrtState, nil
}
func (hs *HavartiState) GetOriginalWorkDir() (string, error) {
if hs.originalWorkDir == nil {
return "", errors.New("originalWorkDir is nil")
} else {
return *hs.originalWorkDir, nil
}
}
func (hs *HavartiState) GetWorkTree() (string, error) {
if hs.workTree == nil {
return "", errors.New("worktree is nil")
} else {
return *hs.workTree, nil
}
}
func (hs *HavartiState) GetDSN() (*url.URL, error) {
if hs.dsn_url == nil {
return nil, MissingDSNError
} else {
return hs.dsn_url, nil
}
}
func (hs *HavartiState) ConnectToRepoDB(writable, create bool) (*sql.DB, error) {
if dsn_url, err := hs.GetDSN(); err != nil {
return nil, MissingDSNError
} else {
// TODO: generalize this beyond just sqlite
parms := CopyOps(SqliteDefaultOpts)
parms["mode"] = []string{"ro"}
if writable && create {
parms["mode"][0] = "rwc"
} else if writable {
parms["mode"][0] = "rw"
}
dsn, err := AddParmsToDSN(dsn_url, parms)
if err != nil {
return nil, err
}
repoDB, err := sql.Open(hs._DBDriverName, dsn.String())
if err != nil {
return nil, err
}
// TODO: set sqlite pragmas here, such as enforcing foreign constraints, etc.
return repoDB, nil
}
}
// Set HavartState workTree
func (hs *HavartiState) SetWorkTree(workTree string) {
hs.workTree = &workTree
}
// Set HavartState to allow potentially unsafe operations during the duration of
// the thunk. When the thunk returns, the state will be restored to its previous
// value. If the value is changed during the duration of the thunk, that value
// will be overwritten by the restored previous value. Any error returned by the
// thunk is returned by this method.
func (hs *HavartiState) AllowUnsafeTemporarily(thnk ThunkErr) error {
prevUnsafeVal := hs.AllowUnsafe
hs.AllowUnsafe = true
defer func() { hs.AllowUnsafe = prevUnsafeVal }()
return thnk()
}
func init() {
}