1use std::path::PathBuf;
2
3use clap::{Parser, Subcommand, ValueEnum};
4
5#[derive(Debug, Clone, Copy, ValueEnum)]
6pub enum FormatArg {
7 Org,
8 Latex,
9 Markdown,
10 Plaintext,
11}
12
13#[derive(Debug, Clone, Copy, ValueEnum)]
14pub enum OutputFormat {
15 Text,
16 Json,
17 Sarif,
18}
19
20#[derive(Debug, Parser)]
21#[command(name = "snapper", version, about = "Semantic line break formatter")]
22pub struct Cli {
23 #[command(subcommand)]
24 pub command: Option<Commands>,
25
26 #[arg()]
28 pub files: Vec<PathBuf>,
29
30 #[arg(short, long)]
32 pub format: Option<FormatArg>,
33
34 #[arg(long)]
36 pub stdin_filepath: Option<PathBuf>,
37
38 #[arg(short, long)]
40 pub output: Option<PathBuf>,
41
42 #[arg(short, long)]
44 pub in_place: bool,
45
46 #[arg(short = 'w', long, default_value_t = 0)]
48 pub max_width: usize,
49
50 #[arg(long)]
52 pub neural: bool,
53
54 #[arg(long, default_value = "en")]
57 pub lang: String,
58
59 #[arg(long)]
61 pub model_path: Option<PathBuf>,
62
63 #[arg(long)]
65 pub check: bool,
66
67 #[arg(long)]
69 pub diff: bool,
70
71 #[arg(long)]
73 pub config: Option<PathBuf>,
74
75 #[arg(long)]
77 pub range: Option<String>,
78
79 #[arg(long, default_value = "text")]
81 pub output_format: OutputFormat,
82}
83
84#[derive(Debug, Subcommand)]
85pub enum Commands {
86 Init {
88 #[arg(long)]
90 dry_run: bool,
91 },
92 Sdiff {
94 old: PathBuf,
96 new: PathBuf,
98 #[arg(short, long)]
100 format: Option<FormatArg>,
101 #[arg(long)]
103 no_color: bool,
104 },
105 GitDiff {
107 #[arg(default_value = "HEAD")]
109 git_ref: String,
110 #[arg()]
112 files: Vec<PathBuf>,
113 #[arg(short, long)]
115 format: Option<FormatArg>,
116 #[arg(long)]
118 no_color: bool,
119 },
120 Lsp,
122 Watch {
124 #[arg(required = true)]
126 patterns: Vec<String>,
127 #[arg(short, long)]
129 format: Option<FormatArg>,
130 },
131}
132
133pub fn parse_range(s: &str) -> Option<(usize, usize)> {
135 let parts: Vec<&str> = s.split(':').collect();
136 if parts.len() != 2 {
137 return None;
138 }
139 let start = parts[0].parse::<usize>().ok()?;
140 let end = parts[1].parse::<usize>().ok()?;
141 if start == 0 || end == 0 || start > end {
142 return None;
143 }
144 Some((start, end))
145}