aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--widget/widget.go118
1 files changed, 118 insertions, 0 deletions
diff --git a/widget/widget.go b/widget/widget.go
new file mode 100644
index 0000000..fa8a308
--- /dev/null
+++ b/widget/widget.go
@@ -0,0 +1,118 @@
+package widget
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "os/exec"
+ "strings"
+ "time"
+)
+
+const Version uint = 1
+
+var (
+ ErrUnexceptedType = errors.New("unexcepted type")
+)
+
+type Widget struct {
+ Name string `json:"name"`
+ ID string `json:"id"`
+ Version uint `json:"version"`
+ Path string `json:"-"`
+}
+
+type Method string
+
+func FromJson(data []byte) (*Widget, error) {
+ var widget Widget
+ return &widget, json.Unmarshal(data, &widget)
+}
+
+func Load(path string) ([]*Widget, error) {
+ path = strings.TrimSuffix(path, "/")
+ dir, err := os.ReadDir(path)
+ if err != nil {
+ if !errors.Is(err, os.ErrNotExist) {
+ return nil, err
+ }
+ return nil, os.Mkdir(path, 0755)
+ }
+ return readPluginDir(dir, path+"/")
+}
+
+func readPluginDir(dir []os.DirEntry, path string) ([]*Widget, error) {
+ if !strings.HasSuffix(path, "/") {
+ path += "/"
+ }
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ var widgets []*Widget
+ for _, e := range dir {
+ p := path + e.Name()
+ if e.IsDir() {
+ d, err := os.ReadDir(p)
+ if err != nil {
+ return nil, err
+ }
+ ws, err := readPluginDir(d, p)
+ if err != nil {
+ return nil, err
+ }
+ widgets = append(widgets, ws...)
+ }
+ w := &Widget{
+ Path: p,
+ }
+ c := make(chan any)
+ ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
+ go w.Request(ctx, "", c)
+ select {
+ case <-ctx.Done():
+ cancel()
+ return nil, ctx.Err()
+ case out := <-c:
+ cancel()
+ err, ok := out.(error)
+ if ok {
+ return nil, err
+ }
+ b, ok := out.([]byte)
+ if !ok {
+ return nil, errors.Join(ErrUnexceptedType, fmt.Errorf("got %T instead of []byte", out))
+ }
+ if err = json.Unmarshal(b, w); err != nil {
+ return nil, err
+ }
+ }
+ widgets = append(widgets, w)
+ }
+ return widgets, nil
+}
+
+func (w *Widget) Request(_ context.Context, m Method, c chan<- any, args ...string) {
+ args = append([]string{string(m)}, args...)
+ cmd := exec.Command(fmt.Sprintf("./plugins/%s", w.Path), args...)
+ if cmd.Err != nil {
+ c <- cmd.Err
+ return
+ }
+ wb := new(bytes.Buffer)
+ cmd.Stdout = wb
+ cmd.Stderr = wb
+ if err := cmd.Run(); err != nil {
+ c <- err
+ return
+ }
+ b, err := io.ReadAll(wb)
+ if err != nil {
+ c <- err
+ return
+ }
+ c <- b
+}