diff options
| -rw-r--r-- | cache/cache.go | 27 | ||||
| -rw-r--r-- | cache/cache_test.go | 2 | ||||
| -rw-r--r-- | indexers/pikdex/hydrate.go | 2 | ||||
| -rw-r--r-- | indexers/pikdex/hydrate_test.go | 3 | ||||
| -rw-r--r-- | indexers/pikdex/index.go | 26 | ||||
| -rw-r--r-- | indexers/pikdex/meta.go | 21 | ||||
| -rw-r--r-- | main.go | 10 | ||||
| -rw-r--r-- | model/multi.go | 73 | ||||
| -rw-r--r-- | model/new.go | 98 | ||||
| -rw-r--r-- | model/source.go | 10 | ||||
| -rw-r--r-- | paths/paths.go | 17 |
11 files changed, 198 insertions, 91 deletions
diff --git a/cache/cache.go b/cache/cache.go index 9f25af0..7121f65 100644 --- a/cache/cache.go +++ b/cache/cache.go @@ -54,12 +54,12 @@ func (e Entry) String() string { // LoadFile creates a Cache from a file or an empty one if the file does not exist // this handles opening a reader for Unmarshal -func LoadFile(root fs.FS, path string) (*Cache, error) { +func LoadFile(root fs.FS, path string) (Cache, error) { fd, err := root.Open(path) if errors.Is(err, fs.ErrNotExist) { - return nil, nil + return Cache{}, nil } else if err != nil { - return nil, err + return Cache{}, err } if fd != nil { defer fd.Close() @@ -68,7 +68,7 @@ func LoadFile(root fs.FS, path string) (*Cache, error) { } // Unmarshal attempts to create a Cache from reader content -func Unmarshal(r io.Reader) (*Cache, error) { +func Unmarshal(r io.Reader) (Cache, error) { c := &Cache{} scanner := bufio.NewScanner(r) for scanner.Scan() { @@ -88,7 +88,7 @@ func Unmarshal(r io.Reader) (*Cache, error) { } c.Entries = append(c.Entries, *entry) } - return c, nil + return *c, nil } // Marshal returns the file representation of the Cache @@ -119,16 +119,13 @@ func New(st *model.State) *Cache { // MergeAndSave creates a cache from a state, combines it with // a potential saved context file, and writes it to disk func MergeAndSave(in *model.State) error { - root := "/" - f := os.DirFS(root) - // remove leading slash from the dirfs - loaded, err := LoadFile(f, strings.TrimPrefix(paths.ContextsFile.String(), "/")) + loaded, err := Get() if err != nil { return err } insert := New(in) result := loaded.Merge(insert) - if loaded == nil { + if loaded.Entries == nil { return SaveFile(paths.ContextsFile.String(), result) } if slices.Equal(loaded.Entries, result.Entries) { @@ -157,12 +154,12 @@ func Save(w io.Writer, loaded *Cache) error { } // LoadState creates a state with model.NewState based on cache content -func LoadState(f fs.FS, cache *Cache, indexers []model.Indexer, runners []model.Runner) (*model.State, []error) { +func LoadState(f fs.FS, data map[string]*model.SourceData, cache Cache, indexers []model.Indexer, runners []model.Runner) (*model.State, []error) { var locs []string for _, e := range cache.Entries { locs = append(locs, e.Path) } - return model.NewState(f, locs, indexers, runners) + return model.NewState(f, locs, data, indexers, runners) } // Strip removes the needle's entries from the receiver's entries when they have matching paths. @@ -182,3 +179,9 @@ outer: Entries: result, } } + +func Get() (Cache, error) { + f := os.DirFS(paths.CacheDir.String()) + loaded, err := LoadFile(f, strings.TrimPrefix(paths.ContextsFileName, "/")) + return loaded, err +} diff --git a/cache/cache_test.go b/cache/cache_test.go index a58bf95..59d4269 100644 --- a/cache/cache_test.go +++ b/cache/cache_test.go @@ -227,7 +227,7 @@ func TestInsertNonExistent(t *testing.T) { func TestLoadState(t *testing.T) { paths.SetAll("/pik") defer paths.Reset() - c := &Cache{ + c := Cache{ Entries: []Entry{ {Path: "/asdf", Label: "hjkl"}, }, diff --git a/indexers/pikdex/hydrate.go b/indexers/pikdex/hydrate.go index 1f71992..a0dfc2b 100644 --- a/indexers/pikdex/hydrate.go +++ b/indexers/pikdex/hydrate.go @@ -6,7 +6,7 @@ import ( ) func (u *pikdex) Mod(src *model.Source, result *model.HydratedSource) error { - mod := u.mods[strings.TrimSuffix(src.Path, "/")] + mod := u.Data[strings.TrimSuffix(src.Path, "/")] if mod.Path != "" { if mod.Aliases != nil { result.Aliases = append(result.Aliases, mod.Aliases...) diff --git a/indexers/pikdex/hydrate_test.go b/indexers/pikdex/hydrate_test.go index ba5e1bc..cd2433a 100644 --- a/indexers/pikdex/hydrate_test.go +++ b/indexers/pikdex/hydrate_test.go @@ -3,6 +3,7 @@ package pikdex import ( + "github.com/ewy1/pik/model" "github.com/ewy1/pik/runner" "github.com/stretchr/testify/assert" "testing" @@ -11,7 +12,7 @@ import ( func TestHydrate(t *testing.T) { aliases := []string{"alias1", "alias2"} p := pikdex{ - mods: map[string]*SourceData{ + Data: map[string]*model.SourceData{ "asdf": { Aliases: aliases, Icon: "I", diff --git a/indexers/pikdex/index.go b/indexers/pikdex/index.go index 1a9b38a..15a74f4 100644 --- a/indexers/pikdex/index.go +++ b/indexers/pikdex/index.go @@ -35,19 +35,11 @@ func (u *pikdex) Init() error { return nil } -var Indexer = &pikdex{mods: make(map[string]*SourceData)} +var Indexer = &pikdex{Data: make(map[string]*model.SourceData)} type pikdex struct { sync.Mutex - mods map[string]*SourceData -} - -type SourceData struct { - Aliases []string - Icon string - Path string - Wants []string - Includes []string + Data map[string]*model.SourceData } func (u *pikdex) Index(absPath string, f fs.FS, runners []model.Runner) ([]model.Target, error) { @@ -57,14 +49,14 @@ func (u *pikdex) Index(absPath string, f fs.FS, runners []model.Runner) ([]model } var targets []model.Target u.Lock() - mod := u.mods[absPath] + mod := u.Data[absPath] u.Unlock() if mod == nil { u.Lock() - u.mods[absPath] = &SourceData{ + u.Data[absPath] = &model.SourceData{ Path: absPath, } - mod = u.mods[absPath] + mod = u.Data[absPath] u.Unlock() } err = fs.WalkDir(f, root, func(p string, d fs.DirEntry, err error) error { @@ -99,12 +91,12 @@ func (u *pikdex) Index(absPath string, f fs.FS, runners []model.Runner) ([]model for _, r := range runners { wants, err := r.Wants(f, p, d) if err != nil { - spool.Warn("%v\n", err) + _, _ = spool.Warn("%v\n", err) } if wants { t, err := r.CreateTarget(f, absPath, p, d) if err != nil { - spool.Warn("%v\n", err) + _, _ = spool.Warn("%v\n", err) } sub := t.Sub() if strings.Join(sub, " ") == t.ShortestId() { @@ -123,13 +115,13 @@ func (u *pikdex) Index(absPath string, f fs.FS, runners []model.Runner) ([]model return nil } if err != nil { - spool.Warn("%v\n", err) + _, _ = spool.Warn("%v\n", err) } } return nil }) u.Lock() - u.mods[absPath] = mod + u.Data[absPath] = mod u.Unlock() return targets, err diff --git a/indexers/pikdex/meta.go b/indexers/pikdex/meta.go index ef2c029..69b3f92 100644 --- a/indexers/pikdex/meta.go +++ b/indexers/pikdex/meta.go @@ -3,22 +3,29 @@ package pikdex import ( "github.com/charmbracelet/lipgloss" "github.com/ewy1/pik/describe" + "github.com/ewy1/pik/model" "strings" ) -type MetaSetter func(s *SourceData, content string) +type MetaSetter func(s *model.SourceData, content string) var MetaFiles = map[string]MetaSetter{ - ".wants": func(s *SourceData, content string) { - s.Wants = contentLines(content) + ".want": func(s *model.SourceData, content string) { + s.Wants = append(s.Wants, contentLines(content)...) }, - ".includes": func(s *SourceData, content string) { - s.Includes = contentLines(content) + ".wants": func(s *model.SourceData, content string) { + s.Wants = append(s.Wants, contentLines(content)...) }, - ".alias": func(s *SourceData, content string) { + ".include": func(s *model.SourceData, content string) { + s.Includes = append(s.Includes, contentLines(content)...) + }, + ".includes": func(s *model.SourceData, content string) { + s.Includes = append(s.Includes, contentLines(content)...) + }, + ".alias": func(s *model.SourceData, content string) { s.Aliases = contentLines(content) }, - ".icon": func(s *SourceData, content string) { + ".icon": func(s *model.SourceData, content string) { lines := contentLines(content) if len(lines) == 0 { return @@ -148,9 +148,9 @@ func pik() spool.ExitCode { var st *model.State var stateErrors []error - var c *cache.Cache + var c cache.Cache if !*flags.All { - st, stateErrors = model.NewState(fs, locs, indexers, runners) + st, stateErrors = model.NewState(fs, locs, pikdex.Indexer.Data, indexers, runners) go func() { if len(stateErrors) > 0 { return @@ -162,12 +162,12 @@ func pik() spool.ExitCode { }() } else { - c, err = cache.LoadFile(fs, paths.ContextsFile.String()[1:]) + c, err = cache.Get() if err != nil { _, _ = spool.Warn("%v\n", err) return spool.CacheReadFailure } - st, stateErrors = cache.LoadState(fs, c, indexers, runners) + st, stateErrors = cache.LoadState(fs, pikdex.Indexer.Data, c, indexers, runners) } if stateErrors != nil { _, _ = spool.Warn("%v\n", stateErrors) @@ -212,7 +212,7 @@ func pik() spool.ExitCode { // TODO: Move auto-all logic into Search? if !*flags.All && result.Target == nil && len(result.Args) > 0 && SourcesWithoutResults == nil && !ForceConfirm { ForceConfirm = true - SourcesWithoutResults = c + SourcesWithoutResults = &c return pik() } diff --git a/model/multi.go b/model/multi.go new file mode 100644 index 0000000..35b792f --- /dev/null +++ b/model/multi.go @@ -0,0 +1,73 @@ +package model + +import ( + "errors" + "io/fs" + "path/filepath" + "slices" + "strings" +) + +func Include(root fs.FS, st *State, data map[string]*SourceData, indexers []Indexer, runners []Runner) error { + for _, s := range st.Sources { + if data[s.Path] == nil || data[s.Path].Includes == nil { + continue + } + for _, w := range data[s.Path].Includes { + if IsRelative(w) { + p, err := filepath.Abs(filepath.Join(s.Path, w)) + if err != nil { + return err + } + + // check if source is already included + for _, maybeSource := range st.Sources { + if maybeSource.Path == p { + continue + } + } + + src, errs := NewSource(root, p, indexers, runners) + if len(errs) > 0 { + return errors.Join(errs...) + } + s.Targets = append(s.Targets, src.Targets...) + } + } + } + return nil +} + +func Wants(root fs.FS, st *State, data map[string]*SourceData, indexers []Indexer, runners []Runner) error { + for i, s := range st.Sources { + if data[s.Path] == nil || data[s.Path].Wants == nil { + continue + } + for _, w := range data[s.Path].Wants { + if IsRelative(w) { + p, err := filepath.Abs(filepath.Join(s.Path, w)) + if err != nil { + return err + } + + // check if source is already included + for _, maybeSource := range st.Sources { + if maybeSource.Path == p { + continue + } + } + + src, errs := NewSource(root, p, indexers, runners) + if len(errs) > 0 { + return errors.Join(errs...) + } + st.Sources = slices.Insert(st.Sources, i+1, src) + } + } + } + return nil +} + +func IsRelative(path string) bool { + return strings.HasPrefix(path, ".") +} diff --git a/model/new.go b/model/new.go index 94e19a8..fc57ad0 100644 --- a/model/new.go +++ b/model/new.go @@ -4,13 +4,58 @@ import ( "errors" "github.com/ewy1/pik/flags" "github.com/ewy1/pik/identity" + "github.com/ewy1/pik/spool" "io/fs" "path/filepath" "strings" "sync" ) -func NewState(rootFs fs.FS, locations []string, indexers []Indexer, runners []Runner) (*State, []error) { +func NewSource(rootFs fs.FS, loc string, indexers []Indexer, runners []Runner) (*Source, []error) { + var errs []error + _, dirName := filepath.Split(strings.TrimSuffix(loc, "/")) + src := &Source{ + Path: loc, + Identity: identity.New(dirName), + Whitelists: make(map[string][]string), + } + loc = strings.TrimSuffix(loc, "/") + loc = strings.TrimPrefix(loc, "/") + + if loc == "" { + return nil, nil + } + + locationWg := sync.WaitGroup{} + var targets = make([][]Target, len(indexers), len(indexers)) + for ti, indexer := range indexers { + locationWg.Go(func() { + subFs, err := fs.Sub(rootFs, loc) + if err != nil && !errors.Is(err, fs.ErrNotExist) { + errs = append(errs, err) + return + } + result, err := indexer.Index("/"+loc, subFs, runners) + if err != nil && !errors.Is(err, fs.ErrNotExist) { + errs = append(errs, err) + return + } + targets[ti] = result + }) + } + locationWg.Wait() + + for _, t := range targets { + if t == nil { + continue + } + src.Targets = append(src.Targets, t...) + } + + return src, errs +} + +func NewState(rootFs fs.FS, locations []string, data map[string]*SourceData, indexers []Indexer, runners []Runner) (*State, []error) { var errs []error st := &State{ All: *flags.All, @@ -19,46 +64,12 @@ func NewState(rootFs fs.FS, locations []string, indexers []Indexer, runners []Ru var sources = make([]*Source, len(locations), len(locations)) for i, loc := range locations { wg.Go(func() { - _, dirName := filepath.Split(strings.TrimSuffix(loc, "/")) - src := &Source{ - Path: loc, - Identity: identity.New(dirName), - Whitelists: make(map[string][]string), + src, err := NewSource(rootFs, loc, indexers, runners) + errs = append(errs, err...) + for _, e := range err { + _, _ = spool.Warn("%v\n", e) } sources[i] = src - loc = strings.TrimSuffix(loc, "/") - loc = strings.TrimPrefix(loc, "/") - - if loc == "" { - return - } - - locationWg := sync.WaitGroup{} - var targets = make([][]Target, len(indexers), len(indexers)) - for ti, indexer := range indexers { - locationWg.Go(func() { - subFs, err := fs.Sub(rootFs, loc) - if err != nil && !errors.Is(err, fs.ErrNotExist) { - errs = append(errs, err) - return - } - result, err := indexer.Index("/"+loc, subFs, runners) - if err != nil && !errors.Is(err, fs.ErrNotExist) { - errs = append(errs, err) - return - } - targets[ti] = result - }) - } - locationWg.Wait() - - for _, t := range targets { - if t == nil { - continue - } - sources[i].Targets = append(sources[i].Targets, t...) - } - }) } @@ -71,5 +82,14 @@ func NewState(rootFs fs.FS, locations []string, indexers []Indexer, runners []Ru st.Sources = append(st.Sources, s) } + err := Wants(rootFs, st, data, indexers, runners) + if err != nil { + errs = append(errs, err) + } + err = Include(rootFs, st, data, indexers, runners) + if err != nil { + errs = append(errs, err) + } + return st, errs } diff --git a/model/source.go b/model/source.go index 246929e..2844d23 100644 --- a/model/source.go +++ b/model/source.go @@ -6,10 +6,20 @@ import ( "github.com/ewy1/pik/spool" ) +// SourceData is data we want to save from a Source before it has been properly instantiated +type SourceData struct { + Aliases []string + Icon string + Path string + Wants []string + Includes []string +} + // Source is a location containing stuff we can run // these get created when we find a makefile, .pik folder, etc. type Source struct { identity.Identity + SourceData Tags Path string Targets []Target diff --git a/paths/paths.go b/paths/paths.go index b714a23..1e6e2f9 100644 --- a/paths/paths.go +++ b/paths/paths.go @@ -9,13 +9,14 @@ import ( ) var ( - Ifs string - ConfigDir = Empty() - CacheDir = Empty() - ContextsFile = Empty() - HomeDir = Empty() - System = Empty() - This = "pik" + Ifs string + ConfigDir = Empty() + CacheDir = Empty() + ContextsFile = Empty() + HomeDir = Empty() + System = Empty() + This = "pik" + ContextsFileName = "contexts" ) var Paths = []*Path{ @@ -50,7 +51,7 @@ func (p *paths) Init() error { HomeDir.Set(xdg.Home) CacheDir.Set(filepath.Join(xdg.CacheHome, This)) ConfigDir.Set(filepath.Join(xdg.ConfigHome, This)) - ContextsFile.Set(path.Join(CacheDir.String(), "contexts")) + ContextsFile.Set(path.Join(CacheDir.String(), ContextsFileName)) System.Set(path.Join(ConfigDir.String())) Ifs = os.Getenv("IFS") |
