Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions cmd/list/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ var ListCmd = &cobra.Command{
if err != nil {
logrus.Errorln("Login Failed:", err)
}
handle(&p, args)
long, _ := cmd.Flags().GetBool("long")
human, _ := cmd.Flags().GetBool("human")
path, _ := cmd.Flags().GetString("path")
parentId, _ := cmd.Flags().GetString("parent-id")
handle(&p, args, long, human, path, parentId)
},
}

Expand All @@ -36,8 +40,11 @@ func init() {
ListCmd.Flags().StringVarP(&parentId, "parent-id", "P", "", "display the specified parent id")
}

func handle(p *pikpak.PikPak, args []string) {
func handle(p *pikpak.PikPak, args []string, long, human bool, path, parentId string) {
var err error
if len(args) > 0 {
path = args[0]
}
if parentId == "" {
parentId, err = p.GetPathFolderId(path)
if err != nil {
Expand Down
12 changes: 12 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,15 @@ func Execute() {
os.Exit(1)
}
}

// ExecuteShell: Interactive shell
func ExecuteShell(shellStarter func(*cobra.Command)) {
// Init Config
err := conf.InitConfig("config.yml")
if err != nil {
logrus.Errorln(err)
os.Exit(1)
}
// Exec shell
shellStarter(rootCmd)
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ require (
require (
github.com/VividCortex/ewma v1.2.0 // indirect
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
github.com/chzyer/readline v1.5.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
Expand Down
5 changes: 5 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1o
github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
Expand Down Expand Up @@ -51,6 +55,7 @@ github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/vbauerster/mpb/v8 v8.7.2 h1:SMJtxhNho1MV3OuFgS1DAzhANN1Ejc5Ct+0iSaIkB14=
github.com/vbauerster/mpb/v8 v8.7.2/go.mod h1:ZFnrjzspgDHoxYLGvxIruiNk73GNTPG4YHgVNpR10VY=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
Expand Down
206 changes: 206 additions & 0 deletions internal/shell/shell.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
package shell

import (
"fmt"
"os"
"strings"

"github.com/52funny/pikpakcli/conf"
"github.com/52funny/pikpakcli/internal/pikpak"
"github.com/chzyer/readline"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)

// Start starts the interactive shell
func Start(rootCmd *cobra.Command) {
fmt.Println("PikPak CLI Interactive Shell")
fmt.Println("Type 'help' for available commands, 'exit' to quit")
fmt.Println()

currentPath := "/"
globalPath := "/"
// globalPath := currentPath

// Create readline instance
// TODO: we can add path here: pikpak {path} >.
l, err := readline.New(fmt.Sprintf("pikpak %s > ", currentPath))
if err != nil {
fmt.Fprintf(os.Stderr, "Error initializing readline: %v\n", err)
return
}
defer l.Close()

for {
input, err := l.Readline()

// l.SetPrompt(fmt.Sprintf("pikpak %s > ", currentPath))

// Handle EOF (Ctrl+D)
if err == readline.ErrInterrupt {
continue
}

if err != nil {
// This is EOF
fmt.Println("\nBye~!")
break
}

input = strings.TrimSpace(input)

if input == "" {
continue
}

if input == "exit" || input == "quit" {
fmt.Println("Bye~!")
break
}

if input == "help" {
rootCmd.Help()
continue
}

// Parse the args and set them to rootCmd
args := parseShellArgs(input)

// Handle cd command
if len(args) > 0 && args[0] == "cd" {
var path string
if len(args) > 1 {
path = args[1]
}
// Go back to root if target path is empty, ~ or /
switch path {
case "", "~", "/":
currentPath = "/"
globalPath = currentPath
case "..":
// Go back to parent directory
if currentPath != "/" {
currentPath = currentPath[:strings.LastIndex(currentPath, "/")]
globalPath = currentPath + "/"
if currentPath == "" {
currentPath = "/"
globalPath = currentPath
}
}

// Update the prompt with the new path
l.SetPrompt(fmt.Sprintf("pikpak %s > ", globalPath))
default:
// Handle relative and absolute paths
var targetPath string
if strings.HasPrefix(path, "/") {
// Absolute path
targetPath = path
} else {
// Relative path
if currentPath == "/" {
targetPath = "/" + path
} else {
targetPath = currentPath + "/" + path
}
}
// Normalize path: remove duplicate slashes and trailing slash
targetPath = strings.ReplaceAll(targetPath, "//", "/")
if targetPath != "/" {
targetPath = strings.TrimSuffix(targetPath, "/")
}
// Check if the path exists and is a directory
p := pikpak.NewPikPak(conf.Config.Username, conf.Config.Password)
err := p.Login()
if err != nil {
fmt.Fprintf(os.Stderr, "Login failed: %v\n", err)
continue
}
_, err = p.GetPathFolderId(targetPath)
if err != nil {
fmt.Fprintf(os.Stderr, "cd: %s: No such directory\n", targetPath)
continue
}
currentPath = targetPath
globalPath = currentPath + "/"
// Update the prompt with the new path
l.SetPrompt(fmt.Sprintf("pikpak %s > ", globalPath))
}

continue
}

// For ls command, if no path specified, add current path
if len(args) == 1 && args[0] == "ls" {
args = append(args, "-p", currentPath)
}

rootCmd.SetArgs(args)

// Directly use pre-defined Execute function
// TODO: Need to be updated if cd command is supported.
if err := rootCmd.Execute(); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
}
rootCmd.SetArgs([]string{}) // Reset for next iteration

// Reset flags to default values to prevent state retention between commands
resetFlags(rootCmd)
}
}

// parseShellArgs parses shell-like arguments
func parseShellArgs(input string) []string {
var args []string
var current strings.Builder
inDoubleQuote := false
inSingleQuote := false

for i := 0; i < len(input); i++ {
ch := input[i]

switch ch {
case '"':
if inSingleQuote {
current.WriteByte(ch)
} else {
inDoubleQuote = !inDoubleQuote
}
case '\'':
if inDoubleQuote {
current.WriteByte(ch)
} else {
inSingleQuote = !inSingleQuote
}
case ' ', '\t':
if inDoubleQuote || inSingleQuote {
current.WriteByte(ch)
} else {
if current.Len() > 0 {
args = append(args, current.String())
current.Reset()
}
}
default:
current.WriteByte(ch)
}
}

if current.Len() > 0 {
args = append(args, current.String())
}
return args
}

// resetFlags recursively resets all flags in the command tree to their default values
func resetFlags(cmd *cobra.Command) {
cmd.Flags().VisitAll(func(f *pflag.Flag) {
f.Value.Set(f.DefValue)
})
cmd.PersistentFlags().VisitAll(func(f *pflag.Flag) {
f.Value.Set(f.DefValue)
})
for _, subCmd := range cmd.Commands() {
resetFlags(subCmd)
}
}
15 changes: 13 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
package main

import "github.com/52funny/pikpakcli/cmd"
import (
"os"

"github.com/52funny/pikpakcli/cmd"
"github.com/52funny/pikpakcli/internal/shell"
)

func main() {
cmd.Execute()
// Check if any args
if len(os.Args) == 1 {
cmd.ExecuteShell(shell.Start)
} else {
// If no arg, execute the command directly
cmd.Execute()
}
}