mirror of
https://github.com/rwxrob/bonzai
synced 2024-11-04 18:00:18 +00:00
Improve organization of functional packages
This commit is contained in:
parent
b5ac9f2ca6
commit
8e2e560e80
14
fn/fn.go
14
fn/fn.go
@ -2,11 +2,25 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/*
|
||||
|
||||
Package fn implements the traditional map/filter/reduce/each functions
|
||||
and an array type (A) for those who prefer a more object-oriented
|
||||
approach. Unlike other implementations, the array (slice) is always
|
||||
first preventing the first-class in-line anonymous function from
|
||||
obfuscating the parameter list of the functional function.
|
||||
|
||||
"Why have a functional package in a commander?"
|
||||
|
||||
These functions are included because commander frameworks are about
|
||||
creating less friction during command applications development. Since
|
||||
many such commands are creating from ported shell scripts where the UNIX
|
||||
philosophy and filters reign supreme in makes sense to enable similar
|
||||
functional approaches (filters, pipelines) to approximate the simplicity
|
||||
of and speed shell scripting development (at the very slight cost of
|
||||
runtime performance). In short, this package speeds and simplifies
|
||||
command application development by making it more compatible with shell
|
||||
scripting in general.
|
||||
|
||||
*/
|
||||
package fn
|
||||
|
||||
|
21
mapf/mapf.go
21
mapf/mapf.go
@ -3,8 +3,27 @@ Package mapf contains nothing but map functions suitable for use with
|
||||
the fn.Map generic function or the equivalent fn.A method. See the maps
|
||||
package for functions that accept entire generic slices to be
|
||||
transformed with mapf (or other) functions.
|
||||
|
||||
Note that any of the functions in this package can easily be added to a template.FuncMap for use in custom text|html/templates.
|
||||
*/
|
||||
package mapf
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// MarkDirs will add a slash (/) to the end of the name if the
|
||||
// fs.DirEntry is a directory and return it as a string.
|
||||
func MarkDirs(f fs.DirEntry) string {
|
||||
if f.IsDir() {
|
||||
return f.Name() + "/"
|
||||
}
|
||||
return f.Name()
|
||||
}
|
||||
|
||||
// HashComment adds a "# " prefix.
|
||||
func HashComment(i string) string { return "# " + i }
|
||||
func HashComment(line string) string { return "# " + line }
|
||||
|
||||
// EscSpace puts backslash in front of any space.
|
||||
func EscSpace(s string) string { return strings.ReplaceAll(s, ` `, `\ `) }
|
||||
|
@ -1,13 +1,32 @@
|
||||
package mapf_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/rwxrob/bonzai/each"
|
||||
"github.com/rwxrob/bonzai/fn"
|
||||
"github.com/rwxrob/bonzai/mapf"
|
||||
)
|
||||
|
||||
func ExampleMarkDirs() {
|
||||
entries, _ := os.ReadDir("testdata/markdirs")
|
||||
each.Println(fn.Map(entries, mapf.MarkDirs))
|
||||
//Output:
|
||||
// dir1/
|
||||
// file1
|
||||
}
|
||||
|
||||
func ExampleHashComment() {
|
||||
fn.A[string]{"foo", "bar"}.M(mapf.HashComment).P()
|
||||
each.Println(fn.Map([]string{"foo", "bar"}, mapf.HashComment))
|
||||
// Output:
|
||||
// # foo
|
||||
// # bar
|
||||
}
|
||||
|
||||
func ExampleEscSpace() {
|
||||
s := []string{"one here", "and another one"}
|
||||
each.Println(fn.Map(s, mapf.EscSpace))
|
||||
// Output:
|
||||
// one\ here
|
||||
// and\ another\ \ \ \ one
|
||||
}
|
||||
|
0
mapf/testdata/markdirs/file1
vendored
Normal file
0
mapf/testdata/markdirs/file1
vendored
Normal file
27
maps/complex.go
Normal file
27
maps/complex.go
Normal file
@ -0,0 +1,27 @@
|
||||
package maps
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/rwxrob/bonzai/fn"
|
||||
)
|
||||
|
||||
// Note to maintainers: This file contains maps that require additional
|
||||
// arguments and are therefore not able to call simple map functions
|
||||
// from the mapf package. Please keep simple mapf-able maps in
|
||||
// simple.go instead.
|
||||
|
||||
// Prefix adds a prefix to the string.
|
||||
func Prefix(in []string, pre string) []string {
|
||||
return fn.Map(in, func(i string) string { return pre + i })
|
||||
}
|
||||
|
||||
// Keys returns the keys in lexicographically sorted order.
|
||||
func Keys[T any](m map[string]T) []string {
|
||||
keys := []string{}
|
||||
for k, _ := range m {
|
||||
keys = append(keys, k)
|
||||
sort.Strings(keys)
|
||||
}
|
||||
return keys
|
||||
}
|
@ -2,12 +2,16 @@ package maps_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/rwxrob/bonzai/loop"
|
||||
"github.com/rwxrob/bonzai/maps"
|
||||
)
|
||||
|
||||
func ExamplePrefix() {
|
||||
fmt.Println(maps.Prefix([]string{"foo", "bar"}, "my"))
|
||||
// Output:
|
||||
// [myfoo mybar]
|
||||
}
|
||||
|
||||
func ExampleKeys() {
|
||||
m1 := map[string]int{"two": 2, "three": 3, "one": 1}
|
||||
m2 := map[string]string{"two": "two", "three": "three", "one": "one"}
|
||||
@ -17,36 +21,3 @@ func ExampleKeys() {
|
||||
// [one three two]
|
||||
// [one three two]
|
||||
}
|
||||
|
||||
func ExamplePrefix() {
|
||||
fmt.Println(maps.Prefix([]string{"foo", "bar"}, "my"))
|
||||
// Output:
|
||||
// [myfoo mybar]
|
||||
}
|
||||
|
||||
func ExampleCleanPaths() {
|
||||
paths := []string{
|
||||
``,
|
||||
`.`,
|
||||
`./`,
|
||||
`./thing`,
|
||||
`/sub/../../thing`,
|
||||
}
|
||||
|
||||
loop.Println(maps.CleanPaths(paths))
|
||||
|
||||
// Output:
|
||||
// .
|
||||
// .
|
||||
// .
|
||||
// thing
|
||||
// /thing
|
||||
}
|
||||
|
||||
func ExampleMarkDirs() {
|
||||
entries, _ := os.ReadDir("testdata/markdirs")
|
||||
loop.Println(maps.MarkDirs(entries))
|
||||
//Output:
|
||||
// dir1/
|
||||
// file1
|
||||
}
|
41
maps/maps.go
41
maps/maps.go
@ -1,41 +0,0 @@
|
||||
package maps
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
|
||||
"github.com/rwxrob/bonzai/fn"
|
||||
)
|
||||
|
||||
// Prefix returns a new slice with prefix added to each string.
|
||||
func Prefix(in []string, pre string) []string {
|
||||
return fn.Map(in, func(i string) string { return pre + i })
|
||||
}
|
||||
|
||||
// Keys returns the keys in lexicographically sorted order.
|
||||
func Keys[T any](m map[string]T) []string {
|
||||
keys := []string{}
|
||||
for k, _ := range m {
|
||||
keys = append(keys, k)
|
||||
sort.Strings(keys)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// CleanPaths runs filepath.Clean on each item in the slice and returns.
|
||||
func CleanPaths(paths []string) []string {
|
||||
return fn.Map(paths, func(i string) string { return filepath.Clean(i) })
|
||||
}
|
||||
|
||||
// MarkDirs will add an os.PathSeparator to the end of the name if the
|
||||
// fs.DirEntry is a directory.
|
||||
func MarkDirs(entries []fs.DirEntry) []string {
|
||||
return fn.Map(entries, func(f fs.DirEntry) string {
|
||||
if f.IsDir() {
|
||||
return f.Name() + string(os.PathSeparator)
|
||||
}
|
||||
return f.Name()
|
||||
})
|
||||
}
|
26
maps/simple.go
Normal file
26
maps/simple.go
Normal file
@ -0,0 +1,26 @@
|
||||
package maps
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/rwxrob/bonzai/fn"
|
||||
"github.com/rwxrob/bonzai/mapf"
|
||||
)
|
||||
|
||||
// Note to maintainers: This file contains simple maps that are
|
||||
// implemented in the mapf package. Please keep complex maps in
|
||||
// complex.go instead.
|
||||
|
||||
// MarkDirs will add an os.PathSeparator to the end of the name if the
|
||||
// fs.DirEntry is a directory.
|
||||
func MarkDirs(s []fs.DirEntry) []string { return fn.Map(s, mapf.MarkDirs) }
|
||||
|
||||
// Base extracts the filepath.Base of each path.
|
||||
func Base(s []string) []string { return fn.Map(s, filepath.Base) }
|
||||
|
||||
// HashComment add the "# " prefix to each.
|
||||
func HashComment(s []string) []string { return fn.Map(s, mapf.HashComment) }
|
||||
|
||||
// EscSpace replaces all spaces with backslashed spaces.
|
||||
func EscSpace(s []string) []string { return fn.Map(s, mapf.EscSpace) }
|
43
maps/simple_test.go
Normal file
43
maps/simple_test.go
Normal file
@ -0,0 +1,43 @@
|
||||
package maps_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/rwxrob/bonzai/each"
|
||||
"github.com/rwxrob/bonzai/maps"
|
||||
)
|
||||
|
||||
func ExampleMarkDirs() {
|
||||
entries, _ := os.ReadDir("testdata/markdirs")
|
||||
each.Println(maps.MarkDirs(entries))
|
||||
//Output:
|
||||
// dir1/
|
||||
// file1
|
||||
}
|
||||
|
||||
func ExampleBase() {
|
||||
paths := []string{
|
||||
`some/thing /here`,
|
||||
`other/thing`,
|
||||
`foo`,
|
||||
}
|
||||
each.Println(maps.Base(paths))
|
||||
//Output:
|
||||
// here
|
||||
// thing
|
||||
// foo
|
||||
}
|
||||
|
||||
func ExampleHashComment() {
|
||||
each.Println(maps.HashComment([]string{"foo", "bar"}))
|
||||
// Output:
|
||||
// # foo
|
||||
// # bar
|
||||
}
|
||||
|
||||
func ExampleEscSpace() {
|
||||
each.Println(maps.EscSpace([]string{"some thing", "one other thing"}))
|
||||
// Output:
|
||||
// some\ thing
|
||||
// one\ other\ \ \ thing
|
||||
}
|
@ -2,27 +2,33 @@ package util
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/rwxrob/bonzai/filt"
|
||||
"github.com/rwxrob/bonzai/maps"
|
||||
)
|
||||
|
||||
// Files returns a slice of strings matching the names of the files
|
||||
// within the given directory adding a slash to the end of any
|
||||
// directories.
|
||||
// directories and escaping any spaces by adding backslash. Note that
|
||||
// this (and all functions of the bonzai package) assume forward slash
|
||||
// path separators because no path argument should ever be passed to any
|
||||
// bonzai command or high-level library that does not use forward slash
|
||||
// paths. Commands should always use the comp.Files completer instead of
|
||||
// host shell completion.
|
||||
func Files(dir string) []string {
|
||||
dir = filepath.Clean(dir)
|
||||
if dir == "" {
|
||||
dir = "."
|
||||
}
|
||||
files := []string{}
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
return files
|
||||
}
|
||||
return maps.Prefix(maps.MarkDirs(entries), dir+string(os.PathSeparator))
|
||||
}
|
||||
|
||||
//FilesWith takes the path of a directory and returns the name of the
|
||||
//files with the matching prefix.
|
||||
func FilesWith(dir, pre string) []string {
|
||||
return filt.HasPrefix(Files(dir), filepath.Join(dir, pre))
|
||||
names := maps.MarkDirs(entries)
|
||||
if dir == "." {
|
||||
return names
|
||||
}
|
||||
if dir[len(dir)-1] != '/' {
|
||||
dir += "/"
|
||||
}
|
||||
return maps.EscSpace(maps.Prefix(names, dir))
|
||||
}
|
||||
|
@ -21,26 +21,25 @@ func ExampleFiles() {
|
||||
// testdata/files/some
|
||||
}
|
||||
|
||||
func ExampleFiles_spaces() {
|
||||
loop.Println(util.Files("testdata/files/dir1"))
|
||||
// Output:
|
||||
// testdata/files/dir1/some\ thing
|
||||
}
|
||||
|
||||
func ExampleFiles_empty() {
|
||||
os.Chdir("testdata/files")
|
||||
defer os.Chdir("../..")
|
||||
loop.Println(util.Files(""))
|
||||
// Output:
|
||||
// ./bar
|
||||
// ./blah
|
||||
// ./dir1/
|
||||
// ./dir2/
|
||||
// ./dir3/
|
||||
// ./foo
|
||||
// ./other
|
||||
// ./some
|
||||
}
|
||||
|
||||
func ExampleFilesWith() {
|
||||
loop.Println(util.FilesWith("testdata/files", "b"))
|
||||
// Output:
|
||||
// testdata/files/bar
|
||||
// testdata/files/blah
|
||||
// bar
|
||||
// blah
|
||||
// dir1/
|
||||
// dir2/
|
||||
// dir3/
|
||||
// foo
|
||||
// other
|
||||
// some
|
||||
}
|
||||
|
||||
func ExampleFiles_not_Directory() {
|
||||
|
0
util/testdata/files/dir1/some thing
vendored
Normal file
0
util/testdata/files/dir1/some thing
vendored
Normal file
Loading…
Reference in New Issue
Block a user