From 0fce6c8c2cadf940d70dd7a45622f06c24d14bda Mon Sep 17 00:00:00 2001
From: Volker Schukai <volker.schukai@schukai.com>
Date: Sat, 9 Sep 2023 22:44:21 +0200
Subject: [PATCH] chore: setup project

---
 .idea/.gitignore             |  8 ++++
 .idea/aws.xml                | 17 ++++++++
 .idea/libraries/fs.xml       |  9 ++++
 .idea/libraries/testdata.xml | 36 +++++++++++++++
 .idea/libraries/zoneinfo.xml |  9 ++++
 .idea/markdown.xml           |  9 ++++
 .idea/misc.xml               |  6 +++
 .idea/modules.xml            |  8 ++++
 .idea/vcs.xml                |  6 +++
 .idea/watch.iml              | 10 +++++
 go.mod                       |  5 +++
 go.sum                       |  4 ++
 watch.go                     | 85 ++++++++++++++++++++++++++++++++++++
 watch_test.go                | 47 ++++++++++++++++++++
 14 files changed, 259 insertions(+)
 create mode 100644 .idea/.gitignore
 create mode 100644 .idea/aws.xml
 create mode 100644 .idea/libraries/fs.xml
 create mode 100644 .idea/libraries/testdata.xml
 create mode 100644 .idea/libraries/zoneinfo.xml
 create mode 100644 .idea/markdown.xml
 create mode 100644 .idea/misc.xml
 create mode 100644 .idea/modules.xml
 create mode 100644 .idea/vcs.xml
 create mode 100644 .idea/watch.iml
 create mode 100644 go.sum
 create mode 100644 watch.go
 create mode 100644 watch_test.go

diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/aws.xml b/.idea/aws.xml
new file mode 100644
index 0000000..ec328d0
--- /dev/null
+++ b/.idea/aws.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="accountSettings">
+    <option name="activeProfile" value="profile:default" />
+    <option name="activeRegion" value="eu-west-1" />
+    <option name="recentlyUsedProfiles">
+      <list>
+        <option value="profile:default" />
+      </list>
+    </option>
+    <option name="recentlyUsedRegions">
+      <list>
+        <option value="eu-west-1" />
+      </list>
+    </option>
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/libraries/fs.xml b/.idea/libraries/fs.xml
new file mode 100644
index 0000000..8b6d565
--- /dev/null
+++ b/.idea/libraries/fs.xml
@@ -0,0 +1,9 @@
+<component name="libraryTable">
+  <library name="fs">
+    <CLASSES>
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/src/html/template/testdata/fs.zip!/" />
+    </CLASSES>
+    <JAVADOC />
+    <SOURCES />
+  </library>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/testdata.xml b/.idea/libraries/testdata.xml
new file mode 100644
index 0000000..35c65e8
--- /dev/null
+++ b/.idea/libraries/testdata.xml
@@ -0,0 +1,36 @@
+<component name="libraryTable">
+  <library name="testdata">
+    <CLASSES>
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/src/archive/zip/testdata/time-22738.zip!/" />
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/src/archive/zip/testdata/crc32-not-streamed.zip!/" />
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/src/archive/zip/testdata/time-win7.zip!/" />
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/src/archive/zip/testdata/test-trailing-junk.zip!/" />
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/src/archive/zip/testdata/readme.zip!/" />
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/src/archive/zip/testdata/zip64.zip!/" />
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/src/archive/zip/testdata/utf8-winzip.zip!/" />
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/src/archive/zip/testdata/time-7zip.zip!/" />
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/src/archive/zip/testdata/winxp.zip!/" />
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/src/archive/zip/testdata/utf8-infozip.zip!/" />
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/src/archive/zip/testdata/time-osx.zip!/" />
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/src/archive/zip/testdata/test.zip!/" />
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/src/archive/zip/testdata/time-infozip.zip!/" />
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/src/archive/zip/testdata/time-go.zip!/" />
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/src/archive/zip/testdata/utf8-osx.zip!/" />
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/src/archive/zip/testdata/zip64-2.zip!/" />
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/src/archive/zip/testdata/test-prefix.zip!/" />
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/src/archive/zip/testdata/dupdir.zip!/" />
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/src/archive/zip/testdata/go-with-datadesc-sig.zip!/" />
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/src/archive/zip/testdata/unix.zip!/" />
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/src/archive/zip/testdata/utf8-winrar.zip!/" />
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/src/archive/zip/testdata/utf8-7zip.zip!/" />
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/src/archive/zip/testdata/dd.zip!/" />
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/src/archive/zip/testdata/symlink.zip!/" />
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/src/archive/zip/testdata/time-winzip.zip!/" />
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/src/archive/zip/testdata/time-winrar.zip!/" />
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/src/archive/zip/testdata/subdir.zip!/" />
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/src/archive/zip/testdata/test-baddirsz.zip!/" />
+    </CLASSES>
+    <JAVADOC />
+    <SOURCES />
+  </library>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/zoneinfo.xml b/.idea/libraries/zoneinfo.xml
new file mode 100644
index 0000000..4d0d4c0
--- /dev/null
+++ b/.idea/libraries/zoneinfo.xml
@@ -0,0 +1,9 @@
+<component name="libraryTable">
+  <library name="zoneinfo">
+    <CLASSES>
+      <root url="jar://$PROJECT_DIR$/.devenv/profile/share/go/lib/time/zoneinfo.zip!/" />
+    </CLASSES>
+    <JAVADOC />
+    <SOURCES />
+  </library>
+</component>
\ No newline at end of file
diff --git a/.idea/markdown.xml b/.idea/markdown.xml
new file mode 100644
index 0000000..ec0b30f
--- /dev/null
+++ b/.idea/markdown.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="MarkdownSettings">
+    <enabledExtensions>
+      <entry key="MermaidLanguageExtension" value="false" />
+      <entry key="PlantUMLLanguageExtension" value="true" />
+    </enabledExtensions>
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..639900d
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectRootManager">
+    <output url="file://$PROJECT_DIR$/out" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..f355a0c
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/watch.iml" filepath="$PROJECT_DIR$/.idea/watch.iml" />
+    </modules>
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="" vcs="Git" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/watch.iml b/.idea/watch.iml
new file mode 100644
index 0000000..25ed3f6
--- /dev/null
+++ b/.idea/watch.iml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="Go" enabled="true" />
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>
\ No newline at end of file
diff --git a/go.mod b/go.mod
index d1381c6..dae053a 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,8 @@
 module gitlab.schukai.com/oss/libraries/go/utilities/watch
 
 go 1.20
