init commit

This commit is contained in:
Yehoshua Adam Sandler
2026-02-05 15:33:07 -06:00
commit e70b629b4a
13 changed files with 1021 additions and 0 deletions

136
cmd/root.go Normal file
View File

@@ -0,0 +1,136 @@
package cmd
import (
"fmt"
"os"
"path/filepath"
"strings"
"mach/clipboard"
"mach/config"
"mach/convert"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var Version = "dev"
var (
inputType string
outputType string
outputPath string
display bool
)
var rootCmd = &cobra.Command{
Use: "mach <file>",
Short: "Convert Markdown to Rich Text and copy to clipboard",
Long: "mach reads a Markdown file, converts it to RTF, and copies the result to the system clipboard.",
Args: cobra.RangeArgs(0, 1),
RunE: runMach,
}
func Execute() {
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
}
func init() {
cobra.OnInitialize(config.Init)
rootCmd.Flags().StringVarP(&inputType, "input", "i", "", "input format (default from config or \"md\")")
rootCmd.Flags().StringVarP(&outputType, "type", "t", "", "output format (default from config or \"rtf\")")
rootCmd.Flags().StringVarP(&outputPath, "output", "o", "", "output to file; use -o or -o=path")
rootCmd.Flags().BoolVarP(&display, "display", "d", false, "display converted text in terminal")
rootCmd.Flags().BoolP("version", "v", false, "print version and exit")
// Allow -o without a value (derives path from input file)
rootCmd.Flags().Lookup("output").NoOptDefVal = " "
}
func runMach(cmd *cobra.Command, args []string) error {
// Handle version flag
if v, _ := cmd.Flags().GetBool("version"); v {
fmt.Printf("mach %s\n", Version)
return nil
}
if len(args) == 0 {
return fmt.Errorf("no input file specified (use mach <file>)")
}
inputFile := args[0]
// Resolve input/output types: CLI flag > config > default
inType := resolveType(inputType, "input_type", "md")
outType := resolveType(outputType, "output_type", "rtf")
_ = inType // reserved for future input format support
if outType != "rtf" {
return fmt.Errorf("unsupported output type: %s (only \"rtf\" is currently supported)", outType)
}
// Read input file
source, err := os.ReadFile(inputFile)
if err != nil {
return fmt.Errorf("cannot read file: %w", err)
}
// Convert to RTF (for file output and display)
rtf, err := convert.Convert(source)
if err != nil {
return fmt.Errorf("conversion error: %w", err)
}
// Convert to HTML (for clipboard — web apps like Slack/Gmail need text/html)
html, err := convert.ConvertToHTML(source)
if err != nil {
return fmt.Errorf("html conversion error: %w", err)
}
// Display if requested
if display {
fmt.Println(string(rtf))
}
// Write to file if -o was given
if cmd.Flags().Changed("output") {
outPath := outputPath
if strings.TrimSpace(outPath) == "" {
outPath = deriveOutputPath(inputFile, outType)
}
if err := os.WriteFile(outPath, rtf, 0644); err != nil {
return fmt.Errorf("cannot write output file: %w", err)
}
fmt.Printf("wrote %s\n", outPath)
}
// Copy to clipboard (both RTF for native apps, HTML for web apps)
if err := clipboard.Copy(rtf, html); err != nil {
return fmt.Errorf("clipboard error: %w", err)
}
if !display {
fmt.Println("copied to clipboard")
}
return nil
}
func resolveType(flag, configKey, fallback string) string {
if flag != "" {
return flag
}
if v := viper.GetString(configKey); v != "" {
return v
}
return fallback
}
func deriveOutputPath(inputFile, outType string) string {
ext := filepath.Ext(inputFile)
base := strings.TrimSuffix(inputFile, ext)
return base + "." + outType
}