main.go (3232B)
1 package main 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "net/http" 8 "os" 9 "os/exec" 10 "runtime" 11 12 "git.ryansepassi.com/git/gotextlog.git/internal/config" 13 "git.ryansepassi.com/git/gotextlog.git/internal/textlog" 14 "git.ryansepassi.com/git/gotextlog.git/internal/tui" 15 tea "github.com/charmbracelet/bubbletea" 16 "github.com/charmbracelet/x/term" 17 ) 18 19 const version = "1.0.0" 20 const help = `textlog 1.0.0 21 22 Usage: textlog [options] 23 24 --url <origin> Textlog-compatible instance 25 --theme <auto|light|dark> Color theme 26 --help Show help 27 --version Show version 28 29 Environment: TEXTLOG_URL, TEXTLOG_TOKEN, TEXTLOG_THEME, NO_COLOR` 30 31 type arguments struct { 32 url string 33 theme config.Theme 34 help, version bool 35 } 36 37 func parseArgs(values []string) (arguments, error) { 38 var result arguments 39 for index := 0; index < len(values); index++ { 40 switch values[index] { 41 case "--help", "-h": 42 result.help = true 43 case "--version", "-v": 44 result.version = true 45 case "--url": 46 index++ 47 if index >= len(values) { 48 return result, errors.New("--url requires a value") 49 } 50 normalized, err := config.NormalizeBaseURL(values[index]) 51 if err != nil { 52 return result, err 53 } 54 result.url = normalized 55 case "--theme": 56 index++ 57 if index >= len(values) { 58 return result, errors.New("--theme requires a value") 59 } 60 result.theme = config.Theme(values[index]) 61 if result.theme != config.ThemeAuto && result.theme != config.ThemeLight && result.theme != config.ThemeDark { 62 return result, errors.New("--theme must be auto, light, or dark") 63 } 64 default: 65 return result, fmt.Errorf("Unknown option: %s", values[index]) 66 } 67 } 68 return result, nil 69 } 70 func main() { os.Exit(run(os.Args[1:], os.Stdin, os.Stdout, os.Stderr)) } 71 func run(values []string, stdin *os.File, stdout, stderr io.Writer) int { 72 args, err := parseArgs(values) 73 if err != nil { 74 fmt.Fprintln(stderr, err) 75 return 2 76 } 77 if args.help { 78 fmt.Fprintln(stdout, help) 79 return 0 80 } 81 if args.version { 82 fmt.Fprintln(stdout, version) 83 return 0 84 } 85 outFile, ok := stdout.(*os.File) 86 if !ok || !term.IsTerminal(stdin.Fd()) || !term.IsTerminal(outFile.Fd()) { 87 fmt.Fprintln(stderr, "textlog requires an interactive terminal") 88 return 1 89 } 90 env := config.Environment() 91 cfg, err := config.Load(env) 92 if err != nil { 93 fmt.Fprintln(stderr, err) 94 return 1 95 } 96 if args.url != "" { 97 cfg.BaseURL = args.url 98 } 99 if args.theme != "" { 100 cfg.Theme = args.theme 101 } 102 client := textlog.NewClient(cfg.BaseURL, cfg.Token, http.DefaultClient) 103 model := tui.New(tui.NewClientBackend(client), cfg, tui.Options{ 104 SaveConfig: func(next config.Config) error { return config.Save(next, env) }, 105 OpenURL: openBrowser, 106 }) 107 program := tea.NewProgram(model, tea.WithAltScreen(), tea.WithInput(stdin), tea.WithOutput(outFile)) 108 if _, err := program.Run(); err != nil { 109 fmt.Fprintln(stderr, err) 110 return 1 111 } 112 return 0 113 } 114 func openBrowser(url string) error { 115 var command *exec.Cmd 116 switch runtime.GOOS { 117 case "darwin": 118 command = exec.Command("open", url) 119 case "windows": 120 command = exec.Command("cmd", "/c", "start", "", url) 121 default: 122 command = exec.Command("xdg-open", url) 123 } 124 return command.Start() 125 }