137 lines
3.3 KiB
Go
137 lines
3.3 KiB
Go
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
|
|
}
|