Skip to content

Commit fa760cf

Browse files
committed
Updated go-sqlite
1 parent f899eee commit fa760cf

File tree

5 files changed

+607
-0
lines changed

5 files changed

+607
-0
lines changed

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ require (
77
github.com/djthorpe/go-marshaler v0.0.15
88
github.com/hashicorp/go-multierror v1.1.1
99
github.com/mutablelogic/go-server v1.0.33
10+
github.com/rjeczalik/notify v0.9.2
1011
github.com/xuri/excelize/v2 v2.4.1
1112
golang.org/x/text v0.3.7
1213
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
@@ -20,4 +21,5 @@ require (
2021
github.com/xuri/efp v0.0.0-20210322160811-ab561f5b45e3 // indirect
2122
golang.org/x/crypto v0.0.0-20210920023735-84f357641f63 // indirect
2223
golang.org/x/net v0.0.0-20210917221730-978cfadd31cf // indirect
24+
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678 // indirect
2325
)

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ github.com/richardlehane/mscfb v1.0.3 h1:rD8TBkYWkObWO0oLDFCbwMeZ4KoalxQy+QgniCj
2929
github.com/richardlehane/mscfb v1.0.3/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
3030
github.com/richardlehane/msoleps v1.0.1 h1:RfrALnSNXzmXLbGct/P2b4xkFz4e8Gmj/0Vj9M9xC1o=
3131
github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
32+
github.com/rjeczalik/notify v0.9.2 h1:MiTWrPj55mNDHEiIX5YUSKefw/+lCQVoAFmD6oQm5w8=
33+
github.com/rjeczalik/notify v0.9.2/go.mod h1:aErll2f0sUX9PXZnVNyeiObbmTlk5jnMoCa4QEjJeqM=
3234
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
3335
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
3436
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
@@ -50,13 +52,15 @@ golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qx
5052
golang.org/x/net v0.0.0-20210917221730-978cfadd31cf h1:R150MpwJIv1MpS0N/pc+NhTM8ajzvlmxlY5OYsrevXQ=
5153
golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
5254
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
55+
golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
5356
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
5457
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
5558
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
5659
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
5760
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
5861
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
5962
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
63+
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678 h1:J27LZFQBFoihqXoegpscI10HpjZ7B5WQLLKL2FZXQKw=
6064
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
6165
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
6266
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

pkg/indexer/indexer.go

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
package indexer
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"io/fs"
7+
"os"
8+
"path/filepath"
9+
"regexp"
10+
"strconv"
11+
"sync"
12+
13+
// Package imports
14+
walkfs "github.com/mutablelogic/go-sqlite/pkg/walkfs"
15+
notify "github.com/rjeczalik/notify"
16+
17+
// Import namepaces
18+
. "github.com/djthorpe/go-errors"
19+
)
20+
21+
///////////////////////////////////////////////////////////////////////////////
22+
// TYPES
23+
24+
type Indexer struct {
25+
*walkfs.WalkFS
26+
name string
27+
path string
28+
walk chan struct{}
29+
}
30+
31+
///////////////////////////////////////////////////////////////////////////////
32+
// GLOBALS
33+
34+
const (
35+
defaultCapacity = 1024
36+
)
37+
38+
var (
39+
// Name for an index must be alphanumeric
40+
reIndexName = regexp.MustCompile(`^([A-Za-z0-9\_\-]+)$`)
41+
)
42+
43+
///////////////////////////////////////////////////////////////////////////////
44+
// LIFECYCLE
45+
46+
// Create a new indexer with an identifier, path to the root of the indexer
47+
// and a channel to receive any errors
48+
func NewIndexer(name, path string) (*Indexer, error) {
49+
this := new(Indexer)
50+
51+
// Check path argument
52+
if stat, err := os.Stat(path); err != nil {
53+
return nil, err
54+
} else if !stat.IsDir() {
55+
return nil, ErrBadParameter.With("invalid path: ", strconv.Quote(path))
56+
} else if abspath, err := filepath.Abs(path); err != nil {
57+
return nil, err
58+
} else if !reIndexName.MatchString(name) {
59+
return nil, ErrBadParameter.With("invalid index name: ", strconv.Quote(name))
60+
} else {
61+
this.name = name
62+
this.path = abspath
63+
this.WalkFS = walkfs.New(this.visit)
64+
this.walk = make(chan struct{})
65+
}
66+
67+
// Return success
68+
return this, nil
69+
}
70+
71+
// run indexer
72+
func (i *Indexer) Run(ctx context.Context, errs chan<- error) error {
73+
var walking sync.Mutex
74+
75+
in := make(chan notify.EventInfo, defaultCapacity)
76+
if err := notify.Watch(filepath.Join(i.path, "..."), in, notify.Create, notify.Remove, notify.Write, notify.Rename); err != nil {
77+
fmt.Println("err", err)
78+
return err
79+
}
80+
81+
FOR_LOOP:
82+
for {
83+
// Dispatch events to index files and folders until context is cancelled
84+
select {
85+
case <-ctx.Done():
86+
break FOR_LOOP
87+
case evt := <-in:
88+
if err := i.event(ctx, evt); err != nil {
89+
senderr(errs, err)
90+
}
91+
case <-i.walk:
92+
walking.Lock()
93+
go func() {
94+
defer walking.Unlock()
95+
if err := i.WalkFS.Walk(ctx, i.path); err != nil {
96+
senderr(errs, err)
97+
}
98+
}()
99+
}
100+
}
101+
102+
// Stop notify and close channels
103+
notify.Stop(in)
104+
close(in)
105+
close(i.walk)
106+
107+
// Return success
108+
return nil
109+
}
110+
111+
///////////////////////////////////////////////////////////////////////////////
112+
// STRINGIFY
113+
114+
func (i *Indexer) String() string {
115+
str := "<indexer"
116+
if i.name != "" {
117+
str += fmt.Sprintf(" name=%q", i.name)
118+
}
119+
if i.path != "" {
120+
str += fmt.Sprintf(" path=%q", i.path)
121+
}
122+
return str + ">"
123+
}
124+
125+
///////////////////////////////////////////////////////////////////////////////
126+
// PROPERTIES
127+
128+
// Return name of the index
129+
func (i *Indexer) Name() string {
130+
return i.name
131+
}
132+
133+
// Return the absolute path of the index
134+
func (i *Indexer) Path() string {
135+
return i.path
136+
}
137+
138+
///////////////////////////////////////////////////////////////////////////////
139+
// PUBLIC METHODS
140+
141+
// Walk will initiate a walk of the index, and block until context is
142+
// cancelled or walk is started
143+
func (i *Indexer) Walk(ctx context.Context) error {
144+
select {
145+
case <-ctx.Done():
146+
return ctx.Err()
147+
case i.walk <- struct{}{}:
148+
break
149+
}
150+
// Return success
151+
return nil
152+
}
153+
154+
///////////////////////////////////////////////////////////////////////////////
155+
// PRIVATE METHODS
156+
157+
// event is used to process an event from the notify
158+
func (i *Indexer) event(ctx context.Context, evt notify.EventInfo) error {
159+
fmt.Println("event", evt)
160+
return nil
161+
}
162+
163+
// visit is used to index a file from the indexer
164+
func (i *Indexer) visit(ctx context.Context, abspath, relpath string, info fs.FileInfo) error {
165+
fmt.Println("visited", abspath, relpath, info.IsDir())
166+
return nil
167+
}
168+
169+
// senderr is used to send an error without blocking
170+
func senderr(ch chan<- error, err error) {
171+
if ch != nil {
172+
select {
173+
case ch <- err:
174+
return
175+
default:
176+
// Channel blocked, ignore error
177+
return
178+
}
179+
}
180+
}
181+
182+
/*
183+
// Return an indexer event or nil if no event should be sent
184+
func (this *Indexer) process(e EventType, path string, info fs.FileInfo, out chan<- IndexerEvent, block bool) error {
185+
// Normalize the path
186+
relpath, err := filepath.Rel(this.path, path)
187+
if err != nil {
188+
return err
189+
} else {
190+
relpath = pathSeparator + relpath
191+
}
192+
193+
// Deal with exclusions
194+
if e&EVENT_TYPE_ADDED > 0 {
195+
// Check for path exclusions
196+
for exclusion := range this.expath {
197+
if strings.HasPrefix(relpath, exclusion) {
198+
return nil
199+
}
200+
}
201+
// Check for extension exclusions
202+
if info != nil && info.Mode().IsRegular() {
203+
ext := strings.ToUpper(filepath.Ext(info.Name()))
204+
if _, exists := this.exext[ext]; exists {
205+
return nil
206+
}
207+
}
208+
}
209+
210+
// Send event
211+
if block {
212+
out <- NewEvent(e, this.name, relpath, info)
213+
} else {
214+
select {
215+
case out <- NewEvent(e, this.name, relpath, info):
216+
// No-op
217+
default:
218+
return ErrChannelBlocked.With(this.name)
219+
}
220+
}
221+
222+
// Return success
223+
return nil
224+
}
225+
226+
// Translate notify types to internal types
227+
func toEventType(e notify.Event, info fs.FileInfo) EventType {
228+
switch e {
229+
case notify.Create:
230+
if info != nil {
231+
return EVENT_TYPE_ADDED
232+
}
233+
case notify.Remove:
234+
return EVENT_TYPE_REMOVED
235+
case notify.Rename:
236+
if info != nil {
237+
return EVENT_TYPE_ADDED | EVENT_TYPE_RENAMED
238+
} else {
239+
return EVENT_TYPE_REMOVED | EVENT_TYPE_RENAMED
240+
}
241+
case notify.Write:
242+
if info != nil {
243+
return EVENT_TYPE_ADDED | EVENT_TYPE_CHANGED
244+
}
245+
}
246+
247+
// Ignore unhandled cases
248+
return EVENT_TYPE_NONE
249+
}
250+
*/

