mirror of https://github.com/vbatts/go-mtree.git
185 lines
3.8 KiB
Go
185 lines
3.8 KiB
Go
package cmd
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"math"
|
|
"os"
|
|
"strings"
|
|
|
|
cli "github.com/urfave/cli/v2"
|
|
"github.com/vbatts/go-mtree"
|
|
)
|
|
|
|
func NewMutateCommand() *cli.Command {
|
|
|
|
return &cli.Command{
|
|
Name: "mutate",
|
|
Usage: "mutate an mtree",
|
|
Description: `Mutate an mtree to have different shapes.
|
|
TODO: more info examples`,
|
|
Action: mutateAction,
|
|
ArgsUsage: "<path to mtree> [path to output]",
|
|
Flags: []cli.Flag{
|
|
&cli.StringSliceFlag{
|
|
Name: "strip-prefix",
|
|
},
|
|
&cli.BoolFlag{
|
|
Name: "keep-comments",
|
|
Value: false,
|
|
},
|
|
&cli.BoolFlag{
|
|
Name: "keep-blank",
|
|
Value: false,
|
|
},
|
|
&cli.StringFlag{
|
|
Name: "output",
|
|
TakesFile: true,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func mutateAction(c *cli.Context) error {
|
|
mtreePath := c.Args().Get(0)
|
|
outputPath := c.Args().Get(1)
|
|
stripPrexies := c.StringSlice("strip-prefix")
|
|
keepComments := c.Bool("keep-comments")
|
|
keepBlank := c.Bool("keep-blank")
|
|
|
|
if mtreePath == "" {
|
|
return fmt.Errorf("mtree path is required.")
|
|
} else if outputPath == "" {
|
|
outputPath = mtreePath
|
|
}
|
|
|
|
file, err := os.Open(mtreePath)
|
|
if err != nil {
|
|
return fmt.Errorf("opening %s: %w", mtreePath, err)
|
|
}
|
|
|
|
spec, err := mtree.ParseSpec(file)
|
|
if err != nil {
|
|
return fmt.Errorf("parsing mtree %s: %w", mtreePath, err)
|
|
}
|
|
|
|
stripPrefixVisitor := stripPrefixVisitor{
|
|
prefixes: stripPrexies,
|
|
}
|
|
tidyVisitor := tidyVisitor{
|
|
keepComments: keepComments,
|
|
keepBlank: keepBlank,
|
|
}
|
|
visitors := []Visitor{
|
|
&stripPrefixVisitor,
|
|
&tidyVisitor,
|
|
}
|
|
|
|
dropped := map[int]bool{}
|
|
entries := []mtree.Entry{}
|
|
|
|
skip:
|
|
for _, entry := range spec.Entries {
|
|
if entry.Parent != nil {
|
|
if _, ok := dropped[entry.Parent.Pos]; ok {
|
|
if entry.Type == mtree.DotDotType {
|
|
// directory for this .. has been dropped so shall this
|
|
continue
|
|
}
|
|
entry.Parent = entry.Parent.Parent
|
|
// TODO: i am not sure if this is the correct behavior
|
|
entry.Raw = strings.TrimPrefix(entry.Raw, " ")
|
|
}
|
|
}
|
|
|
|
for _, visitor := range visitors {
|
|
drop, err := visitor.Visit(&entry)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if drop {
|
|
dropped[entry.Pos] = true
|
|
continue skip
|
|
}
|
|
}
|
|
|
|
entries = append(entries, entry)
|
|
}
|
|
|
|
spec.Entries = entries
|
|
|
|
var writer io.Writer = os.Stdout
|
|
if outputPath != "-" {
|
|
writer, err = os.Create(outputPath)
|
|
if err != nil {
|
|
return fmt.Errorf("creating output %s: %w", outputPath, err)
|
|
}
|
|
}
|
|
|
|
_, err = spec.WriteTo(writer)
|
|
if err != nil {
|
|
return fmt.Errorf("writing mtree %s: %w", outputPath, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type Visitor interface {
|
|
Visit(entry *mtree.Entry) (bool, error)
|
|
}
|
|
|
|
type tidyVisitor struct {
|
|
keepComments bool
|
|
keepBlank bool
|
|
}
|
|
|
|
func (m *tidyVisitor) Visit(entry *mtree.Entry) (bool, error) {
|
|
if !m.keepComments && entry.Type == mtree.CommentType {
|
|
return true, nil
|
|
} else if !m.keepBlank && entry.Type == mtree.BlankType {
|
|
return true, nil
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
type stripPrefixVisitor struct {
|
|
prefixes []string
|
|
}
|
|
|
|
func (m *stripPrefixVisitor) Visit(entry *mtree.Entry) (bool, error) {
|
|
if entry.Type != mtree.FullType && entry.Type != mtree.RelativeType {
|
|
return false, nil
|
|
}
|
|
|
|
fp, err := entry.Path()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
pathSegments := strings.Split(fp, "/")
|
|
|
|
for _, prefix := range m.prefixes {
|
|
|
|
prefixSegments := strings.Split(prefix, "/")
|
|
minLen := int(math.Min(float64(len(pathSegments)), float64(len(prefixSegments))))
|
|
matches := make([]string, minLen)
|
|
for i := 0; i < minLen; i++ {
|
|
if pathSegments[i] == prefixSegments[i] {
|
|
matches[i] = prefixSegments[i]
|
|
}
|
|
}
|
|
|
|
strip := strings.Join(matches, "/")
|
|
if entry.Type == mtree.FullType {
|
|
entry.Name = strings.TrimPrefix(entry.Name, strip)
|
|
entry.Name = strings.TrimPrefix(entry.Name, "/")
|
|
if entry.Name == "" {
|
|
return true, nil
|
|
}
|
|
} else if fp == strip {
|
|
return true, nil
|
|
}
|
|
}
|
|
return false, nil
|
|
}
|