2019-10-20 16:02:28 +02:00
|
|
|
package sheets
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2022-08-05 01:26:07 +02:00
|
|
|
"io/fs"
|
2019-10-20 16:02:28 +02:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
cp "github.com/cheat/cheat/internal/cheatpath"
|
|
|
|
"github.com/cheat/cheat/internal/sheet"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Load produces a map of cheatsheet titles to filesystem paths
|
|
|
|
func Load(cheatpaths []cp.Cheatpath) ([]map[string]sheet.Sheet, error) {
|
|
|
|
|
|
|
|
// create a slice of maps of sheets. This structure will store all sheets
|
|
|
|
// that are associated with each cheatpath.
|
|
|
|
sheets := make([]map[string]sheet.Sheet, len(cheatpaths))
|
|
|
|
|
|
|
|
// iterate over each cheatpath
|
|
|
|
for _, cheatpath := range cheatpaths {
|
|
|
|
|
|
|
|
// vivify the map of cheatsheets on this specific cheatpath
|
|
|
|
pathsheets := make(map[string]sheet.Sheet)
|
|
|
|
|
|
|
|
// recursively iterate over the cheatpath, and load each cheatsheet
|
|
|
|
// encountered along the way
|
|
|
|
err := filepath.Walk(
|
|
|
|
cheatpath.Path, func(
|
|
|
|
path string,
|
|
|
|
info os.FileInfo,
|
|
|
|
err error) error {
|
|
|
|
|
|
|
|
// fail if an error occurred while walking the directory
|
|
|
|
if err != nil {
|
2020-03-11 23:51:06 +01:00
|
|
|
return fmt.Errorf("failed to walk path: %v", err)
|
2019-10-20 16:02:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// don't register directories as cheatsheets
|
|
|
|
if info.IsDir() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// calculate the cheatsheet's "title" (the phrase with which it may be
|
|
|
|
// accessed. Eg: `cheat tar` - `tar` is the title)
|
|
|
|
title := strings.TrimPrefix(
|
|
|
|
strings.TrimPrefix(path, cheatpath.Path),
|
2019-11-20 01:10:19 +01:00
|
|
|
string(os.PathSeparator),
|
2019-10-20 16:02:28 +02:00
|
|
|
)
|
|
|
|
|
2019-11-06 00:44:47 +01:00
|
|
|
// ignore hidden files and directories. Otherwise, we'll likely load
|
|
|
|
// .git/* and .DS_Store.
|
|
|
|
//
|
|
|
|
// NB: this is still somewhat brittle in that it will miss files
|
|
|
|
// contained within hidden directories in the middle of a path, though
|
|
|
|
// that should not realistically occur.
|
2019-11-06 01:08:40 +01:00
|
|
|
if strings.HasPrefix(title, ".") || strings.HasPrefix(info.Name(), ".") {
|
2022-08-05 01:26:07 +02:00
|
|
|
// Do not walk hidden directories. This is important,
|
|
|
|
// because it's common for cheatsheets to be stored in
|
|
|
|
// version-control, and a `.git` directory can easily
|
|
|
|
// contain thousands of files.
|
|
|
|
return fs.SkipDir
|
2019-10-20 16:02:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// parse the cheatsheet file into a `sheet` struct
|
2020-11-27 22:39:34 +01:00
|
|
|
s, err := sheet.New(
|
|
|
|
title,
|
|
|
|
cheatpath.Name,
|
|
|
|
path,
|
|
|
|
cheatpath.Tags,
|
|
|
|
cheatpath.ReadOnly,
|
|
|
|
)
|
2019-10-20 16:02:28 +02:00
|
|
|
if err != nil {
|
2020-03-11 23:51:06 +01:00
|
|
|
return fmt.Errorf(
|
|
|
|
"failed to load sheet: %s, path: %s, err: %v",
|
|
|
|
title,
|
|
|
|
path,
|
|
|
|
err,
|
|
|
|
)
|
2019-10-20 16:02:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// register the cheatsheet on its cheatpath, keyed by its title
|
|
|
|
pathsheets[title] = s
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return sheets, fmt.Errorf("failed to load cheatsheets: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// store the sheets on this cheatpath alongside the other cheatsheets on
|
|
|
|
// other cheatpaths
|
|
|
|
sheets = append(sheets, pathsheets)
|
|
|
|
}
|
|
|
|
|
|
|
|
// return the cheatsheets, grouped by cheatpath
|
|
|
|
return sheets, nil
|
|
|
|
}
|