nf/nft/nfctl/tp/tp.go
2024-07-12 16:00:15 +08:00

317 lines
6.8 KiB
Go

package tp
import (
"bufio"
"bytes"
"fmt"
"github.com/loveuer/nf/nft/log"
"io/fs"
"os"
"path"
"path/filepath"
"regexp"
"strings"
)
type Cmd interface {
String() string
Execute() error
}
var (
_ Cmd = (*Generate)(nil)
_ Cmd = (*ReplaceContent)(nil)
_ Cmd = (*ReplaceName)(nil)
)
type Generate struct {
pwd string
filename string
content []string
}
func (t *Generate) String() string {
return fmt.Sprintf("!generate\n%s\n%s\n", t.filename, strings.Join(t.content, "\n"))
}
func (t *Generate) Execute() error {
var (
err error
location = t.filename
input *os.File
)
log.Debug("[Generate] generate[%s]", t.filename)
if !path.IsAbs(t.filename) {
location = path.Join(t.pwd, t.filename)
}
if err = os.MkdirAll(path.Dir(location), 0644); err != nil {
return err
}
if !strings.HasSuffix(location, "/") {
if input, err = os.OpenFile(location, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0744); err != nil {
return err
}
if len(t.content) > 0 {
content := strings.Join(t.content, "\n")
_, err = input.WriteString(content)
return err
}
}
return nil
}
func newGenerate(pwd string, lines []string) (*Generate, error) {
if len(lines) == 0 {
return nil, fmt.Errorf("generate cmd require file/folder name")
}
return &Generate{
pwd: pwd,
filename: lines[0],
content: lines[1:],
}, nil
}
type replaceNameMatchType int
const (
replaceNameMatchReg replaceNameMatchType = iota + 1
replaceNameMatchExact
replaceNameMatchPrefix
replaceNameMatchSuffix
)
func (rm replaceNameMatchType) Label() string {
switch rm {
case replaceNameMatchReg:
return "reg"
case replaceNameMatchExact:
return "exact"
case replaceNameMatchPrefix:
return "prefix"
case replaceNameMatchSuffix:
return "suffix"
}
log.Panic("unknown replace match type: %v", rm)
return ""
}
type ReplaceContent struct {
pwd string
name string
content string
targetName string
matchType replaceNameMatchType
fromContent string
targetEmpty bool
targetContent string
}
func (t *ReplaceContent) String() string {
return fmt.Sprintf("!replace content\n%s\n%s\n", t.name, t.content)
}
func (t *ReplaceContent) Execute() error {
var (
fn filepath.WalkFunc
handler = func(location string) error {
bs, err := os.ReadFile(location)
if err != nil {
return err
}
log.Debug("[ReplaceContent] handle[%s] replace [%s] => [%s]", location, t.fromContent, t.targetContent)
newbs, err := t.executeFile(bs)
if err != nil {
return err
}
return os.WriteFile(location, newbs, 0644)
}
)
switch t.matchType {
case replaceNameMatchExact:
fn = func(location string, info fs.FileInfo, err error) error {
if location == path.Join(t.pwd, t.targetName) {
log.Debug("[ReplaceContent] exact match: %s", location)
return handler(location)
}
return nil
}
case replaceNameMatchPrefix:
fn = func(location string, info fs.FileInfo, err error) error {
if strings.HasPrefix(path.Base(location), t.targetName) {
log.Debug("[ReplaceContent] prefix match: %s", location)
return handler(location)
}
return nil
}
case replaceNameMatchSuffix:
fn = func(location string, info fs.FileInfo, err error) error {
if strings.HasSuffix(location, t.targetName) {
log.Debug("[ReplaceContent] suffix match: %s", location)
return handler(location)
}
return nil
}
case replaceNameMatchReg:
fn = func(location string, info fs.FileInfo, err error) error {
if match, err := regexp.MatchString(t.targetName, location); err == nil && match {
log.Debug("[ReplaceContent] reg match: %s", location)
return handler(location)
}
return nil
}
}
return filepath.Walk(t.pwd, fn)
}
func (t *ReplaceContent) executeFile(raw []byte) ([]byte, error) {
scanner := bufio.NewScanner(bytes.NewReader(raw))
scanner.Buffer(make([]byte, 1024), 1024*1024)
lines := make([]string, 0)
for scanner.Scan() {
line := scanner.Text()
lines = append(
lines,
strings.ReplaceAll(line, t.fromContent, t.targetContent),
)
}
return []byte(strings.Join(lines, "\n")), nil
}
func newReplaceContent(pwd string, lines []string) (*ReplaceContent, error) {
if len(lines) != 2 {
return nil, fmt.Errorf("invalid replace_content cmd: required 2 lines params")
}
var (
name = lines[0]
content = lines[1]
matchType replaceNameMatchType
)
names := strings.SplitN(name, " ", 2)
if len(names) != 2 {
return nil, fmt.Errorf("invalid replace_content cmd: name line, required: [reg/exact/prefix/shuffix] {filename}")
}
switch names[0] {
case "exact":
matchType = replaceNameMatchExact
case "reg":
matchType = replaceNameMatchReg
case "prefix":
matchType = replaceNameMatchPrefix
case "suffix":
matchType = replaceNameMatchSuffix
default:
return nil, fmt.Errorf("invalid replace_content name match type, example: [reg *.go] [exact go.mod]")
}
var (
targetName string = names[1]
targetEmpty = false
targetContent string
)
contents := strings.SplitN(content, "=>", 2)
fromContent := strings.TrimSpace(contents[0])
if len(contents) == 1 {
targetEmpty = true
} else {
if targetContent = strings.TrimSpace(contents[1]); targetContent == "" || targetContent == `""` || targetContent == `''` {
targetEmpty = true
}
}
return &ReplaceContent{
pwd: pwd,
name: name,
content: content,
matchType: matchType,
targetName: targetName,
fromContent: fromContent,
targetEmpty: targetEmpty,
targetContent: targetContent,
}, nil
}
type ReplaceName struct {
pwd string
line string
targetEmpty bool
fromContent string
targetContent string
}
func (t *ReplaceName) String() string {
return fmt.Sprintf("!replace name\n%s\n", t.line)
}
func (t *ReplaceName) Execute() error {
fullpath := path.Join(t.pwd, t.fromContent)
if t.targetEmpty {
return os.RemoveAll(fullpath)
}
ftpath := path.Join(t.pwd, t.targetContent)
return os.Rename(fullpath, ftpath)
}
func newReplaceName(pwd string, lines []string) (*ReplaceName, error) {
if len(lines) != 1 {
return nil, fmt.Errorf("replace_name need one line param, for example: mian.go => main.go")
}
var (
content = lines[0]
targetEmpty = false
fromContent string
targetContent string
)
contents := strings.SplitN(content, "=>", 2)
fromContent = strings.TrimSpace(contents[0])
if len(contents) == 1 {
targetEmpty = true
} else {
if targetContent = strings.TrimSpace(contents[1]); targetContent == "" || targetContent == `""` || targetContent == `''` {
targetEmpty = true
}
}
if !targetEmpty {
if (strings.HasPrefix(targetContent, `"`) && strings.HasSuffix(targetContent, `"`)) || (strings.HasPrefix(targetContent, `'`) && strings.HasSuffix(targetContent, `'`)) {
targetContent = targetContent[1 : len(targetContent)-1]
}
}
return &ReplaceName{
pwd: pwd,
line: content,
targetEmpty: targetEmpty,
fromContent: fromContent,
targetContent: targetContent,
}, nil
}