diff options
| -rw-r--r-- | widget/widget.go | 118 |
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 +} |
