reveil/model/routine.go

173 lines
3.3 KiB
Go

package reveil
import (
"bytes"
"crypto/sha512"
"fmt"
"io/fs"
"io/ioutil"
"log"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"git.nemunai.re/nemunaire/reveil/config"
)
type RoutineStep struct {
Delay uint64 `json:"delay"`
Action string `json:"action"`
Args []string `json:"args,omitempty"`
}
func (s *RoutineStep) GetAction(cfg *config.Config) (*Action, error) {
return LoadAction(cfg, s.Action)
}
type Routine struct {
Id Identifier `json:"id"`
Name string `json:"name"`
Path string `json:"path"`
Steps []RoutineStep `json:"steps"`
}
func LoadRoutine(path string, cfg *config.Config) ([]RoutineStep, error) {
ds, err := ioutil.ReadDir(path)
if err != nil {
return nil, err
}
actionsDir, err := filepath.Abs(cfg.ActionsDir)
if err != nil {
return nil, err
}
var steps []RoutineStep
for _, f := range ds {
fullpath := filepath.Join(path, f.Name())
if f.Mode()&os.ModeSymlink == 0 {
continue
}
dst, err := os.Readlink(fullpath)
if err != nil {
return nil, err
}
if adst, err := filepath.Abs(filepath.Join(path, dst)); err == nil {
dst = adst
}
args := strings.Split(f.Name(), "_")
delay, err := strconv.ParseUint(args[0], 10, 64)
step := RoutineStep{
Delay: delay,
Action: strings.TrimPrefix(dst, actionsDir+"/"),
}
if len(args) > 1 {
step.Args = args[1:]
}
steps = append(steps, step)
}
return steps, nil
}
func LoadRoutineFromId(id Identifier, cfg *config.Config) (*Routine, error) {
routines, err := LoadRoutines(cfg)
if err != nil {
return nil, err
}
for _, routine := range routines {
if bytes.Equal(routine.Id, id) {
return routine, nil
}
}
return nil, fmt.Errorf("Unable to find routine %x", id)
}
func LoadRoutines(cfg *config.Config) (routines []*Routine, err error) {
err = filepath.Walk(cfg.RoutinesDir, func(path string, d fs.FileInfo, err error) error {
if d.IsDir() && path != cfg.RoutinesDir {
hash := sha512.Sum512([]byte(path))
// Explore directory
steps, err := LoadRoutine(path, cfg)
if err != nil {
log.Printf("Invalid routine directory (trying to walk through %s): %s", path, err.Error())
// Ignore invalid routines
return nil
}
routines = append(routines, &Routine{
Id: hash[:],
Name: d.Name(),
Path: path,
Steps: steps,
})
}
return nil
})
return
}
func (r *Routine) Rename(newName string) error {
newPath := filepath.Join(filepath.Dir(r.Path), newName)
err := os.Rename(
r.Path,
newPath,
)
if err != nil {
return err
}
r.Path = newPath
return nil
}
func (a *Routine) Remove() error {
return os.Remove(a.Path)
}
func (a *Routine) Launch(cfg *config.Config) error {
for _, s := range a.Steps {
act, err := s.GetAction(cfg)
if err != nil {
log.Printf("Unable to get action: %s: %s", s.Action, err.Error())
continue
}
settings, err := ReadSettings(cfg.SettingsFile)
if err != nil {
log.Printf("Unable to read settings: %s", err.Error())
continue
}
time.Sleep(time.Duration(s.Delay) * time.Second)
cmd, err := act.Launch(settings)
if err != nil {
log.Printf("Unable to launch the action %q: %s", s.Action, err.Error())
continue
}
err = cmd.Wait()
if err != nil {
log.Printf("Something goes wrong when waiting for the action %q's end: %s", s.Action, err.Error())
}
}
return nil
}