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
use std::borrow::Cow;
pub trait ExitStatus {
fn to_i32(&self) -> i32;
}
impl ExitStatus for u8 {
fn to_i32(&self) -> i32 {
i32::from(*self)
}
}
impl ExitStatus for i32 {
fn to_i32(&self) -> i32 {
*self
}
}
impl ExitStatus for () {
fn to_i32(&self) -> i32 {
0
}
}
pub fn entry_point<
Args: FromIterator<String>,
Env: FromIterator<(String, String)>,
Return: ExitStatus,
>(
main_package_name: &str,
main: fn(Args, Env) -> Result<Return, eyre::Report>,
) -> Result<(), eyre::Report> {
dotenv::dotenv().ok();
if std::env::var("RUST_LOG").unwrap_or_default().is_empty() {
if cfg!(debug_assertions) {
std::env::set_var(
"RUST_LOG",
format!("warn,{main_package_name}=debug,ez=debug"),
);
} else {
std::env::set_var("RUST_LOG", format!("warn,{main_package_name}=info,ez=info"));
}
}
if std::env::var("RUST_SPANTRACE")
.unwrap_or_default()
.is_empty()
{
std::env::set_var("RUST_SPANTRACE", "1");
}
color_eyre::install().unwrap();
tracing_subscriber::util::SubscriberInitExt::init(tracing_subscriber::Layer::with_subscriber(
tracing_error::ErrorLayer::default(),
tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.with_target(true)
.with_span_events(
tracing_subscriber::fmt::format::FmtSpan::NEW
| tracing_subscriber::fmt::format::FmtSpan::CLOSE,
)
.finish(),
));
let args = std::env::args_os()
.skip(1)
.map(|s| match s.to_string_lossy() {
Cow::Borrowed(lossless) => lossless.to_owned(),
Cow::Owned(lossy) => {
tracing::warn!(
target: "ez",
"Invalid UTF-8 in command-line argument. Invalid sequences have been replaced \
with '�':\n {lossy:?}"
);
lossy
},
});
let env = std::env::vars_os().filter_map(|(name, value)| {
let name = name
.to_str()
.or_else(|| {
let lossy = name.to_string_lossy();
tracing::warn!(
target: "ez",
"Invalid UTF-8 in an environment variable name ({lossy:?}). It has been \
skipped."
);
None
})?
.to_owned();
let value = value
.to_str()
.or_else(|| {
tracing::warn!(
target: "ez",
"Invalid UTF-8 in the value of the environment variable {name:?}. It has been \
skipped."
);
None
})?
.to_owned();
Some((name, value))
});
let exit_status = main(args.collect(), env.collect()).map_err(|err| {
tracing::error!(target: "ez", "exiting with error status code due to an unhandled error");
err
})?.to_i32();
if exit_status != 0 {
tracing::debug!(target: "ez", "exiting with error status code {}", exit_status);
std::process::exit(exit_status);
}
Ok(())
}