sort : port over full code, fix flag value iteration skipping in parseArgs
This commit is contained in:
parent
d404b3d096
commit
ba46937c3c
2 changed files with 110 additions and 2 deletions
29
pipe/pipe.go
Normal file
29
pipe/pipe.go
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
package pipe
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const Newline string = "\n"
|
||||
|
||||
func isStdinLoaded() bool {
|
||||
stat, _ := os.Stdin.Stat()
|
||||
return (stat.Mode() & os.ModeCharDevice) == 0
|
||||
}
|
||||
|
||||
func GetStdin() ([]string, error) {
|
||||
if !isStdinLoaded() {
|
||||
return nil, errors.New("stdin not loaded")
|
||||
}
|
||||
data, err := io.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// TODO: there's got to be a better way:
|
||||
s, _ := strings.CutSuffix(string(data), Newline)
|
||||
return strings.Split(s, Newline), nil
|
||||
}
|
||||
83
sort/sort.go
83
sort/sort.go
|
|
@ -1,14 +1,20 @@
|
|||
package sort
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/brtholomy/um/go/flags"
|
||||
"github.com/brtholomy/um/go/pipe"
|
||||
)
|
||||
|
||||
type options struct {
|
||||
Source flags.Flag[string]
|
||||
Key flags.Flag[string]
|
||||
Write flags.Flag[bool]
|
||||
Help flags.Flag[bool]
|
||||
}
|
||||
|
||||
|
|
@ -16,20 +22,27 @@ func initOpts() options {
|
|||
return options{
|
||||
flags.Flag[string]{"--source", "-s", "", "path to source list. reads from stdin if omitted."},
|
||||
flags.Flag[string]{"--key", "-k", "", "path to sort key"},
|
||||
flags.Flag[bool]{"--write", "-w", false, "write sorted list back to --key file"},
|
||||
flags.Flag[bool]{"--help", "-h", false, "show help"},
|
||||
}
|
||||
}
|
||||
|
||||
func parseArgs(args []string) options {
|
||||
opts := initOpts()
|
||||
for i, arg := range args {
|
||||
|
||||
for i := 0; i < len(args); i++ {
|
||||
arg := args[i]
|
||||
switch {
|
||||
case arg == opts.Source.Long || arg == opts.Source.Short:
|
||||
flags.ValidValueOrExit(args, i)
|
||||
opts.Source.Val = args[i+1]
|
||||
i++
|
||||
case arg == opts.Key.Long || arg == opts.Key.Short:
|
||||
flags.ValidValueOrExit(args, i)
|
||||
opts.Key.Val = args[i+1]
|
||||
i++
|
||||
case arg == opts.Write.Long || arg == opts.Write.Short:
|
||||
opts.Write.Val = true
|
||||
case arg == opts.Help.Long || arg == opts.Help.Short:
|
||||
flags.Help("sort", opts)
|
||||
default:
|
||||
|
|
@ -37,10 +50,76 @@ func parseArgs(args []string) options {
|
|||
flags.Help("sort", opts)
|
||||
}
|
||||
}
|
||||
if !opts.Key.IsSet() {
|
||||
log.Printf("um sort: %s is required", opts.Key.Long)
|
||||
flags.Help("sort", opts)
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
// reads files from stdin if present, otherwise from the filename:
|
||||
func fileListSplitMaybeStdin(f string) []string {
|
||||
filelist, err := pipe.GetStdin()
|
||||
if err != nil {
|
||||
filelist = fileListSplit(f)
|
||||
}
|
||||
return filelist
|
||||
}
|
||||
|
||||
// opens the given filename and splits into lines:
|
||||
func fileListSplit(f string) []string {
|
||||
dat, err := os.ReadFile(f)
|
||||
if err != nil {
|
||||
// if this fails all is lost. just exit.
|
||||
log.Fatal(fmt.Errorf("um sort: error opening file: %s\n%w", f, err))
|
||||
}
|
||||
return strings.Split(string(dat), pipe.Newline)
|
||||
}
|
||||
|
||||
// record the order of the given filelist.
|
||||
func kMap(kslice []string) map[string]int {
|
||||
m := make(map[string]int, 0)
|
||||
for i, l := range kslice {
|
||||
m[l] = i
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// write out the provided source slice while respecting the order provided by kmap
|
||||
func sort(sslice []string, kmap map[string]int) string {
|
||||
oslice := make([]string, max(len(sslice), len(kmap))+1)
|
||||
for _, l := range sslice {
|
||||
// lines not represented in the key get appended to the end:
|
||||
if _, ok := kmap[l]; !ok {
|
||||
// we don't care that this might leave empty strings between:
|
||||
oslice = append(oslice, l)
|
||||
continue
|
||||
}
|
||||
// NOTE: raison d'etre of this whole thang:
|
||||
// if present in the key, respect the order:
|
||||
oslice[kmap[l]] = l
|
||||
}
|
||||
// delete empties and add a final newline
|
||||
oslice = slices.DeleteFunc(oslice, func(e string) bool { return e == "" })
|
||||
return strings.Join(oslice, pipe.Newline) + pipe.Newline
|
||||
}
|
||||
|
||||
func Write(file string, content string) {
|
||||
if err := os.WriteFile(file, []byte(content), 0664); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Sort(args []string) {
|
||||
opts := parseArgs(args)
|
||||
log.Println(opts)
|
||||
|
||||
sslice := fileListSplitMaybeStdin(opts.Source.Val)
|
||||
kslice := fileListSplit(opts.Key.Val)
|
||||
kmap := kMap(kslice)
|
||||
out := sort(sslice, kmap)
|
||||
if opts.Write.Val {
|
||||
Write(opts.Key.Val, out)
|
||||
} else {
|
||||
fmt.Print(out)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue