Skip to content

Commit b15f328

Browse files
author
Alexander Weber
committed
regression test and error overhaul
1 parent 730a7e8 commit b15f328

10 files changed

Lines changed: 123 additions & 64 deletions

File tree

.neomake.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
version: 0.3
2+
3+
chains:
4+
"test:regression":
5+
env:
6+
config: "./test/.complate/config.yaml"
7+
tasks:
8+
- script: |
9+
set -e
10+
export RUST_BACKTRACE=1
11+
printf $config
12+
bravo="bravo" cargo run -- render -c $config -t static
13+
bravo="bravo" cargo run -- -e render -c $config -t override -v c.charlie="charlie"

Cargo.lock

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,4 @@ serde_yaml = "^0.8"
3838
cursive = { version = "^0.16", optional = true }
3939
dialoguer = { version = "^0.8", optional = true }
4040
fui = { version = "^2.0", optional = true }
41+
thiserror = "1.0.38"

src/args/mod.rs

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ use std::{collections::HashMap, result::Result, str::FromStr};
22

33
use clap::{Arg, ArgAction};
44

5+
use crate::error::Error;
6+
57
#[derive(Debug)]
68
pub struct CallArgs {
79
pub privileges: Privilege,
@@ -14,9 +16,7 @@ impl CallArgs {
1416
match &self.command {
1517
| Command::Render(args) => {
1618
if args.helpers && args.shell_trust != ShellTrust::Ultimate {
17-
return Err(Box::new(crate::error::NoShellTrust::new(
18-
"need ultimate shell trust for helper functions",
19-
)));
19+
return Err(Box::new(Error::NoTrust));
2020
}
2121
},
2222
| _ => {},
@@ -27,21 +27,15 @@ impl CallArgs {
2727
| Command::Render(args) => {
2828
match args.backend {
2929
#[cfg(feature = "backend+ui")]
30-
| Backend::UI => {
31-
return Err(Box::new(crate::error::NeedExperimentalFlag::new(
32-
"to enable UI backend",
33-
)))
34-
},
30+
| Backend::UI => return Err(Box::new(Error::ExperimentalCommand)),
3531
#[cfg(feature = "backend+cli")]
3632
| Backend::CLI => {},
3733
};
3834
if args.value_overrides.len() > 0 {
39-
return Err(Box::new(crate::error::NeedExperimentalFlag::new(
40-
"to enable value overrides",
41-
)));
35+
return Err(Box::new(Error::ExperimentalCommand));
4236
}
4337
if args.helpers {
44-
return Err(Box::new(crate::error::NeedExperimentalFlag::new("to enable helpers")));
38+
return Err(Box::new(Error::ExperimentalCommand));
4539
}
4640
#[allow(unreachable_code)]
4741
Ok(())
@@ -222,7 +216,7 @@ impl ClapArgumentLoader {
222216
| "cli" => Backend::CLI,
223217
#[cfg(feature = "backend+ui")]
224218
| "ui" => Backend::UI,
225-
| _ => return Err(Box::new(crate::error::Failed::new("no backend specified"))),
219+
| _ => return Err(Box::new(Error::Argument("no backend specified".into()))),
226220
};
227221
let helpers = subc.get_flag("helpers");
228222

@@ -239,7 +233,7 @@ impl ClapArgumentLoader {
239233
}),
240234
})
241235
} else {
242-
return Err(Box::new(crate::error::Failed::new("unknown subcommand")));
236+
return Err(Box::new(Error::UnknownCommand));
243237
}
244238
}
245239
}

src/error.rs

