Initial commit

This commit is contained in:
Yuzuki616
2024-09-12 06:04:32 +09:00
commit 3f58fa7f0d
31 changed files with 2814 additions and 0 deletions

8
common/file/file.go Normal file
View File

@@ -0,0 +1,8 @@
package file
import "os"
func IsExist(path string) bool {
_, err := os.Stat(path)
return err == nil || !os.IsNotExist(err)
}

112
common/json/trim.go Normal file
View File

@@ -0,0 +1,112 @@
package json
import (
"bytes"
"io"
)
type TrimNodeReader struct {
r io.Reader
br *bytes.Reader
}
func isNL(c byte) bool {
return c == '\n' || c == '\r'
}
func isWS(c byte) bool {
return c == ' ' || c == '\t' || isNL(c)
}
func consumeComment(s []byte, i int) int {
if i < len(s) && s[i] == '/' {
s[i-1] = ' '
for ; i < len(s) && !isNL(s[i]); i += 1 {
s[i] = ' '
}
}
if i < len(s) && s[i] == '*' {
s[i-1] = ' '
s[i] = ' '
for ; i < len(s); i += 1 {
if s[i] != '*' {
s[i] = ' '
} else {
s[i] = ' '
i++
if i < len(s) {
if s[i] == '/' {
s[i] = ' '
break
}
}
}
}
}
return i
}
func prep(r io.Reader) (s []byte, err error) {
buf := &bytes.Buffer{}
_, err = io.Copy(buf, r)
s = buf.Bytes()
if err != nil {
return
}
i := 0
for i < len(s) {
switch s[i] {
case '"':
i += 1
for i < len(s) {
if s[i] == '"' {
i += 1
break
} else if s[i] == '\\' {
i += 1
}
i += 1
}
case '/':
i = consumeComment(s, i+1)
case ',':
j := i
for {
i += 1
if i >= len(s) {
break
} else if s[i] == '}' || s[i] == ']' {
s[j] = ' '
break
} else if s[i] == '/' {
i = consumeComment(s, i+1)
} else if !isWS(s[i]) {
break
}
}
default:
i += 1
}
}
return
}
// Read acts as a proxy for the underlying reader and cleans p
// of comments and trailing commas preceeding ] and }
// comments are delimitted by // up until the end the line
func (st *TrimNodeReader) Read(p []byte) (n int, err error) {
if st.br == nil {
var s []byte
if s, err = prep(st.r); err != nil {
return
}
st.br = bytes.NewReader(s)
}
return st.br.Read(p)
}
// NewTrimNodeReader New returns an io.Reader acting as proxy to r
func NewTrimNodeReader(r io.Reader) io.Reader {
return &TrimNodeReader{r: r}
}

10
common/maps/maps.go Normal file
View File

@@ -0,0 +1,10 @@
package maps
func Merge[k comparable, v any](m1 map[k]v, m2 ...map[k]v) map[k]v {
for _, m2v := range m2 {
for k2, v2 := range m2v {
m1[k2] = v2
}
}
return m1
}

18
common/slices/slice.go Normal file
View File

@@ -0,0 +1,18 @@
package slices
func Range[t any](sl []t, handle func(i int, v t) (_break bool)) {
for i := range sl {
b := handle(i, sl[i])
if b {
break
}
}
}
func RangeToNew[old, new any](sl []old, handle func(i int, v old) new) []new {
ns := make([]new, len(sl))
for i := range ns {
ns[i] = handle(i, sl[i])
}
return ns
}

75
common/watcher/http.go Normal file
View File

@@ -0,0 +1,75 @@
package watcher
import (
"bytes"
"crypto/sha256"
"fmt"
"io"
"net/http"
"time"
)
type HTTPWatcher struct {
hash [32]byte
url string
interval uint
handler EventHandler
errorHandler ErrorHandler
close chan struct{}
}
func NewHTTPWatcher(url string, interval uint) *HTTPWatcher {
return &HTTPWatcher{
url: url,
interval: interval,
}
}
func (w *HTTPWatcher) handle() error {
rsp, err := http.Get(w.url)
if err != nil {
return fmt.Errorf("request error: %w", err)
}
defer rsp.Body.Close()
b, err := io.ReadAll(rsp.Body)
if err != nil {
return fmt.Errorf("read body error: %w", err)
}
h := sha256.Sum256(b)
if bytes.Equal(w.hash[:], h[:]) {
return nil
}
w.hash = h
err = w.handler(w.url)
if err != nil {
return fmt.Errorf("handle error: %w", err)
}
return nil
}
func (w *HTTPWatcher) SetEventHandler(handler EventHandler) {
w.handler = handler
}
func (w *HTTPWatcher) SetErrorHandler(handler ErrorHandler) {
w.errorHandler = handler
}
func (w *HTTPWatcher) Watch() error {
go func() {
for range time.Tick(time.Duration(w.interval) * time.Second) {
select {
case <-w.close:
return
default:
}
w.errorHandler(w.handle())
}
}()
return nil
}
func (w *HTTPWatcher) Close() error {
close(w.close)
return nil
}

85
common/watcher/local.go Normal file
View File

@@ -0,0 +1,85 @@
package watcher
import (
"fmt"
"path"
"github.com/fsnotify/fsnotify"
)
type LocalWatcher struct {
dir string
filenames []string
handler EventHandler
errorHandler ErrorHandler
watcher *fsnotify.Watcher
close chan struct{}
}
func NewLocalWatcher(dir string, filenames []string) *LocalWatcher {
return &LocalWatcher{
dir: dir,
filenames: filenames,
close: make(chan struct{}),
}
}
func (w *LocalWatcher) SetEventHandler(handler EventHandler) {
w.handler = handler
}
func (w *LocalWatcher) SetErrorHandler(handler ErrorHandler) {
w.errorHandler = handler
}
func (w *LocalWatcher) handle(e fsnotify.Event) error {
if (!e.Has(fsnotify.Write)) && (!e.Has(fsnotify.Create)) {
return nil
}
name := path.Base(e.Name)
file := ""
for _, filename := range w.filenames {
ok, _ := path.Match(filename, name)
if ok {
file = filename
}
}
if len(file) == 0 {
return nil
}
err := w.handler(file)
if err != nil {
return err
}
return nil
}
func (w *LocalWatcher) Watch() error {
watcher, err := fsnotify.NewWatcher()
if err != nil {
return fmt.Errorf("new watcher error: %s", err)
}
go func() {
defer watcher.Close()
for {
select {
case e := <-watcher.Events:
err := w.handle(e)
if err != nil {
w.errorHandler(err)
}
case err := <-watcher.Errors:
if err != nil {
w.errorHandler(err)
}
case <-w.close:
return
}
}
}()
return watcher.Add(w.dir)
}
func (w *LocalWatcher) Close() error {
close(w.close)
return w.watcher.Close()
}

11
common/watcher/watcher.go Normal file
View File

@@ -0,0 +1,11 @@
package watcher
type EventHandler func(filename string) error
type ErrorHandler func(err error)
type Watcher interface {
SetEventHandler(handler EventHandler)
SetErrorHandler(handler ErrorHandler)
Watch() error
Close() error
}