feat: add nfctl(ctl to start nf project)
This commit is contained in:
@ -1,40 +1,48 @@
|
||||
package tp
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/loveuer/nf/nft/log"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type TpCmd interface {
|
||||
type Cmd interface {
|
||||
String() string
|
||||
Execute() error
|
||||
}
|
||||
|
||||
var (
|
||||
_ TpCmd = (*TpGenerate)(nil)
|
||||
_ TpCmd = (*TpReplace)(nil)
|
||||
_ Cmd = (*Generate)(nil)
|
||||
_ Cmd = (*ReplaceContent)(nil)
|
||||
_ Cmd = (*ReplaceName)(nil)
|
||||
)
|
||||
|
||||
type TpGenerate struct {
|
||||
type Generate struct {
|
||||
pwd string
|
||||
filename string
|
||||
content []string
|
||||
}
|
||||
|
||||
func (t *TpGenerate) String() string {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
func (t *Generate) String() string {
|
||||
return fmt.Sprintf("!generate\n%s\n%s\n", t.filename, strings.Join(t.content, "\n"))
|
||||
}
|
||||
|
||||
func (t *TpGenerate) Execute() error {
|
||||
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)
|
||||
}
|
||||
@ -44,7 +52,7 @@ func (t *TpGenerate) Execute() error {
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(location, "/") {
|
||||
if input, err = os.OpenFile(location, os.O_CREATE|os.O_APPEND, 0744); err != nil {
|
||||
if input, err = os.OpenFile(location, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0744); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -58,35 +66,251 @@ func (t *TpGenerate) Execute() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func newGenerate(pwd string, lines []string) (*TpGenerate, error) {
|
||||
func newGenerate(pwd string, lines []string) (*Generate, error) {
|
||||
if len(lines) == 0 {
|
||||
return nil, fmt.Errorf("generate cmd require file/folder name")
|
||||
}
|
||||
|
||||
return &TpGenerate{
|
||||
return &Generate{
|
||||
pwd: pwd,
|
||||
filename: lines[0],
|
||||
content: lines[1:],
|
||||
}, nil
|
||||
}
|
||||
|
||||
type TpReplace struct {
|
||||
pwd string
|
||||
}
|
||||
type replaceNameMatchType int
|
||||
|
||||
func (t *TpReplace) String() string {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
const (
|
||||
replaceNameMatchReg replaceNameMatchType = iota + 1
|
||||
replaceNameMatchExact
|
||||
replaceNameMatchPrefix
|
||||
replaceNameMatchSuffix
|
||||
)
|
||||
|
||||
func (t *TpReplace) Execute() error {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func newReplace(pwd string, lines []string) (*TpReplace, error) {
|
||||
if len(lines) < 2 {
|
||||
return nil, fmt.Errorf("invalid replace cmd")
|
||||
func (rm replaceNameMatchType) Label() string {
|
||||
switch rm {
|
||||
case replaceNameMatchReg:
|
||||
return "reg"
|
||||
case replaceNameMatchExact:
|
||||
return "exact"
|
||||
case replaceNameMatchPrefix:
|
||||
return "prefix"
|
||||
case replaceNameMatchSuffix:
|
||||
return "suffix"
|
||||
}
|
||||
return &TpReplace{pwd: pwd}, nil
|
||||
|
||||
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
|
||||
}
|
||||
|
Reference in New Issue
Block a user