Lines changed: 23 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,25 @@
1-
macro_rules! make_error {
2-
($name:ident) => {
3-
#[derive(Debug, Clone)]
4-
/// An error type.
5-
pub struct $name {
6-
details: String,
7-
}
1+
use thiserror::Error;
82

9-
impl $name {
10-
pub fn default() -> Self {
11-
Self::new("")
12-
}
13-
14-
/// Error type constructor.
15-
pub fn new(details: &str) -> Self {
16-
Self {
17-
details: details.to_owned(),
18-
}
19-
}
20-
}
21-
22-
impl std::fmt::Display for $name {
23-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24-
f.write_str(&self.details)
25-
}
26-
}
27-
28-
impl std::error::Error for $name {}
29-
};
3+
#[derive(Debug, Error)]
4+
pub(crate) enum Error {
5+
#[error("std")]
6+
Std(#[from] Box<dyn std::error::Error + Sync + Send>),
7+
#[error("generic")]
8+
Generic(String),
9+
// #[error("many")]
10+
// Many(Vec<Self>),
11+
#[error("argument")]
12+
Argument(String),
13+
#[error("experimental command")]
14+
ExperimentalCommand,
15+
#[error("unknown command")]
16+
UnknownCommand,
17+
#[error("no trust")]
18+
NoTrust,
19+
#[error("shell command")]
20+
ShellCommand(String),
21+
#[error("user interact abort")]
22+
InteractAbort,
23+
#[error("helper")]
24+
Helper(String),
3025
}
31-
32-
make_error!(Failed);
33-
make_error!(NeedExperimentalFlag);
34-
make_error!(UnknownCommand);
35-
make_error!(UserAbort);
36-
make_error!(NoShellTrust);

src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
1818
match cmd.command {
1919
| crate::args::Command::Man(path) => {
2020
let out_path = PathBuf::from(path);
21-
std::fs::create_dir_all(&out_path).unwrap();
21+
std::fs::create_dir_all(&out_path)?;
2222
reference::build_manpages(&out_path)?;
2323
Ok(())
2424
},
2525
| crate::args::Command::Autocomplete(path, shell) => {
2626
let out_path = PathBuf::from(path);
27-
std::fs::create_dir_all(&out_path).unwrap();
27+
std::fs::create_dir_all(&out_path)?;
2828
reference::build_shell_completion(&out_path, &shell)?;
2929
Ok(())
3030
},

src/render/cli.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use crate::error::Error;
2+
13
use super::UserInput;
24
use async_trait::async_trait;
35
use std::{collections::HashMap, result::Result};
@@ -17,7 +19,7 @@ impl<'a> UserInput for CLIBackend<'a> {
1719
async fn prompt(&self, text: &str) -> Result<String, Box<dyn std::error::Error>> {
1820
match dialoguer::Input::new().allow_empty(true).with_prompt(text).interact() {
1921
| Ok(res) => Ok(res),
20-
| Err(_) => Err(Box::new(crate::error::Failed::default())),
22+
| Err(_) => Err(Box::new(Error::InteractAbort)),
2123
}
2224
}
2325

@@ -53,8 +55,7 @@ impl<'a> UserInput for CLIBackend<'a> {
5355
let indices = dialoguer::MultiSelect::new()
5456
.with_prompt(prompt)
5557
.items(&display_vals)
56-
.interact()
57-
.unwrap();
58+
.interact()?;
5859

5960
match indices.len() {
6061
| 0usize => Ok("".to_owned()),

src/render/mod.rs

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::args::{Backend, ShellTrust};
22
use crate::config::{Config, Content, Option, OptionValue, Template, VariableDefinition};
3+
use crate::error::Error;
34
use async_trait::async_trait;
45
use std::collections::{BTreeMap, HashMap};
56
use std::env;
@@ -58,14 +59,24 @@ pub async fn render(
5859
_: &mut handlebars::RenderContext,
5960
out: &mut dyn handlebars::Output|
6061
-> handlebars::HelperResult {
61-
let param = h.param(0).unwrap();
62+
let param = h
63+
.param(0)
64+
.ok_or_else(|| Error::Helper("no parameter to helper function found".into()))
65+
.unwrap();
6266
// dbg!(param);
6367
let cmd = helper.1.shell.to_owned();
6468

6569
let output = std::process::Command::new("sh")
6670
.arg("-c")
6771
.arg(cmd)
68-
.env("VALUE", param.value().as_str().unwrap())
72+
.env(
73+
"VALUE",
74+
param
75+
.value()
76+
.as_str()
77+
.ok_or_else(|| Error::Helper("parameter is not a string".into()))
78+
.unwrap(),
79+
)
6980
.output()?;
7081
if output.status.code().unwrap() != 0 {
7182
return Err(handlebars::RenderError::new("failed to get command status"));
@@ -78,8 +89,7 @@ pub async fn render(
7889
}
7990
}
8091

81-
let rendered_template = hb.render_template(template, &values_json).unwrap();
82-
Ok(rendered_template)
92+
Ok(hb.render_template(template, &values_json)?)
8393
}
8494

8595
#[allow(clippy::needless_lifetimes)]
@@ -105,7 +115,7 @@ pub async fn select_template<'a>(
105115

106116
match config.templates.get(&selection) {
107117
| Some(x) => Ok(x),
108-
| None => Err(Box::new(crate::error::Failed::default())),
118+
| None => Err(Box::new(Error::Generic("invalid template selection".into()))),
109119
}
110120
}
111121

@@ -132,7 +142,10 @@ pub async fn select_and_render(
132142
let cfg: Config = serde_yaml::from_str(&invoke_options.configuration).unwrap();
133143

134144
let template = match &invoke_options.template {
135-
| Some(x) => cfg.templates.get(x).unwrap(),
145+
| Some(x) => cfg
146+
.templates
147+
.get(x)
148+
.ok_or_else(|| Error::Generic("template not found".into()))?,
136149
| None => select_template(&cfg, &invoke_options.backend, &invoke_options.shell_trust).await?,
137150
};
138151
let template_str = match &template.content {
@@ -210,7 +223,7 @@ async fn shell(
210223
shell_trust: &ShellTrust,
211224
) -> Result<String, Box<dyn std::error::Error>> {
212225
match shell_trust {
213-
| ShellTrust::None => return Err(Box::new(crate::error::NoShellTrust::default())),
226+
| ShellTrust::None => return Err(Box::new(Error::NoTrust)),
214227
| ShellTrust::Ultimate => {},
215228
}
216229

@@ -220,7 +233,7 @@ async fn shell(
220233
.envs(env)
221234
.output()?;
222235
if output.status.code().unwrap() != 0 {
223-
return Err(Box::new(crate::error::Failed::default()));
236+
return Err(Box::new(Error::ShellCommand(command.into())));
224237
}
225-
Ok(String::from_utf8(output.stdout).unwrap())
238+
Ok(String::from_utf8(output.stdout)?)
226239
}

src/render/ui.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use crate::error::Error;
2+
13
use super::UserInput;
24
use async_trait::async_trait;
35
use cursive::traits::*;
@@ -36,7 +38,7 @@ impl<'a> UserInput for UIBackend<'a> {
3638

3739
match v.take() {
3840
| Some(x) => Ok(x),
39-
| None => Err(Box::new(crate::error::UserAbort::default())),
41+
| None => Err(Box::new(Error::InteractAbort)),
4042
}
4143
}
4244

@@ -136,7 +138,7 @@ impl<'a> UserInput for UIBackend<'a> {
136138
siv.run();
137139

138140
if !ok_pressed.take() {
139-
return Err(Box::new(crate::error::UserAbort::default()));
141+
return Err(Box::new(Error::InteractAbort));
140142
}
141143
for x in items.try_read().unwrap().iter() {
142144
let pos = display_vals.iter().position(|v| x == v).unwrap();

test/.complate/config.yaml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
version: 0.12
2+
templates:
3+
static:
4+
content:
5+
inline: |-
6+
{{ a.alpha }}
7+
{{ b.bravo }}
8+
values:
9+
a.alpha:
10+
static: "alpha"
11+
b.bravo:
12+
env: "bravo"
13+
override:
14+
content:
15+
inline: |-
16+
{{ a.alpha }}
17+
{{ b.bravo }}
18+
{{ c.charlie }}
19+
values:
20+
a.alpha:
21+
static: "alpha"
22+
b.bravo:
23+
env: "bravo"
24+
c.charlie:
25+
env: "DOES_NOT_EXIST"

0 commit comments

Comments
 (0)