Skip to content

Commit 37bd5d0

Browse files
committed
feat(options): replace options parser with jaarg
1 parent cc4bc6e commit 37bd5d0

File tree

5 files changed

+92
-230
lines changed

5 files changed

+92
-230
lines changed

Cargo.lock

Lines changed: 6 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: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ default-features = false
1919
features = ["png"]
2020
optional = true
2121

22+
[dependencies.jaarg]
23+
git = "https://github.com/gay-pizza/jaarg.git"
24+
rev = "6b4d6d20efd7596121c1a253f276634233b2b53f"
25+
2226
[dependencies.serde]
2327
version = "1.0.228"
2428
features = ["derive"]

src/main.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ use crate::context::{RootContext, SproutContext};
99
use crate::entries::BootableEntry;
1010
use crate::integrations::bootloader_interface::{BootloaderInterface, BootloaderInterfaceTimeout};
1111
use crate::options::SproutOptions;
12-
use crate::options::parser::OptionsRepresentable;
1312
use crate::phases::phase;
1413
use crate::platform::timer::PlatformTimer;
1514
use crate::platform::tpm::PlatformTpm;

src/options.rs

Lines changed: 82 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
use crate::options::parser::{OptionDescription, OptionForm, OptionsRepresentable};
21
use anyhow::{Context, Result, bail};
3-
use std::collections::BTreeMap;
4-
5-
/// The Sprout options parser.
6-
pub mod parser;
2+
use jaarg::std::ParseMapResult;
3+
use jaarg::{
4+
HelpWriter, HelpWriterContext, Opt, Opts, ParseError, StandardFullHelpWriter,
5+
StandardShortUsageWriter,
6+
};
7+
use log::info;
8+
use std::process::ExitCode;
79

810
/// Default configuration file path.
911
const DEFAULT_CONFIG_PATH: &str = "\\sprout.toml";
@@ -36,79 +38,76 @@ impl Default for SproutOptions {
3638
}
3739
}
3840

