mirror of
https://github.com/vbatts/go-mtree.git
synced 2024-11-24 00:55:41 +00:00
feat: implement mutate command
This commit is contained in:
parent
12e242c268
commit
739d74de3c
4 changed files with 166 additions and 1 deletions
144
cmd/gomtree/cmd/mutate.go
Normal file
144
cmd/gomtree/cmd/mutate.go
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"os"
|
||||||
|
"slices"
|
||||||
|
"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)
|
||||||
|
}
|
||||||
|
|
||||||
|
dropDotDot := 0
|
||||||
|
droppedParents := []*mtree.Entry{}
|
||||||
|
|
||||||
|
entries := []mtree.Entry{}
|
||||||
|
|
||||||
|
skip:
|
||||||
|
for _, entry := range spec.Entries {
|
||||||
|
|
||||||
|
if !keepComments && entry.Type == mtree.CommentType {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !keepBlank && entry.Type == mtree.BlankType {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if entry.Parent != nil && slices.Contains(droppedParents, &entry) {
|
||||||
|
entry.Parent = nil
|
||||||
|
entry.Type = mtree.FullType
|
||||||
|
entry.Raw = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if entry.Type == mtree.FullType || entry.Type == mtree.RelativeType {
|
||||||
|
fp, err := entry.Path()
|
||||||
|
// fmt.Println("fp", fp, entry.Name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pathSegments := strings.Split(fp, "/")
|
||||||
|
|
||||||
|
for _, prefix := range stripPrexies {
|
||||||
|
|
||||||
|
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 == "" {
|
||||||
|
continue skip
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if entry.IsDir() {
|
||||||
|
dropDotDot++
|
||||||
|
droppedParents = append(droppedParents, &entry)
|
||||||
|
}
|
||||||
|
if fp == strip {
|
||||||
|
continue skip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if dropDotDot > 0 && entry.Type == mtree.DotDotType {
|
||||||
|
dropDotDot--
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spec.WriteTo(writer)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -42,6 +42,7 @@ to support xattrs and interacting with tar archives.`
|
||||||
}
|
}
|
||||||
app.Commands = []*cli.Command{
|
app.Commands = []*cli.Command{
|
||||||
cmd.NewValidateCommand(),
|
cmd.NewValidateCommand(),
|
||||||
|
cmd.NewMutateCommand(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unfortunately urfave/cli is not at good at using DefaultCommand
|
// Unfortunately urfave/cli is not at good at using DefaultCommand
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -1,6 +1,6 @@
|
||||||
module github.com/vbatts/go-mtree
|
module github.com/vbatts/go-mtree
|
||||||
|
|
||||||
go 1.17
|
go 1.18
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
|
|
20
testdata/flat.relative.mtree
vendored
Normal file
20
testdata/flat.relative.mtree
vendored
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# ./lib
|
||||||
|
lib type=dir mode=0644
|
||||||
|
foo mode=0644 size=12288 time=1457644483.833957552 type=file
|
||||||
|
|
||||||
|
# ./lib/dir
|
||||||
|
lib type=dir mode=0644
|
||||||
|
|
||||||
|
dir type=dir mode=0644
|
||||||
|
|
||||||
|
# ./lib
|
||||||
|
..
|
||||||
|
|
||||||
|
# .
|
||||||
|
..
|
||||||
|
|
||||||
|
# ./lib/dir/sub
|
||||||
|
lib/dir/sub type=dir
|
||||||
|
lib/dir/sub/file.txt type=file
|
||||||
|
|
||||||
|
lib/dir/PKG.info type=file mode=0644
|
Loading…
Reference in a new issue