-
Notifications
You must be signed in to change notification settings - Fork 1
/
single.rs
120 lines (95 loc) · 3.99 KB
/
single.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
//! An example for a single command application.
//!
//! In this example, you can see that the application is broken down into three parts: the command
//! line options, the entrypoint, and the function that does the real work. For processing of the
//! command line options, [`clap`] is being used. The entrypoint and function both use parts of
//! [`carli`] to handle IO and errors that may result from the function.
use carli::io::Streams;
use carli::prelude::app::*;
use carli::prelude::cmd::*;
use clap::Parser;
use std::io::Write;
/// An example application that is a single command.
#[derive(Parser)]
pub struct Application {
/// A flag to determine if the command should result in an error.
#[clap(short, long)]
error: bool,
}
/// A simple function that does the real work in the application.
///
/// This is the meat-and-potatoes" of the application. The function takes the parsed command line
/// arguments, input and output streams, and does something with it. In this example, the function
/// will simply use the `--error` flag to determine if the application should exit with an error or
/// success.
fn example(app: &Application, streams: &Streams) -> Result<()> {
// Requesting that we produce an error?
if app.error {
// Write to the error output stream.
writeln!(streams.error(), "The command is about to fail!")?;
// Returns with an error that has a message and exit status code.
err!(1, "The command failed.");
// Expecting success?
} else {
// Write to the global output stream.
writeln!(streams.output(), "Hello, world!")?;
// Since the result ultimately ends up in the entrypoint function (main), we cannot
// return any result. So, we return nothing and let the operating environment take
// it from there.
Ok(())
}
}
/// Sets up and executes the command.
fn main() {
// Parse the command line options into an instance of a type.
let app = Application::parse();
// Uses the standard input and output streams.
let streams = standard();
// Do the real work, and exit if there is an error.
if let Err(error) = example(&app, &streams) {
error.exit();
}
// If there is no error, the application will naturaly exit with a 0 (zero) status code.
}
#[cfg(test)]
mod test {
use super::{example, Application};
use carli::prelude::test::*;
use std::io::{Seek, SeekFrom};
/// Verifies that when the `error` flag is used, the function returns a failing response.
#[test]
fn example_should_fail() {
// Create the `Application` instance with the error flag set.
let app = Application { error: true };
// Create streams that we can debug.
let streams = memory();
// Do the real work.
let result = example(&app, &streams);
// Make sure we got the error we were expecting.
assert!(result.is_err());
let error = result.unwrap_err();
assert_eq!(error.get_message(), Some("The command failed."));
assert_eq!(error.get_status(), 1);
// Make sure the expected error output was written.
streams.error().seek(SeekFrom::Start(0)).unwrap();
assert_eq!(
streams.error().to_string_lossy(),
"The command is about to fail!\n"
);
}
/// Verifies that the function returns a successful response if the `error` flag is not used.
#[test]
fn example_should_succeed() {
// Create the `Application` instance without the error flag set.
let app = Application { error: false };
// Create streams that we can debug.
let streams = memory();
// Do the real work.
let result = example(&app, &streams);
// Make sure we got the success result we were expecting.
assert!(result.is_ok());
// Make sure the expected output was written.
streams.output().seek(SeekFrom::Start(0)).unwrap();
assert_eq!(streams.output().to_string_lossy(), "Hello, world!\n");
}
}