pkg/indexer/indexer_test.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package indexer_test
2+
3+
import (
4+
"context"
5+
"sync"
6+
"testing"
7+
"time"
8+
9+
. "github.com/mutablelogic/go-sqlite/pkg/indexer"
10+
)
11+
12+
const (
13+
TEST_PATH_1 = "../../.."
14+
)
15+
16+
func Test_Indexer_000(t *testing.T) {
17+
indexer, err := NewIndexer("test", TEST_PATH_1)
18+
if err != nil {
19+
t.Fatal(err)
20+
} else {
21+
t.Log(indexer)
22+
}
23+
}
24+
25+
func Test_Indexer_001(t *testing.T) {
26+
// Create channel for errors
27+
errs, cancel := catchErrors(t)
28+
defer cancel()
29+
30+
// Create indexer
31+
indexer, err := NewIndexer("test", TEST_PATH_1)
32+
if err != nil {
33+
t.Fatal(err)
34+
}
35+
36+
// Create context for running the indexer
37+
ctx, cancel2 := context.WithTimeout(context.Background(), 5*time.Second)
38+
defer cancel2()
39+
40+
// Run indexer in background and end when context done
41+
var wg sync.WaitGroup
42+
wg.Add(1)
43+
go func() {
44+
defer wg.Done()
45+
if err := indexer.Run(ctx, errs); err != nil {
46+
t.Error(err)
47+
}
48+
}()
49+
50+
// Queue up an indexing operation
51+
indexer.Exclude("/waveshare")
52+
if err := indexer.Walk(ctx); err != nil {
53+
t.Error(err)
54+
}
55+
56+
// Wait for end of goroutine
57+
wg.Wait()
58+
}
59+
60+
////////////////////////////////////////////////////////////////////////////////
61+
// PRIVATE METHODS
62+
63+
// catchErrors returns an error channel and a function to cancel catching the errors
64+
func catchErrors(t *testing.T) (chan<- error, context.CancelFunc) {
65+
var wg sync.WaitGroup
66+
67+
errs := make(chan error)
68+
ctx, cancel := context.WithCancel(context.Background())
69+
wg.Add(1)
70+
go func(ctx context.Context) {
71+
defer wg.Done()
72+
for {
73+
select {
74+
case err := <-errs:
75+
t.Error(err)
76+
case <-ctx.Done():
77+
return
78+
}
79+
}
80+
}(ctx)
81+
82+
return errs, func() {
83+
cancel()
84+
wg.Wait()
85+
close(errs)
86+
}
87+
}

0 commit comments

Comments
 (0)