205 lines
5.0 KiB
Go
205 lines
5.0 KiB
Go
package tui
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"charm.land/huh/v2"
|
|
"charm.land/huh/v2/spinner"
|
|
|
|
"dirmd/internal/config"
|
|
)
|
|
|
|
func Run() error {
|
|
var (
|
|
inputDir string
|
|
outputDir string
|
|
force bool
|
|
verticalSlices bool
|
|
skipFrontend bool
|
|
frontendOnly bool
|
|
omitTest bool
|
|
mitMd bool
|
|
protonDrive bool
|
|
drivePath string
|
|
maxDepthStr string
|
|
maxSizeStr string
|
|
ignorePatterns string
|
|
)
|
|
|
|
inputDir = "."
|
|
maxDepthStr = "20"
|
|
maxSizeStr = "512"
|
|
defOut := defaultOutputDir()
|
|
outputDir = defOut
|
|
|
|
form := huh.NewForm(
|
|
huh.NewGroup(
|
|
huh.NewInput().Title("Input directory").Value(&inputDir).Placeholder("."),
|
|
huh.NewInput().Title("Output directory").Value(&outputDir).Placeholder(defOut),
|
|
huh.NewConfirm().Title("Force overwrite?").Value(&force),
|
|
huh.NewConfirm().Title("Vertical slices?").Value(&verticalSlices),
|
|
huh.NewConfirm().Title("Skip frontend files?").Value(&skipFrontend),
|
|
huh.NewConfirm().Title("Frontend only?").Value(&frontendOnly),
|
|
huh.NewConfirm().Title("Omit test files?").Value(&omitTest),
|
|
huh.NewConfirm().Title("Omit non-README markdown?").Value(&mitMd),
|
|
huh.NewConfirm().Title("Upload to Proton Drive?").Value(&protonDrive),
|
|
huh.NewInput().Title("Max depth").Value(&maxDepthStr).Placeholder("20"),
|
|
huh.NewInput().Title("Max size (KB)").Value(&maxSizeStr).Placeholder("512"),
|
|
huh.NewText().Title("Ignore patterns (one per line)").Value(&ignorePatterns).Placeholder("One pattern per line..."),
|
|
),
|
|
huh.NewGroup(
|
|
huh.NewInput().Title("Remote path on Proton Drive").Value(&drivePath).Placeholder("/my-files/md/"),
|
|
).WithHideFunc(func() bool {
|
|
return !protonDrive
|
|
}),
|
|
)
|
|
|
|
if err := form.Run(); err != nil {
|
|
return fmt.Errorf("form: %w", err)
|
|
}
|
|
|
|
maxDepth, err := strconv.Atoi(strings.TrimSpace(maxDepthStr))
|
|
if err != nil || maxDepth <= 0 {
|
|
maxDepth = 20
|
|
}
|
|
|
|
maxSizeKB, err := strconv.Atoi(strings.TrimSpace(maxSizeStr))
|
|
if err != nil || maxSizeKB <= 0 {
|
|
maxSizeKB = 512
|
|
}
|
|
|
|
var ignores []string
|
|
raw := strings.TrimSpace(ignorePatterns)
|
|
if raw != "" {
|
|
for _, line := range strings.Split(raw, "\n") {
|
|
line = strings.TrimSpace(line)
|
|
if line != "" {
|
|
ignores = append(ignores, line)
|
|
}
|
|
}
|
|
}
|
|
|
|
if skipFrontend && frontendOnly {
|
|
return fmt.Errorf("--skip-frontend and --frontend-only are mutually exclusive")
|
|
}
|
|
|
|
excludedExts := append([]string{}, config.DefaultExts...)
|
|
if skipFrontend {
|
|
excludedExts = append(excludedExts, config.FrontendExts...)
|
|
}
|
|
|
|
allIgnores := append([]string{}, config.DefaultIgnores...)
|
|
allIgnores = append(allIgnores, ignores...)
|
|
|
|
cfg := &config.Config{
|
|
InputPath: inputDir,
|
|
OutputPath: outputDir,
|
|
Force: force,
|
|
MaxSize: int64(maxSizeKB) * 1024,
|
|
MaxDepth: maxDepth,
|
|
Ignores: allIgnores,
|
|
Extensions: excludedExts,
|
|
ProtonDrive: protonDrive,
|
|
DrivePath: drivePath,
|
|
VerticalSlices: verticalSlices,
|
|
SkipFrontend: skipFrontend,
|
|
FrontendOnly: frontendOnly,
|
|
OmitTest: omitTest,
|
|
OmitMd: mitMd,
|
|
}
|
|
|
|
cmd := buildCommand(cfg)
|
|
fmt.Fprintf(os.Stderr, "%s\n", cmd)
|
|
|
|
var result GenerateResult
|
|
if err := spinner.New().
|
|
Title("Generating...").
|
|
Action(func() {
|
|
result = Generate(cfg)
|
|
}).
|
|
Run(); err != nil {
|
|
return fmt.Errorf("interrupted: %w", err)
|
|
}
|
|
|
|
if result.Error != nil {
|
|
return result.Error
|
|
}
|
|
|
|
for _, f := range result.Files {
|
|
fmt.Fprintf(os.Stdout, " %s\n", f)
|
|
}
|
|
if result.Uploaded {
|
|
fmt.Fprintln(os.Stdout, "Uploaded to Proton Drive")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func defaultOutputDir() string {
|
|
cwd, err := os.Getwd()
|
|
if err != nil {
|
|
return "/tmp/output_dirmd"
|
|
}
|
|
base := filepath.Base(cwd)
|
|
return filepath.Join("/tmp", base+"_dirmd")
|
|
}
|
|
|
|
func buildCommand(cfg *config.Config) string {
|
|
var parts []string
|
|
parts = append(parts, "dirmd")
|
|
parts = append(parts, "-o", cfg.OutputPath)
|
|
if cfg.Force {
|
|
parts = append(parts, "-f")
|
|
}
|
|
if cfg.VerticalSlices {
|
|
parts = append(parts, "--vertical-slices")
|
|
}
|
|
if cfg.SkipFrontend {
|
|
parts = append(parts, "--skip-frontend")
|
|
}
|
|
if cfg.FrontendOnly {
|
|
parts = append(parts, "--frontend-only")
|
|
}
|
|
if cfg.OmitTest {
|
|
parts = append(parts, "--omit-test")
|
|
}
|
|
if cfg.OmitMd {
|
|
parts = append(parts, "--omit-md")
|
|
}
|
|
if cfg.ProtonDrive {
|
|
parts = append(parts, "--proton-drive")
|
|
if cfg.DrivePath != "" {
|
|
parts = append(parts, "--drive-path", cfg.DrivePath)
|
|
}
|
|
}
|
|
if cfg.MaxDepth != 20 {
|
|
parts = append(parts, "--max-depth", strconv.Itoa(cfg.MaxDepth))
|
|
}
|
|
if cfg.MaxSize != 512*1024 {
|
|
parts = append(parts, "--max-size", strconv.FormatInt(cfg.MaxSize, 10))
|
|
}
|
|
for _, ig := range extraIgnores(cfg) {
|
|
parts = append(parts, "--ignore", ig)
|
|
}
|
|
parts = append(parts, cfg.InputPath)
|
|
return strings.Join(parts, " ")
|
|
}
|
|
|
|
func extraIgnores(cfg *config.Config) []string {
|
|
defaultSet := make(map[string]bool)
|
|
for _, d := range config.DefaultIgnores {
|
|
defaultSet[d] = true
|
|
}
|
|
var extra []string
|
|
for _, ig := range cfg.Ignores {
|
|
if !defaultSet[ig] {
|
|
extra = append(extra, ig)
|
|
}
|
|
}
|
|
return extra
|
|
}
|