39-
/// The options parser mechanism for Sprout.
40-
impl OptionsRepresentable for SproutOptions {
41-
/// Produce the [SproutOptions] structure.
42-
type Output = Self;
43-
44-
/// All the Sprout options that are defined.
45-
fn options() -> &'static [(&'static str, OptionDescription<'static>)] {
46-
&[
47-
(
48-
"autoconfigure",
49-
OptionDescription {
50-
description: "Enable Sprout Autoconfiguration",
51-
form: OptionForm::Flag,
52-
},
53-
),
54-
(
55-
"config",
56-
OptionDescription {
57-
description: "Path to Sprout configuration file",
58-
form: OptionForm::Value,
59-
},
60-
),
61-
(
62-
"boot",
63-
OptionDescription {
64-
description: "Entry to boot, bypassing the menu",
65-
form: OptionForm::Value,
66-
},
67-
),
68-
(
69-
"force-menu",
70-
OptionDescription {
71-
description: "Force showing of the boot menu",
72-
form: OptionForm::Flag,
73-
},
74-
),
75-
(
76-
"menu-timeout",
77-
OptionDescription {
78-
description: "Boot menu timeout, in seconds",
79-
form: OptionForm::Value,
80-
},
81-
),
82-
(
83-
"help",
84-
OptionDescription {
85-
description: "Display Sprout Help",
86-
form: OptionForm::Help,
87-
},
88-
),
89-
]
90-
}
41+
/// Sprout options parser.
42+
impl SproutOptions {
43+
/// Parse options with jaarg.
44+
pub fn parse() -> Result<Self> {
45+
const OPTIONS: Opts<&str> = Opts::new(&[
46+
Opt::help_flag("help", &["--help"]).help_text("Display Sprout Help"),
47+
Opt::flag("autoconfigure", &["--autoconfigure"])
48+
.help_text("Enable Sprout autoconfiguration"),
49+
Opt::value("config", &["--config"], "PATH")
50+
.help_text("Path to Sprout configuration file"),
51+
Opt::value("boot", &["--boot"], "ENTRY").help_text("Entry to boot, bypassing the menu"),
52+
Opt::flag("force-menu", &["--force-menu"]).help_text("Force showing the boot menu"),
53+
Opt::value("menu-timeout", &["--menu-timeout"], "TIMEOUT")
54+
.help_text("Boot menu timeout, in seconds"),
55+
]);
9156

92-
/// Produces [SproutOptions] from the parsed raw `options` map.
93-
fn produce(options: BTreeMap<String, Option<String>>) -> Result<Self> {
94-
// Use the default value of sprout options and have the raw options be parsed into it.
95-
let mut result = Self::default();
57+
// Collect all the arguments to Sprout.
58+
// Skip the first argument, which is the path to our executable.
59+
let mut args = std::env::args().skip(1).collect::<Vec<_>>();
60+
61+
// Correct firmware that may add invalid arguments at the start.
62+
// Witnessed this on a Dell Precision 5690 when direct booting.
63+
loop {
64+
// Grab the first argument or break.
65+
let Some(arg) = args.first() else {
66+
break;
67+
};
68+
69+
// If the argument starts with a tilde, remove it.
70+
if arg.starts_with("`") {
71+
args.remove(0);
72+
continue;
73+
}
74+
break;
75+
}
76+
77+
// Parse the OPTIONS into a map using jaarg.
78+
let parsed = match OPTIONS.parse_map(
79+
"sprout",
80+
args.iter(),
81+
|name| Self::help(&OPTIONS, name),
82+
|name, e| Self::error(&OPTIONS, name, e),
83+
) {
84+
ParseMapResult::Map(map) => map,
85+
ParseMapResult::Exit(ExitCode::SUCCESS) => {
86+
std::process::exit(0);
87+
}
88+
89+
ParseMapResult::Exit(ExitCode::FAILURE) => {
90+
std::process::exit(1);
91+
}
92+
};
9693

97-
for (key, value) in options {
98-
match key.as_str() {
94+
// Use the default value of sprout OPTIONS and have the raw OPTIONS be parsed into it.
95+
let mut result = Self::default();
96+
for (key, value) in parsed {
97+
match key {
9998
"autoconfigure" => {
10099
// Enable autoconfiguration.
101100
result.autoconfigure = true;
102101
}
103102

104103
"config" => {
105104
// The configuration file to load.
106-
result.config = value.context("--config option requires a value")?;
105+
result.config = value;
107106
}
108107

109108
"boot" => {
110109
// The entry to boot.
111-
result.boot = Some(value.context("--boot option requires a value")?);
110+
result.boot = Some(value);
112111
}
113112

114113
"force-menu" => {
@@ -118,7 +117,6 @@ impl OptionsRepresentable for SproutOptions {
118117

119118
"menu-timeout" => {
120119
// The timeout for the boot menu in seconds.
121-
let value = value.context("--menu-timeout option requires a value")?;
122120
let value = value
123121
.parse::<u64>()
124122
.context("menu-timeout must be a number")?;
@@ -130,4 +128,22 @@ impl OptionsRepresentable for SproutOptions {
130128
}
131129
Ok(result)
132130
}
131+
132+
fn help<'a>(options: &'a Opts<&'static str>, program_name: &'a str) {
133+
let ctx = HelpWriterContext {
134+
options,
135+
program_name,
136+
};
137+
info!("{}", StandardFullHelpWriter::new(ctx));
138+
}
139+
140+
fn error(options: &Opts<&'static str>, program_name: &str, err: ParseError) {
141+
info!("{program_name}: {err}");
142+
let ctx = HelpWriterContext {
143+
options,
144+
program_name,
145+
};
146+
info!("{}", StandardShortUsageWriter::new(ctx));
147+
info!("Run 'sprout --help' to view all available options.");
148+
}
133149
}

src/options/parser.rs

Lines changed: 0 additions & 163 deletions
This file was deleted.

0 commit comments

Comments
 (0)