-
Notifications
You must be signed in to change notification settings - Fork 8
/
main.rs
159 lines (144 loc) · 4.34 KB
/
main.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
//! This is the nufmt binary documentation
//!
//! # Usage
//!
//! ```text
//! nufmt [OPTIONS] [FILES] ...
//! ```
//! ## Files
//!
//! `Files` are a list of files. It cannot be used combined with `--stdin`.
//! You can format many files with one command!. For example:
//!
//! ```text
//! nufmt my-file1.nu my-file2.nu my-file3.nu
//! ```
//!
//! ## Options
//!
//! - `-s` or `--stdin` formats from `stdin`, returns to `stdout` as a String. It cannot be used combined with `files`.
//!
//! - `-c` or `--config` pass the config file path.
//!
//! Sample:
//!
//! ```text
//! nufmt <files> --config my-config.json
//! ```
//!
//! or
//!
//! ```text
//! nufmt --stdin <string> --config my-stdin-config.json
//! ```
//!
//! - `-h` or `--help` show help and exit
//!
//! - `-v` or `--version` prints the version and exit
use anyhow::{Ok, Result};
use clap::Parser;
use log::trace;
use nufmt::config::Config;
use nufmt::{format_single_file, format_string};
use std::error::Error;
use std::io::Write;
use std::path::PathBuf;
/// wrapper to the successful exit code
const SUCCESSFUL_EXIT: i32 = 0;
/// wrapper to the failure exit code
const FAILED_EXIT: i32 = 1;
/// Main CLI struct.
///
/// The derive Clippy API starts from defining the CLI struct
#[derive(Parser)]
#[command(author, version, about)]
struct Cli {
/// The list of files passed in the cmdline
/// It is required and it cannot be used with `--stdin`
#[arg(
required_unless_present("stdin"),
help = "The file or files you want to format in nu"
)]
files: Vec<PathBuf>,
/// The string you pass in stdin. You can pass only one string.
#[arg(
short,
long,
conflicts_with = "files",
help = "Format the code passed in stdin as a string."
)]
stdin: Option<String>,
/// The optional config file you can pass in the cmdline
/// You can only pass a file config, not a flag config
#[arg(short, long, help = "The configuration file")]
config: Option<PathBuf>,
}
fn main() -> Result<(), Box<dyn Error>> {
// set up logger
env_logger::init();
let cli = Cli::parse();
trace!("recieved cli.files: {:?}", cli.files);
trace!("recieved cli.stdin: {:?}", cli.stdin);
trace!("recieved cli.config: {:?}", cli.config);
let cli_config = match cli.config {
None => Config::default(),
Some(input_cli) => {
todo!(
"cannot read from {:?} Reading a config from file not implemented!",
input_cli
)
}
};
// Note the deref and reborrow here to obtain a slice
// so rust doesnt complain for the [] arm
let exit_code = match &*cli.files {
// if cli.files is an empty list,
// it means the flag --stdin was passed
[] => execute_string(cli.stdin, &cli_config)?,
_ => execute_files(cli.files, &cli_config)?,
};
// Make sure standard output is flushed before we exit.
std::io::stdout().flush().unwrap();
trace!("exit code: {exit_code}");
// Exit with given exit code.
//
// NOTE: this immediately terminates the process without doing any cleanup,
// so make sure to finish all necessary cleanup before this is called.
std::process::exit(exit_code);
}
/// returns the string formatted to `stdout`
fn execute_string(string: Option<String>, options: &Config) -> Result<i32> {
// format the string
let output = format_string(&string.unwrap(), options);
println!("output: \n{output}");
Ok(SUCCESSFUL_EXIT)
}
/// Sends the files to format in lib.rs
fn execute_files(files: Vec<PathBuf>, options: &Config) -> Result<i32> {
// walk the files in the vec of files
for file in &files {
if !file.exists() {
eprintln!("Error: {} not found!", file.to_str().unwrap());
return Ok(FAILED_EXIT);
} else if file.is_dir() {
eprintln!(
"Error: {} is a directory. Please pass files only.",
file.to_str().unwrap()
);
return Ok(FAILED_EXIT);
}
// send the file to lib.rs
println!("formatting file: {:?}", file);
format_single_file(file, options);
}
Ok(SUCCESSFUL_EXIT)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn clap_cli_construction() {
use clap::CommandFactory;
Cli::command().debug_assert()
}
}