1- use crate :: options:: parser:: { OptionDescription , OptionForm , OptionsRepresentable } ;
21use 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.
911const 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}
0 commit comments