Skip to content

Commit 80f9a59

Browse files
committed
Added
1 parent 5b99fce commit 80f9a59

File tree

7 files changed

+610
-92
lines changed

7 files changed

+610
-92
lines changed

cmd/indexer/main.go

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"flag"
6+
"fmt"
7+
"os"
8+
"os/signal"
9+
"strings"
10+
"sync"
11+
"syscall"
12+
"time"
13+
"unicode"
14+
15+
"github.com/mutablelogic/go-sqlite/pkg/indexer"
16+
)
17+
18+
var (
19+
flagInclude = flag.String("include", "", "Paths, names and extensions to include")
20+
flagExclude = flag.String("exclude", "", "Paths, names and extensions to exclude")
21+
)
22+
23+
func main() {
24+
flag.Parse()
25+
if flag.NArg() != 1 {
26+
fmt.Fprintln(os.Stderr, "missing path argument")
27+
os.Exit(-1)
28+
}
29+
path := flag.Arg(0)
30+
if stat, err := os.Stat(path); err != nil {
31+
fmt.Fprintln(os.Stderr, err)
32+
os.Exit(-1)
33+
} else if !stat.IsDir() {
34+
fmt.Fprintln(os.Stderr, "Not a directory")
35+
os.Exit(-1)
36+
}
37+
indexer, err := indexer.NewIndexer("indexer", path)
38+
if err != nil {
39+
fmt.Fprintln(os.Stderr, err)
40+
os.Exit(-1)
41+
}
42+
43+
// Inclusions
44+
for _, include := range strings.FieldsFunc(*flagInclude, sep) {
45+
if err := indexer.Include(include); err != nil {
46+
fmt.Fprintln(os.Stderr, err)
47+
os.Exit(-1)
48+
}
49+
}
50+
51+
// Exclusions
52+
for _, exclude := range strings.FieldsFunc(*flagExclude, sep) {
53+
if err := indexer.Exclude(exclude); err != nil {
54+
fmt.Fprintln(os.Stderr, err)
55+
os.Exit(-1)
56+
}
57+
}
58+
59+
var wg sync.WaitGroup
60+
61+
ctx := HandleSignal()
62+
errs := make(chan error)
63+
wg.Add(1)
64+
go func() {
65+
defer wg.Done()
66+
for {
67+
select {
68+
case err := <-errs:
69+
fmt.Fprintln(os.Stderr, err)
70+
case <-ctx.Done():
71+
return
72+
}
73+
}
74+
}()
75+
76+
wg.Add(1)
77+
go func() {
78+
defer wg.Done()
79+
if err := indexer.Run(ctx, errs); err != nil {
80+
errs <- err
81+
}
82+
}()
83+
84+
wg.Add(1)
85+
go func() {
86+
defer wg.Done()
87+
<-time.After(time.Second)
88+
if err := indexer.Walk(ctx); err != nil {
89+
errs <- err
90+
}
91+
}()
92+
93+
// Wait for all goroutines to finish
94+
wg.Wait()
95+
os.Exit(0)
96+
}
97+
98+
func HandleSignal() context.Context {
99+
// Handle signals - call cancel when interrupt received
100+
ctx, cancel := context.WithCancel(context.Background())
101+
ch := make(chan os.Signal, 1)
102+
signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
103+
go func() {
104+
<-ch
105+
cancel()
106+
}()
107+
return ctx
108+
}
109+
110+
func sep(r rune) bool {
111+
return r == ',' || unicode.IsSpace(r)
112+
}

pkg/indexer/indexer.go

Lines changed: 24 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func (i *Indexer) Run(ctx context.Context, errs chan<- error) error {
7474

7575
in := make(chan notify.EventInfo, defaultCapacity)
7676
if err := notify.Watch(filepath.Join(i.path, "..."), in, notify.Create, notify.Remove, notify.Write, notify.Rename); err != nil {
77-
fmt.Println("err", err)
77+
senderr(errs, err)
7878
return err
7979
}
8080

@@ -156,13 +156,34 @@ func (i *Indexer) Walk(ctx context.Context) error {
156156

157157
// event is used to process an event from the notify
158158
func (i *Indexer) event(ctx context.Context, evt notify.EventInfo) error {
159-
fmt.Println("event", evt)
159+
relpath, err := filepath.Rel(i.path, evt.Path())
160+
if err != nil {
161+
return err
162+
}
163+
switch evt.Event() {
164+
case notify.Create, notify.Write:
165+
info, err := os.Stat(evt.Path())
166+
if err == nil && info.Mode().IsRegular() && i.ShouldVisit(relpath, info) {
167+
fmt.Println("INDEX ", relpath)
168+
}
169+
case notify.Remove, notify.Rename:
170+
info, err := os.Stat(evt.Path())
171+
if err == nil && info.Mode().IsRegular() && i.ShouldVisit(relpath, info) {
172+
fmt.Println("INDEX ", relpath)
173+
} else {
174+
// Always attempt removal from index
175+
fmt.Println("REMOVE ", relpath)
176+
}
177+
}
178+
// Return success
160179
return nil
161180
}
162181

163182
// visit is used to index a file from the indexer
164183
func (i *Indexer) visit(ctx context.Context, abspath, relpath string, info fs.FileInfo) error {
165-
fmt.Println("visited", abspath, relpath, info.IsDir())
184+
if info.Mode().IsRegular() {
185+
fmt.Println("INDEX ", relpath)
186+
}
166187
return nil
167188
}
168189

@@ -178,73 +199,3 @@ func senderr(ch chan<- error, err error) {
178199
}
179200
}
180201
}
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: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
)
1111

