feat: 重构
This commit is contained in:
262
actions.go
Normal file
262
actions.go
Normal file
@@ -0,0 +1,262 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
)
|
||||
|
||||
func executeAction(versionDir string, action Action, index int, backupDir string) tea.Cmd {
|
||||
total := index + 1
|
||||
prefix := fmt.Sprintf("[%d]", total)
|
||||
|
||||
switch action.Type {
|
||||
case "add":
|
||||
return executeAdd(versionDir, action, index, backupDir, prefix)
|
||||
case "delete":
|
||||
return func() tea.Msg {
|
||||
absPath := filepath.Join(versionDir, action.Path)
|
||||
if err := backupPath(versionDir, action.Path, backupDir); err != nil {
|
||||
return actionErrorMsg{index: index, err: fmt.Errorf("backup failed: %w", err)}
|
||||
}
|
||||
if err := os.RemoveAll(absPath); err != nil {
|
||||
return actionErrorMsg{index: index, err: fmt.Errorf("delete failed: %w", err)}
|
||||
}
|
||||
return actionCompleteMsg{index: index}
|
||||
}
|
||||
case "copy":
|
||||
return func() tea.Msg {
|
||||
dst := filepath.Join(versionDir, action.NewPath)
|
||||
if _, err := os.Stat(dst); err == nil {
|
||||
if err := backupPath(versionDir, action.NewPath, backupDir); err != nil {
|
||||
return actionErrorMsg{index: index, err: fmt.Errorf("backup failed: %w", err)}
|
||||
}
|
||||
}
|
||||
src := filepath.Join(versionDir, action.Path)
|
||||
if err := copyPath(src, dst); err != nil {
|
||||
return actionErrorMsg{index: index, err: fmt.Errorf("copy failed: %w", err)}
|
||||
}
|
||||
return actionCompleteMsg{index: index}
|
||||
}
|
||||
case "move":
|
||||
return func() tea.Msg {
|
||||
src := filepath.Join(versionDir, action.Path)
|
||||
dst := filepath.Join(versionDir, action.NewPath)
|
||||
if _, err := os.Stat(dst); err == nil {
|
||||
if err := backupPath(versionDir, action.NewPath, backupDir); err != nil {
|
||||
return actionErrorMsg{index: index, err: fmt.Errorf("backup failed: %w", err)}
|
||||
}
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
|
||||
return actionErrorMsg{index: index, err: fmt.Errorf("mkdir failed: %w", err)}
|
||||
}
|
||||
if err := os.Rename(src, dst); err != nil {
|
||||
if err := copyPath(src, dst); err != nil {
|
||||
return actionErrorMsg{index: index, err: fmt.Errorf("move failed: %w", err)}
|
||||
}
|
||||
os.RemoveAll(src)
|
||||
}
|
||||
return actionCompleteMsg{index: index}
|
||||
}
|
||||
default:
|
||||
return func() tea.Msg {
|
||||
return actionErrorMsg{index: index, err: fmt.Errorf("unknown action type: %s", action.Type)}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func executeAdd(versionDir string, action Action, index int, backupDir, prefix string) tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
absPath := filepath.Join(versionDir, action.Path)
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(absPath), 0o755); err != nil {
|
||||
return actionErrorMsg{index: index, err: fmt.Errorf("mkdir failed: %w", err)}
|
||||
}
|
||||
|
||||
if _, err := os.Stat(absPath); err == nil {
|
||||
if err := backupPath(versionDir, action.Path, backupDir); err != nil {
|
||||
return actionErrorMsg{index: index, err: fmt.Errorf("backup failed: %w", err)}
|
||||
}
|
||||
}
|
||||
|
||||
err := downloadFile(action.URL, absPath)
|
||||
if err != nil {
|
||||
if len(action.Mirrors) > 0 {
|
||||
return mirrorChoiceMsg{index: index, mirrors: action.Mirrors, action: action}
|
||||
}
|
||||
return actionErrorMsg{index: index, err: fmt.Errorf("download failed: %w", err)}
|
||||
}
|
||||
|
||||
if action.Unzip {
|
||||
destDir := filepath.Dir(absPath)
|
||||
if err := unzipFile(absPath, destDir); err != nil {
|
||||
return actionErrorMsg{index: index, err: fmt.Errorf("unzip failed: %w", err)}
|
||||
}
|
||||
os.Remove(absPath)
|
||||
}
|
||||
|
||||
return actionCompleteMsg{index: index}
|
||||
}
|
||||
}
|
||||
|
||||
func waitForProgress(ch chan float64, index int) tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
p, ok := <-ch
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return actionProgressMsg{percent: p}
|
||||
}
|
||||
}
|
||||
|
||||
func downloadFile(url, destPath string) error {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("HTTP %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
tmpPath := destPath + ".tmp"
|
||||
f, err := os.Create(tmpPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = io.Copy(f, resp.Body)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
os.Remove(tmpPath)
|
||||
return err
|
||||
}
|
||||
|
||||
return os.Rename(tmpPath, destPath)
|
||||
}
|
||||
|
||||
func unzipFile(zipPath, destDir string) error {
|
||||
r, err := zip.OpenReader(zipPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
for _, f := range r.File {
|
||||
target := filepath.Join(destDir, f.Name)
|
||||
|
||||
if !strings.HasPrefix(filepath.Clean(target), filepath.Clean(destDir)+string(os.PathSeparator)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if f.FileInfo().IsDir() {
|
||||
os.MkdirAll(target, 0o755)
|
||||
continue
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rc, err := f.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
outFile, err := os.Create(target)
|
||||
if err != nil {
|
||||
rc.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = io.Copy(outFile, rc)
|
||||
outFile.Close()
|
||||
rc.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func backupPath(versionDir, relativePath, backupDir string) error {
|
||||
src := filepath.Join(versionDir, relativePath)
|
||||
if _, err := os.Stat(src); os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
dst := filepath.Join(versionDir, backupDir, relativePath)
|
||||
if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
info, err := os.Stat(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
return copyDir(src, dst)
|
||||
}
|
||||
return copyFile(src, dst)
|
||||
}
|
||||
|
||||
func copyPath(src, dst string) error {
|
||||
info, err := os.Stat(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
return copyDir(src, dst)
|
||||
}
|
||||
return copyFile(src, dst)
|
||||
}
|
||||
|
||||
func copyFile(src, dst string) error {
|
||||
in, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer in.Close()
|
||||
|
||||
out, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
_, err = io.Copy(out, in)
|
||||
return err
|
||||
}
|
||||
|
||||
func copyDir(src, dst string) error {
|
||||
return filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rel, err := filepath.Rel(src, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
target := filepath.Join(dst, rel)
|
||||
|
||||
if info.IsDir() {
|
||||
return os.MkdirAll(target, 0o755)
|
||||
}
|
||||
return copyFile(path, target)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user