ghorg/cmd/ls.go
2024-09-18 20:47:58 -07:00

272 lines
6.0 KiB
Go

package cmd
import (
"fmt"
"os"
"path/filepath"
"strings"
"sync"
"time"
"github.com/briandowns/spinner"
"github.com/gabrie30/ghorg/colorlog"
"github.com/gabrie30/ghorg/utils"
"github.com/spf13/cobra"
)
var lsCmd = &cobra.Command{
Use: "ls [dir]",
Short: "List contents of your ghorg home or ghorg directories",
Long: `If no dir is specified it will list contents of GHORG_ABSOLUTE_PATH_TO_CLONE_TO`,
Run: lsFunc,
}
var spinningSpinner *spinner.Spinner
func init() {
spinningSpinner = spinner.New(spinner.CharSets[14], 100*time.Millisecond)
}
func lsFunc(cmd *cobra.Command, argz []string) {
if len(argz) == 0 {
listGhorgHome()
}
if len(argz) >= 1 {
for _, arg := range argz {
listGhorgDir(arg)
}
}
}
func listGhorgHome() {
path := os.Getenv("GHORG_ABSOLUTE_PATH_TO_CLONE_TO")
files, err := os.ReadDir(path)
if err != nil {
colorlog.PrintError("No clones found. Please clone some and try again.")
}
longFormat := false
totalFormat := false
for _, arg := range os.Args {
if arg == "-l" || arg == "--long" {
longFormat = true
}
if arg == "-t" || arg == "--total" {
totalFormat = true
}
}
if !longFormat && !totalFormat {
for _, f := range files {
if f.IsDir() {
colorlog.PrintInfo(path + f.Name())
}
}
return
}
spinningSpinner.Start()
var totalDirs int
var totalSizeMB float64
var totalRepos int
type result struct {
dirPath string
dirSizeMB float64
subDirCount int
err error
}
results := make(chan result, len(files))
var wg sync.WaitGroup
for _, f := range files {
if f.IsDir() {
totalDirs++
wg.Add(1)
go func(f os.DirEntry) {
defer wg.Done()
dirPath := filepath.Join(path, f.Name())
dirSizeMB, err := utils.CalculateDirSizeInMb(dirPath)
if err != nil {
results <- result{dirPath: dirPath, err: err}
return
}
subDirCount := 0
subFiles, err := os.ReadDir(dirPath)
if err != nil {
results <- result{dirPath: dirPath, err: err}
return
}
for _, subFile := range subFiles {
if subFile.IsDir() {
subDirCount++
}
}
results <- result{dirPath: dirPath, dirSizeMB: dirSizeMB, subDirCount: subDirCount}
}(f)
}
}
go func() {
wg.Wait()
close(results)
}()
for res := range results {
if res.err != nil {
colorlog.PrintError(fmt.Sprintf("Error processing directory %s: %v", res.dirPath, res.err))
continue
}
totalSizeMB += res.dirSizeMB
totalRepos += res.subDirCount
if !totalFormat || longFormat {
spinningSpinner.Stop()
if longFormat {
if res.dirSizeMB > 1000 {
dirSizeGB := res.dirSizeMB / 1000
colorlog.PrintInfo(fmt.Sprintf("%-90s %10.2f GB %10d repos", res.dirPath, dirSizeGB, res.subDirCount))
} else {
colorlog.PrintInfo(fmt.Sprintf("%-90s %10.2f MB %10d repos", res.dirPath, res.dirSizeMB, res.subDirCount))
}
} else {
colorlog.PrintInfo(res.dirPath)
}
}
}
spinningSpinner.Stop()
if totalFormat {
if totalSizeMB > 1000 {
totalSizeGB := totalSizeMB / 1000
colorlog.PrintSuccess(fmt.Sprintf("Total: %d directories, %.2f GB, %d repos", totalDirs, totalSizeGB, totalRepos))
} else {
colorlog.PrintSuccess(fmt.Sprintf("Total: %d directories, %.2f MB, %d repos", totalDirs, totalSizeMB, totalRepos))
}
}
}
func listGhorgDir(arg string) {
path := os.Getenv("GHORG_ABSOLUTE_PATH_TO_CLONE_TO") + arg
_, err := os.ReadDir(path)
if err != nil {
// ghorg natively uses underscores in folder names, but a user can specify an output dir with underscores
// so first try what the user types if not then try replace
arg = strings.ReplaceAll(arg, "-", "_")
path = os.Getenv("GHORG_ABSOLUTE_PATH_TO_CLONE_TO") + arg
}
files, err := os.ReadDir(path)
if err != nil {
colorlog.PrintError("No clones found. Please clone some and try again.")
}
longFormat := false
totalFormat := false
for _, arg := range os.Args {
if arg == "-l" || arg == "--long" {
longFormat = true
}
if arg == "-t" || arg == "--total" {
totalFormat = true
}
}
if !longFormat && !totalFormat {
for _, f := range files {
if f.IsDir() {
str := filepath.Join(path, f.Name())
colorlog.PrintInfo(str)
}
}
return
}
spinningSpinner.Start()
var totalDirs int
var totalSizeMB float64
type result struct {
dirPath string
dirSizeMB float64
subDirCount int
err error
}
results := make(chan result)
var wg sync.WaitGroup
for _, f := range files {
if f.IsDir() {
wg.Add(1)
go func(f os.DirEntry) {
defer wg.Done()
dirPath := filepath.Join(path, f.Name())
dirSizeMB, err := utils.CalculateDirSizeInMb(dirPath)
if err != nil {
results <- result{dirPath: dirPath, err: err}
return
}
// Count the number of directories with a depth of 1 inside
subDirCount := 0
subFiles, err := os.ReadDir(dirPath)
if err != nil {
results <- result{dirPath: dirPath, err: err}
return
}
for _, subFile := range subFiles {
if subFile.IsDir() {
subDirCount++
}
}
results <- result{dirPath: dirPath, dirSizeMB: dirSizeMB, subDirCount: subDirCount}
}(f)
}
}
go func() {
wg.Wait()
close(results)
}()
for res := range results {
if res.err != nil {
colorlog.PrintError(fmt.Sprintf("Error processing directory %s: %v", res.dirPath, res.err))
continue
}
totalSizeMB += res.dirSizeMB
totalDirs++
if !totalFormat || longFormat {
spinningSpinner.Stop()
if longFormat {
if res.dirSizeMB > 1000 {
dirSizeGB := res.dirSizeMB / 1000
colorlog.PrintInfo(fmt.Sprintf("%-90s %10.2f GB ", res.dirPath, dirSizeGB))
} else {
colorlog.PrintInfo(fmt.Sprintf("%-90s %10.2f MB", res.dirPath, res.dirSizeMB))
}
} else {
colorlog.PrintInfo(res.dirPath)
}
}
}
spinningSpinner.Stop()
if totalFormat {
if totalSizeMB > 1000 {
totalSizeGB := totalSizeMB / 1000
colorlog.PrintSuccess(fmt.Sprintf("Total: %d repos, %.2f GB", totalDirs, totalSizeGB))
} else {
colorlog.PrintSuccess(fmt.Sprintf("Total: %d repos, %.2f MB", totalDirs, totalSizeMB))
}
}
}