1212
const (
13-
TEST_PATH_1 = "../../.."
13+
TEST_PATH_1 = "../../../.."
1414
)
1515

1616
func Test_Indexer_000(t *testing.T) {
@@ -48,7 +48,12 @@ func Test_Indexer_001(t *testing.T) {
4848
}()
4949

5050
// Queue up an indexing operation
51-
indexer.Exclude("/waveshare")
51+
indexer.Exclude("/Library")
52+
indexer.Exclude("/go")
53+
indexer.Exclude("/Work")
54+
indexer.Exclude("/bin")
55+
indexer.Exclude("/Projects")
56+
indexer.Exclude("/Documents")
5257
if err := indexer.Walk(ctx); err != nil {
5358
t.Error(err)
5459
}

pkg/indexer/queue.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package indexer
2+
3+
import (
4+
"io/fs"
5+
"path/filepath"
6+
"sync"
7+
// Package imports
8+
// Import namepaces
9+
//. "github.com/djthorpe/go-errors"
10+
)
11+
12+
///////////////////////////////////////////////////////////////////////////////
13+
// TYPES
14+
15+
type Queue struct {
16+
sync.RWMutex
17+
q []QueueEvent
18+
k map[string]*QueueEvent
19+
}
20+
21+
type QueueEvent struct {
22+
name string
23+
path string
24+
info fs.FileInfo
25+
}
26+
27+
///////////////////////////////////////////////////////////////////////////////
28+
// GLOBALS
29+
30+
const (
31+
defaultQueueCapacity = 1024
32+
)
33+
34+
///////////////////////////////////////////////////////////////////////////////
35+
// LIFECYCLE
36+
37+
// Create a new queue which acts as a buffer between the file indexing
38+
func NewQueue() *Queue {
39+
return NewQueueWithCapacity(defaultQueueCapacity)
40+
}
41+
42+
// Create a new queue which acts as a buffer between the file indexing
43+
// and the rendering which can be slower than the file indexing
44+
func NewQueueWithCapacity(cap int) *Queue {
45+
q := new(Queue)
46+
47+
// Create capacity
48+
if cap == 0 {
49+
q.q = make([]QueueEvent, 0, defaultQueueCapacity)
50+
q.k = make(map[string]*QueueEvent, defaultQueueCapacity)
51+
} else {
52+
q.q = make([]QueueEvent, 0, cap)
53+
q.k = make(map[string]*QueueEvent, cap)
54+
}
55+
56+
// Return success
57+
return q
58+
}
59+
60+
///////////////////////////////////////////////////////////////////////////////
61+
// PUBLIC METHODS
62+
63+
// Add an item to the queue. If the item is already in the queue,
64+
// then it is bumped to the end of the queue
65+
func (q *Queue) Add(name, path string) error {
66+
/* if elem := q.Get(name, path); elem != nil {
67+
// Remove the element from the existing queue
68+
q.pop(elem)
69+
}
70+
// Add the element to the queue
71+
q.push(elem)*/
72+
return nil
73+
}
74+
75+
// Remove an item to the queue. If the item is already in the queue,
76+
// it is removed
77+
func (q *Queue) Remove(name, path string) error {
78+
/* if elem := q.Get(name, path); elem != nil {
79+
// Remove the element from the existing queue
80+
q.pop(elem)
81+
} else {
82+
return ErrNotFound.With(name, path)
83+
}*/
84+
// Return success
85+
return nil
86+
}
87+
88+
// Return a queue event from the queue, or nil
89+
func (q *Queue) Get(name, path string) *QueueEvent {
90+
q.RWMutex.RLock()
91+
defer q.RWMutex.RUnlock()
92+
key := filepath.Join(name, path)
93+
if elem, exists := q.k[key]; exists {
94+
return elem
95+
} else {
96+
return nil
97+
}
98+
}

0 commit comments

Comments
 (0)