From 28e43c7356d796e18d2f818d100078c5aa31c14c Mon Sep 17 00:00:00 2001 From: ewy Date: Wed, 29 Apr 2026 01:59:23 +0200 Subject: add executable runner --- main.go | 2 ++ motd/motd.go | 2 ++ order/order.go | 13 +++++++++++++ runner/base.go | 28 ++++++++++++++++++++++++++-- runner/exc/exc.go | 6 ++++++ runner/exc/runner.go | 42 ++++++++++++++++++++++++++++++++++++++++++ runner/exc/target.go | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ runner/shell/shell.go | 2 +- runner/shell/target.go | 2 +- 9 files changed, 143 insertions(+), 4 deletions(-) create mode 100644 runner/exc/exc.go create mode 100644 runner/exc/runner.go create mode 100644 runner/exc/target.go diff --git a/main.go b/main.go index 7909274..045e5e0 100644 --- a/main.go +++ b/main.go @@ -14,6 +14,7 @@ import ( "pik/model" "pik/paths" "pik/run" + "pik/runner/exc" "pik/runner/gnumake" "pik/runner/just" "pik/runner/python" @@ -49,6 +50,7 @@ var indexers = []model.Indexer{ var runners = []model.Runner{ shell.Runner, python.Python, + exc.Exc, } // hydrators are ran when the menu is required diff --git a/motd/motd.go b/motd/motd.go index bb64606..10eacd2 100644 --- a/motd/motd.go +++ b/motd/motd.go @@ -21,6 +21,8 @@ var Messages = []string{ "create a target with the same base name but containing .override. to prefer it over the other during invocations", "pik crawls both the regular and symlink evaluated locations, if they are different", "unsure about what you're doing? use --dry to check what you're running", + "use --edit to open a target in your $EDITOR", + "pik indexes executable files too, if you want to run something arbitrary", } func One() string { diff --git a/order/order.go b/order/order.go index 40e6e73..83c7dcb 100644 --- a/order/order.go +++ b/order/order.go @@ -31,6 +31,7 @@ func FromFile(f fs.FS, path string) (Order, error) { } func FromReader(r io.Reader) (Order, error) { + o := &Order{} scanner := bufio.NewScanner(r) scanner.Split(bufio.ScanLines) for scanner.Scan() { @@ -45,6 +46,18 @@ func FromReader(r io.Reader) (Order, error) { continue } } + + spl := strings.SplitN(line, "#", 2) + + e := &Element{ + Identifier: identity.New(spl[0]), + } + if len(spl) > 1 { + e.Description = spl[1] + } + + o.Elements = append(o.Elements, *e) } + return *o, nil } diff --git a/runner/base.go b/runner/base.go index c9825ae..e1fef66 100644 --- a/runner/base.go +++ b/runner/base.go @@ -1,15 +1,35 @@ package runner import ( + "path/filepath" "pik/identity" + "pik/indexers/pikdex" "pik/model" + "slices" + "strings" ) // BaseTarget is an embeddable type which contains some of the information we need for (almost) every target. type BaseTarget struct { identity.Identity MyTags model.Tags - Sub []string + MySub []string +} + +func SubFromFile(file string) []string { + _, filename := filepath.Split(file) + var sub []string + split := strings.Split(file, "/") + for _, p := range split { + if slices.Contains(pikdex.Roots, p) { + continue + } + if filename == p { + continue + } + sub = append(sub, p) + } + return sub } func (t *BaseTarget) Tags() model.Tags { @@ -29,7 +49,7 @@ func (b *BaseTarget) Visible() bool { } func (b *BaseTarget) Invocation(src *model.Source) []string { - return append([]string{src.Identity.Reduced}, append(b.Sub, b.Identity.Reduced)...) + return append([]string{src.Identity.Reduced}, append(b.MySub, b.Identity.Reduced)...) } func Hydrated[T model.Target](in T) BaseHydration[T] { @@ -53,3 +73,7 @@ func (b *BaseHydration[T]) Description() string { func (b *BaseHydration[T]) Target() model.Target { return b.Self } + +func (b *BaseTarget) Sub() []string { + return b.MySub +} diff --git a/runner/exc/exc.go b/runner/exc/exc.go new file mode 100644 index 0000000..18d5214 --- /dev/null +++ b/runner/exc/exc.go @@ -0,0 +1,6 @@ +package exc + +type exc struct { +} + +var Exc = &exc{} diff --git a/runner/exc/runner.go b/runner/exc/runner.go new file mode 100644 index 0000000..4b0c2e6 --- /dev/null +++ b/runner/exc/runner.go @@ -0,0 +1,42 @@ +package exc + +import ( + "io/fs" + "path/filepath" + "pik/identity" + "pik/model" + "pik/runner" + "pik/spool" +) + +func (e *exc) Hydrate(target model.Target) (model.HydratedTarget, error) { + return &Hydrated{ + BaseHydration: &runner.BaseHydration[*Executable]{ + Self: target.(*Executable), + }, + }, nil +} + +func (e *exc) Wants(fs fs.FS, file string, entry fs.DirEntry) (bool, error) { + if entry.IsDir() { + return false, nil + } + info, err := entry.Info() + if err != nil { + spool.Warn("%v\n", err) + } + return info.Mode()&0100 != 0, nil +} + +func (e *exc) CreateTarget(fs fs.FS, source string, file string, entry fs.DirEntry) (model.Target, error) { + _, filename := filepath.Split(file) + return &Executable{ + + BaseTarget: runner.BaseTarget{ + Identity: identity.New(entry.Name()), + MyTags: model.TagsFromFilename(filename), + MySub: runner.SubFromFile(file), + }, + Path: filepath.Join(source, file), + }, nil +} diff --git a/runner/exc/target.go b/runner/exc/target.go new file mode 100644 index 0000000..c9d20b8 --- /dev/null +++ b/runner/exc/target.go @@ -0,0 +1,50 @@ +package exc + +import ( + "os/exec" + "pik/describe" + "pik/model" + "pik/runner" + "pik/spool" +) + +type Executable struct { + runner.BaseTarget + Path string +} + +type Hydrated struct { + *runner.BaseHydration[*Executable] +} + +func (h *Hydrated) Icon() string { + return "\uEAE8" +} + +func (h *Hydrated) Description(src *model.HydratedSource) string { + d, err := describe.Describe(h.Self, h.Self.Path) + if err != nil { + spool.Warn("%v\n", err) + } + return d +} + +func (e *Executable) Create(s *model.Source) *exec.Cmd { + return exec.Command(e.Path) +} + +func (e *Executable) Label() string { + return e.Identity.Full +} + +func (e *Executable) Hydrate(src *model.Source) (model.HydratedTarget, error) { + return &Hydrated{ + BaseHydration: &runner.BaseHydration[*Executable]{ + Self: e, + }, + }, nil +} + +func (e *Executable) File(src *model.Source) string { + return e.Path +} diff --git a/runner/shell/shell.go b/runner/shell/shell.go index f8e353c..c4513c9 100644 --- a/runner/shell/shell.go +++ b/runner/shell/shell.go @@ -99,7 +99,7 @@ func (s *shell) CreateTarget(fs fs.FS, src string, file string, _ fs.DirEntry) ( BaseTarget: runner.BaseTarget{ Identity: identity.New(filename), MyTags: model.TagsFromFilename(filename), - Sub: sub, + MySub: sub, }, Shell: shell, Script: filepath.Join(src, file), diff --git a/runner/shell/target.go b/runner/shell/target.go index 189bcb7..9c53617 100644 --- a/runner/shell/target.go +++ b/runner/shell/target.go @@ -25,7 +25,7 @@ func (s *Target) Hydrate(_ *model.Source) (model.HydratedTarget, error) { } func (s *Target) Sub() []string { - return s.BaseTarget.Sub + return s.BaseTarget.MySub } func (s *Target) Label() string { -- cgit v1.3.1