Skip to content

Commit a434d2c

Browse files
committed
Add initial cli macro implementation
1 parent 02acb42 commit a434d2c

File tree

4 files changed

+78
-85
lines changed

4 files changed

+78
-85
lines changed

src/bin/uname.rs

Lines changed: 15 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
use std::io::{Write, stdout};
22

3-
use coreutils::{Result, help_text, version_text};
3+
use coreutils::{Result, cli};
44
use rustix::system::uname;
5-
use sap::{
6-
Argument::{Long, Short},
7-
Parser,
8-
};
95

106
bitflags::bitflags! {
117
#[rustfmt::skip]
@@ -21,42 +17,25 @@ bitflags::bitflags! {
2117
}
2218
}
2319

24-
const VERSION: &str = version_text!("uname");
25-
const HELP: &str = help_text!("uname");
26-
2720
fn main() -> Result {
2821
let mut info_mask = Info::empty();
2922
let mut stdout = stdout();
3023

31-
let mut arg_parser = Parser::from_env()?;
32-
33-
while let Some(arg) = arg_parser.forward()? {
34-
match arg {
35-
Long("version") => {
36-
stdout.write_all(VERSION.as_bytes())?;
37-
stdout.flush()?;
38-
return Ok(());
39-
}
40-
Long("help") => {
41-
stdout.write_all(HELP.as_bytes())?;
42-
stdout.flush()?;
43-
return Ok(());
44-
}
45-
Short('a') | Long("all") => {
46-
info_mask = Info::all();
47-
}
48-
Short('s') | Long("kernel-name") => info_mask |= Info::KERNEL_NAME,
49-
Short('n') | Long("nodename") => info_mask |= Info::NODENAME,
50-
Short('r') | Long("kernel-release") => info_mask |= Info::KERNEL_RELEASE,
51-
Short('v') | Long("kernel-version") => info_mask |= Info::KERNEL_VERSION,
52-
Short('m') | Long("machine") => info_mask |= Info::MACHINE,
53-
Short('p') | Long("processor") => info_mask |= Info::PROCESSOR,
54-
Short('i') | Long("hardware-platform") => info_mask |= Info::HARDWARE_PLATFORM,
55-
Short('o') | Long("operating-system") => info_mask |= Info::OPERATING_SYSTEM,
56-
57-
argument => return Err(argument.into_error(None).into()),
24+
cli! {
25+
"uname", stdout, while,
26+
Short('a') | Long("all") => {
27+
info_mask = Info::all();
5828
}
59-
}
29+
Short('s') | Long("kernel-name") => info_mask |= Info::KERNEL_NAME
30+
Short('n') | Long("nodename") => info_mask |= Info::NODENAME
31+
Short('r') | Long("kernel-release") => info_mask |= Info::KERNEL_RELEASE
32+
Short('v') | Long("kernel-version") => info_mask |= Info::KERNEL_VERSION
33+
Short('m') | Long("machine") => info_mask |= Info::MACHINE
34+
Short('p') | Long("processor") => info_mask |= Info::PROCESSOR
35+
Short('i') | Long("hardware-platform") => info_mask |= Info::HARDWARE_PLATFORM
36+
Short('o') | Long("operating-system") => info_mask |= Info::OPERATING_SYSTEM
37+
arg => return Err(arg.into_error(None).into())
38+
};
6039

6140
if info_mask.is_empty() {
6241
info_mask = Info::KERNEL_NAME;

src/bin/whoami.rs

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,18 @@
11
use std::io::{Write, stderr, stdout};
22

33
use acumen::getpwuid;
4-
use coreutils::{Exit, Result, help_text, version_text};
4+
use coreutils::{Result, cli};
55
use rustix::process::geteuid;
6-
use sap::{Argument::Long, Parser};
7-
8-
const VERSION: &str = version_text!("whoami");
9-
const HELP: &str = help_text!("whoami");
106

117
const CANNOT_FIND_UID: &[u8] = b"cannot find name for user ID: ";
128

139
fn main() -> Result {
1410
let mut stdout = stdout();
15-
let mut arg_parser = Parser::from_env()?;
16-
17-
if let Some(arg) = arg_parser.forward()? {
18-
match arg {
19-
Long("version") => stdout.write_all(VERSION.as_bytes())?,
20-
Long("help") => stdout.write_all(HELP.as_bytes())?,
21-
invalid => return Err(Exit::ArgError(invalid.into_error(None))),
22-
}
2311

24-
stdout.flush()?;
25-
26-
return Ok(());
27-
}
12+
cli! {
13+
"whoami", stdout, if,
14+
arg => return Err(arg.into_error(None).into())
15+
};
2816

2917
let uid = geteuid();
3018

src/bin/yes.rs

Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,43 +4,21 @@ use std::{
44
os::unix::ffi::OsStringExt,
55
};
66

7-
use coreutils::{Exit, Result, help_text, version_text};
8-
use sap::{
9-
Argument::{Long, Short, Value},
10-
Parser,
11-
};
12-
13-
const VERSION: &str = version_text!("yes");
14-
const HELP: &str = help_text!("yes");
7+
use coreutils::{Result, cli};
158

169
fn main() -> Result {
17-
let mut arg_parser = Parser::from_env()?;
18-
1910
// No point in locking stdout since we only use it once in this program
2011
let mut stdout = stdout();
2112

2213
let mut buffer: Option<Vec<u8>> = None;
2314

24-
while let Some(arg) = arg_parser.forward()? {
25-
match arg {
26-
Long("version") => {
27-
stdout.write_all(VERSION.as_bytes())?;
28-
stdout.flush()?;
29-
30-
return Ok(());
31-
}
32-
Long("help") => {
33-
stdout.write_all(HELP.as_bytes())?;
34-
stdout.flush()?;
35-
36-
return Ok(());
37-
}
38-
Value(value) => {
39-
extend_buffer(&mut buffer, value.as_bytes().to_vec());
40-
}
41-
Long(_) | Short(_) => return Err(Exit::ArgError(arg.into_error(None))),
15+
let arg_parser = cli! {
16+
"yes", stdout, while,
17+
Value(value) => {
18+
extend_buffer(&mut buffer, value.as_bytes().to_vec());
4219
}
43-
}
20+
arg => return Err(arg.into_error(None).into())
21+
};
4422

4523
arg_parser
4624
.into_inner()

src/lib.rs

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ impl From<io::Error> for Exit {
4747

4848
#[macro_export]
4949
macro_rules! version_text {
50-
($name: literal, $authors: literal) => {
50+
($name:literal, $authors:literal) => {
5151
concat!(
5252
$name,
5353
" (puppyutils) ",
@@ -58,7 +58,7 @@ macro_rules! version_text {
5858
)
5959
};
6060

61-
($name: literal) => {
61+
($name:literal) => {
6262
concat!(
6363
$name,
6464
" (puppyutils) ",
@@ -71,11 +71,59 @@ macro_rules! version_text {
7171

7272
#[macro_export]
7373
macro_rules! help_text {
74-
($name: literal) => {
74+
($name:literal) => {
7575
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/", $name, ".txt"))
7676
};
7777
}
7878

79+
/// Internal helper macro
80+
#[macro_export]
81+
#[doc(hidden)]
82+
macro_rules! _cli_impl {
83+
($name:literal, $stdout:ident, $loop_type:tt, $($item:pat => $matcher:expr)*) => {
84+
{
85+
use std::io::Write;
86+
use sap::Argument::*;
87+
88+
let mut arg_parser = sap::Parser::from_env()?;
89+
90+
$loop_type let Some(arg) = arg_parser.forward()? {
91+
match arg {
92+
Long("version") => {
93+
$stdout.write_all($crate::version_text!($name).as_bytes())?;
94+
$stdout.flush()?;
95+
return Ok(());
96+
}
97+
Long("help") => {
98+
$stdout.write_all($crate::help_text!($name).as_bytes())?;
99+
$stdout.flush()?;
100+
return Ok(());
101+
}
102+
$($item => $matcher,)*
103+
}
104+
}
105+
106+
arg_parser
107+
}
108+
};
109+
}
110+
111+
#[macro_export]
112+
macro_rules! cli {
113+
($name:literal, $stdout:ident, while, $($item:pat => $matcher:expr)*) => {
114+
$crate::_cli_impl!($name, $stdout, while, $($item => $matcher)*)
115+
};
116+
117+
($name:literal, $stdout:ident, if, $($item:pat => $matcher:expr)*) => {
118+
$crate::_cli_impl!($name, $stdout, if, $($item => $matcher)*)
119+
};
120+
}
121+
122+
// Usage examples:
123+
// cli!("myapp", stdout, while, arg1 => handle_arg1(), arg2 => handle_arg2());
124+
// cli!("myapp", stdout, if, arg1 => handle_arg1(), arg2 => handle_arg2());
125+
// cli!("myapp", stdout, arg1 => handle_arg1(), arg2 => handle_arg2()); // defaults to while
126+
79127
/// Gets the umask of the calling process
80128
pub fn get_umask() -> Mode {
81129
/*

0 commit comments

Comments
 (0)