+
+require (
+	golang.org/x/sys v0.12.0 // indirect
+    gopkg.in/fsnotify.v1 v1.6.0
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..8150683
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,4 @@
+golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
+golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
diff --git a/watch.go b/watch.go
new file mode 100644
index 0000000..b7c9791
--- /dev/null
+++ b/watch.go
@@ -0,0 +1,85 @@
+package watch
+
+import (
+	"gopkg.in/fsnotify.v1"
+	"sync"
+)
+
+type WatchEvent int
+
+const (
+	FileChanged WatchEvent = iota
+	FileAdded
+	FileDeleted
+	FileRemoved
+)
+
+type EventHandler func(string, WatchEvent)
+
+type Watch struct {
+	watcher  *fsnotify.Watcher
+	handlers map[string]EventHandler
+	queue    chan string
+	mu       sync.Mutex
+}
+
+func NewWatch() (*Watch, error) {
+	w, err := fsnotify.NewWatcher()
+	if err != nil {
+		return nil, err
+	}
+	return &Watch{
+		watcher:  w,
+		handlers: make(map[string]EventHandler),
+		queue:    make(chan string, 100),
+	}, nil
+}
+
+func (w *Watch) Add(path string, handler EventHandler) error {
+	w.mu.Lock()
+	defer w.mu.Unlock()
+	w.handlers[path] = handler
+	return w.watcher.Add(path)
+}
+
+func (w *Watch) Remove(path string) error {
+	w.mu.Lock()
+	defer w.mu.Unlock()
+	delete(w.handlers, path)
+	return w.watcher.Remove(path)
+}
+
+func (w *Watch) startWorker(concurrency int) {
+	for i := 0; i < concurrency; i++ {
+		go func() {
+			for path := range w.queue {
+				if handler, exists := w.handlers[path]; exists {
+					handler(path, FileChanged)
+				}
+			}
+		}()
+	}
+}
+
+func (w *Watch) Watch(concurrency int) {
+	w.startWorker(concurrency)
+
+	for {
+		select {
+		case event := <-w.watcher.Events:
+			if handler, exists := w.handlers[event.Name]; exists {
+				we := FileChanged // Map fsnotify events to WatchEvent here
+				w.queue <- event.Name
+				handler(event.Name, we)
+			}
+		case err := <-w.watcher.Errors:
+			// Handle errors
+			println("Error:", err)
+		}
+	}
+}
+
+func (w *Watch) Stop() {
+	close(w.queue)
+	w.watcher.Close()
+}
diff --git a/watch_test.go b/watch_test.go
new file mode 100644
index 0000000..9ac9a06
--- /dev/null
+++ b/watch_test.go
@@ -0,0 +1,47 @@
+package watch
+
+import (
+	"io/ioutil"
+	"os"
+	"testing"
+	"time"
+)
+
+func TestWatch(t *testing.T) {
+	w, err := NewWatch()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	tmpFile, err := ioutil.TempFile("", "watch_test")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.Remove(tmpFile.Name())
+
+	called := make(chan bool)
+	handler := func(path string, event WatchEvent) {
+		if path == tmpFile.Name() && event == FileChanged {
+			called <- true
+		}
+	}
+
+	if err := w.Add(tmpFile.Name(), handler); err != nil {
+		t.Fatal(err)
+	}
+
+	go w.Watch(1)
+	defer w.Stop()
+
+	_, err = tmpFile.WriteString("test")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	select {
+	case <-called:
+		// Success
+	case <-time.After(time.Second * 2):
+		t.Fatal("Event handler not called")
+	}
+}
-- 
GitLab