mirror of
https://github.com/tw93/Mole.git
synced 2026-03-24 08:25:06 +00:00
feat: add --json flag to analyze command for non-TTY environments (#533)
* feat: add --json flag to analyze command * feat: implement JSON output mode for analyze * refactor: rename jsonOutput flag to jsonMode to avoid conflict
This commit is contained in:
79
cmd/analyze/json.go
Normal file
79
cmd/analyze/json.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
//go:build darwin
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"sync/atomic"
|
||||||
|
)
|
||||||
|
|
||||||
|
type jsonOutput struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
Entries []jsonEntry `json:"entries"`
|
||||||
|
TotalSize int64 `json:"total_size"`
|
||||||
|
TotalFiles int64 `json:"total_files"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type jsonEntry struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
IsDir bool `json:"is_dir"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func runJSONMode(path string, isOverview bool) {
|
||||||
|
result := performScanForJSON(path)
|
||||||
|
|
||||||
|
encoder := json.NewEncoder(os.Stdout)
|
||||||
|
encoder.SetIndent("", " ")
|
||||||
|
if err := encoder.Encode(result); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "failed to encode JSON: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func performScanForJSON(path string) jsonOutput {
|
||||||
|
var filesScanned, dirsScanned, bytesScanned int64
|
||||||
|
currentPath := &atomic.Value{}
|
||||||
|
currentPath.Store("")
|
||||||
|
|
||||||
|
items, err := os.ReadDir(path)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "failed to read directory: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
var entries []jsonEntry
|
||||||
|
var totalSize int64
|
||||||
|
|
||||||
|
for _, item := range items {
|
||||||
|
fullPath := path + "/" + item.Name()
|
||||||
|
var size int64
|
||||||
|
|
||||||
|
if item.IsDir() {
|
||||||
|
size = calculateDirSizeFast(fullPath, &filesScanned, &dirsScanned, &bytesScanned, currentPath)
|
||||||
|
} else {
|
||||||
|
info, err := item.Info()
|
||||||
|
if err == nil {
|
||||||
|
size = info.Size()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
totalSize += size
|
||||||
|
entries = append(entries, jsonEntry{
|
||||||
|
Name: item.Name(),
|
||||||
|
Path: fullPath,
|
||||||
|
Size: size,
|
||||||
|
IsDir: item.IsDir(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonOutput{
|
||||||
|
Path: path,
|
||||||
|
Entries: entries,
|
||||||
|
TotalSize: totalSize,
|
||||||
|
TotalFiles: filesScanned,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@@ -16,6 +17,10 @@ import (
|
|||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
jsonMode = flag.Bool("json", false, "output analysis as JSON instead of TUI")
|
||||||
|
)
|
||||||
|
|
||||||
type dirEntry struct {
|
type dirEntry struct {
|
||||||
Name string
|
Name string
|
||||||
Path string
|
Path string
|
||||||
@@ -127,9 +132,11 @@ func (m model) inOverviewMode() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
target := os.Getenv("MO_ANALYZE_PATH")
|
target := os.Getenv("MO_ANALYZE_PATH")
|
||||||
if target == "" && len(os.Args) > 1 {
|
if target == "" && len(flag.Args()) > 0 {
|
||||||
target = os.Args[1]
|
target = flag.Args()[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
var abs string
|
var abs string
|
||||||
@@ -148,12 +155,20 @@ func main() {
|
|||||||
isOverview = false
|
isOverview = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *jsonMode {
|
||||||
|
runJSONMode(abs, isOverview)
|
||||||
|
} else {
|
||||||
|
runTUIMode(abs, isOverview)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runTUIMode(path string, isOverview bool) {
|
||||||
// Warm overview cache in background.
|
// Warm overview cache in background.
|
||||||
prefetchCtx, prefetchCancel := context.WithTimeout(context.Background(), 30*time.Second)
|
prefetchCtx, prefetchCancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||||
defer prefetchCancel()
|
defer prefetchCancel()
|
||||||
go prefetchOverviewCache(prefetchCtx)
|
go prefetchOverviewCache(prefetchCtx)
|
||||||
|
|
||||||
p := tea.NewProgram(newModel(abs, isOverview), tea.WithAltScreen())
|
p := tea.NewProgram(newModel(path, isOverview), tea.WithAltScreen())
|
||||||
if _, err := p.Run(); err != nil {
|
if _, err := p.Run(); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "analyzer error: %v\n", err)
|
fmt.Fprintf(os.Stderr, "analyzer error: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|||||||
Reference in New Issue
Block a user