fn parse(&self) -> Result<schema::Config, String> {
        // Parse the default shell
        let default_shell = match &self.default_shell {
            // If we're just given a shell string, use it as the generic shell
            Some(DefaultShell::Simple(generic)) => schema::DefaultShell {
                generic: generic.parse(),
                targets: HashMap::new(),
            },
            // If we have all the information we need, just transform it
            Some(DefaultShell::Complex { generic, targets }) => schema::DefaultShell {
                generic: generic.parse(),
                targets: match targets {
                    Some(raw_targets) => {
                        // This is just transformation logic
                        let mut targets = HashMap::new();
                        for (target_name, shell) in raw_targets.iter() {
                            targets.insert(target_name.to_string(), shell.parse());
                        }
                        targets
                    }
                    None => HashMap::new(), // We'll just use the generic if we don't have anything else
                },
            },
            // If no default shell is provided, we'll use the default paradigm (see `default_shells.rs`)
            None => get_default_shells(),
        };
        // Parse the scripts (brace yourself!)
        // We do this inside a function because it's recursive
        // Unfortunately we can't define methods on type aliases, so this goes here
        // This involves validation logic to ensure invalid property combinations aren't specified, so we need to know whether or not `order` is specified if this is parsing subcommands
        fn parse_scripts(
            raw_scripts: &Scripts,
            is_order_defined: bool,
        ) -> Result<schema::Scripts, String> {
            let mut scripts: schema::Scripts = HashMap::new();
            for (script_name, raw_command) in raw_scripts.iter() {
                let command = match raw_command {
                    Command::Simple(raw_command_wrapper) => schema::Command {
                        args: Vec::new(),
                        env_vars: Vec::new(),
                        subcommands: None,
                        order: None,
                        cmd: Some(raw_command_wrapper.parse()), // In the simple form, a command must be given (no subcommands can be specified)
                        description: None
                    },
                    Command::Complex {
                        args,
                        env_vars,
                        subcommands,
                        order,
                        cmd,
                        desc
                    } => schema::Command {
                        // If `order` is defined at the level above, we can't interpolate environment variables from here (has to be done at the level `order` was specified)
                        args: match is_order_defined {
                            // Unordered subcommands can't take arguments in any case of upper-level `order` definition
                            _ if matches!(subcommands, Some(_)) && matches!(order, None) && matches!(args, Some(_)) => return Err(format!("Error in parsing Bonnie configuration file: if `subcommands` is specified without `order`, `args` cannot be specified. This error occurred in in the '{}' script/subscript.", script_name)),
                            // If it was and `args` is specified, return an error
                            true if matches!(args, Some(_)) => return Err(format!("Error in parsing Bonnie configuration file: if `order` is specified, subscripts cannot specify `args`, as no environment variables can be provided to them. Environment variables to be interpolated in ordered subcommands must be set at the top-level. This error occurred in the '{}' script/subscript.", script_name)),
                            // If it was but args` isn't specified, it doesn't matter and we just give an empty vector instead
                            true => Vec::new(),
                            // If it wasn't, no validation needed
                            false => args.as_ref().unwrap_or(&Vec::new()).to_vec()
                        },
                        // This doesn't need any transformation, just a simple alternative if it's `None`
                        env_vars: env_vars.as_ref().unwrap_or(&Vec::new()).to_vec(),
                        // The subcommands are parsed recursively as scripts using this very function
                        // We parse through whether or not `order` is defined (has validation implications)
                        subcommands: match subcommands {
                            // We can't use `.map()` for this because we need support for `?`
                            Some(subcommands) => Some(
                                parse_scripts(subcommands, matches!(order, Some(_)))?
                            ),
                            None => None
                        },
                        // If `order` is defined at the level above and `subcommands` is defined here, `order` must be defined here too
                        order: match is_order_defined {
                            true if matches!(subcommands, Some(_)) => match order {
                                // If it was required and was given, no problem
                                Some(order) => Some(parse_directive_str(order)?),
                                // If it was required but not given, return an error
                                None => return Err(format!("Error in parsing Bonnie configuration file: if `order` is specified, all further nested subsubcommands must also specify `order`. This occurred in the '{}' script/subscript.", script_name))
                            }
                            // If it wasn't required, no validation needed
                            true | false => match order {
                                Some(order) => Some(parse_directive_str(order)?),
                                None => None
                            }
                        },
                        // If subcommands were specified, this is optional, otherwise we return an error
                        cmd: match cmd {
                            // It was given, but there are also ordered subcommands here, so execution will be ambiguous, return an error
                            Some(_) if matches!(order, Some(_)) => return Err(format!("Error in parsing Bonnie configuration file: both `cmd` and `order` were specified. This would lead to problems of ambiguous execution, so commands can have either the top-level `cmd` property or ordered subcommands, the two are mutually exclusive. This error occurred in in the '{}' script/subscript.", script_name)),
                            // It's optional
                            _ if matches!(subcommands, Some(_)) => cmd.as_ref().map(|cmd| cmd.parse()),
                            // It's mandatory and given
                            Some(cmd) => Some(cmd.parse()),
                            // It's mandatory and not given
                            None => return Err(format!("Error in parsing Bonnie configuration file: if `subcommands` is not specified, `cmd` is mandatory. This error occurred in in the '{}' script/subscript.", script_name))
                        },
                        description: desc.clone()
                    },
                };
                scripts.insert(script_name.to_string(), command);
            }

            Ok(scripts)
        }

        let scripts = parse_scripts(&self.scripts, false)?;

        Ok(schema::Config {
            default_shell,
            scripts,
            // Copy these last two in case the final config is cached and needs to be revalidated on load
            env_files: match &self.env_files {
                Some(env_files) => env_files.to_vec(),
                None => Vec::new(),
            },
            version: self.version.clone(),
        })
    }
// Parses the rest of the config into the final form, consuming `self`
// A very large portion of Bonnie's logic lives here or is called here (spec transformation)
Parses the rest of the config into the final form, consuming `self`
A very large portion of Bonnie's logic lives here or is called here (spec transformation)
Parses the rest of the config into the final form consuming ` self ` A very large portion of Bonnie ' s logic lives here or is called here ( spec transformation )
fn parse(&self) -> Result<schema::Config, String> {
        // Parse the default shell
        let default_shell = match &self.default_shell {
            // If we're just given a shell string, use it as the generic shell
            Some(DefaultShell::Simple(generic)) => schema::DefaultShell {
                generic: generic.parse(),
                targets: HashMap::new(),
            },
            // If we have all the information we need, just transform it
            Some(DefaultShell::Complex { generic, targets }) => schema::DefaultShell {
                generic: generic.parse(),
                targets: match targets {
                    Some(raw_targets) => {
                        // This is just transformation logic
                        let mut targets = HashMap::new();
                        for (target_name, shell) in raw_targets.iter() {
                            targets.insert(target_name.to_string(), shell.parse());
                        }
                        targets
                    }
                    None => HashMap::new(), // We'll just use the generic if we don't have anything else
                },
            },
            // If no default shell is provided, we'll use the default paradigm (see `default_shells.rs`)
            None => get_default_shells(),
        };
        // Parse the scripts (brace yourself!)
        // We do this inside a function because it's recursive
        // Unfortunately we can't define methods on type aliases, so this goes here
        // This involves validation logic to ensure invalid property combinations aren't specified, so we need to know whether or not `order` is specified if this is parsing subcommands
        fn parse_scripts(
            raw_scripts: &Scripts,
            is_order_defined: bool,
        ) -> Result<schema::Scripts, String> {
            let mut scripts: schema::Scripts = HashMap::new();
            for (script_name, raw_command) in raw_scripts.iter() {
                let command = match raw_command {
                    Command::Simple(raw_command_wrapper) => schema::Command {
                        args: Vec::new(),
                        env_vars: Vec::new(),
                        subcommands: None,
                        order: None,
                        cmd: Some(raw_command_wrapper.parse()), // In the simple form, a command must be given (no subcommands can be specified)
                        description: None
                    },
                    Command::Complex {
                        args,
                        env_vars,
                        subcommands,
                        order,
                        cmd,
                        desc
                    } => schema::Command {
                        // If `order` is defined at the level above, we can't interpolate environment variables from here (has to be done at the level `order` was specified)
                        args: match is_order_defined {
                            // Unordered subcommands can't take arguments in any case of upper-level `order` definition
                            _ if matches!(subcommands, Some(_)) && matches!(order, None) && matches!(args, Some(_)) => return Err(format!("Error in parsing Bonnie configuration file: if `subcommands` is specified without `order`, `args` cannot be specified. This error occurred in in the '{}' script/subscript.", script_name)),
                            // If it was and `args` is specified, return an error
                            true if matches!(args, Some(_)) => return Err(format!("Error in parsing Bonnie configuration file: if `order` is specified, subscripts cannot specify `args`, as no environment variables can be provided to them. Environment variables to be interpolated in ordered subcommands must be set at the top-level. This error occurred in the '{}' script/subscript.", script_name)),
                            // If it was but args` isn't specified, it doesn't matter and we just give an empty vector instead
                            true => Vec::new(),
                            // If it wasn't, no validation needed
                            false => args.as_ref().unwrap_or(&Vec::new()).to_vec()
                        },
                        // This doesn't need any transformation, just a simple alternative if it's `None`
                        env_vars: env_vars.as_ref().unwrap_or(&Vec::new()).to_vec(),
                        // The subcommands are parsed recursively as scripts using this very function
                        // We parse through whether or not `order` is defined (has validation implications)
                        subcommands: match subcommands {
                            // We can't use `.map()` for this because we need support for `?`
                            Some(subcommands) => Some(
                                parse_scripts(subcommands, matches!(order, Some(_)))?
                            ),
                            None => None
                        },
                        // If `order` is defined at the level above and `subcommands` is defined here, `order` must be defined here too
                        order: match is_order_defined {
                            true if matches!(subcommands, Some(_)) => match order {
                                // If it was required and was given, no problem
                                Some(order) => Some(parse_directive_str(order)?),
                                // If it was required but not given, return an error
                                None => return Err(format!("Error in parsing Bonnie configuration file: if `order` is specified, all further nested subsubcommands must also specify `order`. This occurred in the '{}' script/subscript.", script_name))
                            }
                            // If it wasn't required, no validation needed
                            true | false => match order {
                                Some(order) => Some(parse_directive_str(order)?),
                                None => None
                            }
                        },
                        // If subcommands were specified, this is optional, otherwise we return an error
                        cmd: match cmd {
                            // It was given, but there are also ordered subcommands here, so execution will be ambiguous, return an error
                            Some(_) if matches!(order, Some(_)) => return Err(format!("Error in parsing Bonnie configuration file: both `cmd` and `order` were specified. This would lead to problems of ambiguous execution, so commands can have either the top-level `cmd` property or ordered subcommands, the two are mutually exclusive. This error occurred in in the '{}' script/subscript.", script_name)),
                            // It's optional
                            _ if matches!(subcommands, Some(_)) => cmd.as_ref().map(|cmd| cmd.parse()),
                            // It's mandatory and given
                            Some(cmd) => Some(cmd.parse()),
                            // It's mandatory and not given
                            None => return Err(format!("Error in parsing Bonnie configuration file: if `subcommands` is not specified, `cmd` is mandatory. This error occurred in in the '{}' script/subscript.", script_name))
                        },
                        description: desc.clone()
                    },
                };
                scripts.insert(script_name.to_string(), command);
            }
            Ok(scripts)
        }
        let scripts = parse_scripts(&self.scripts, false)?;
        Ok(schema::Config {
            default_shell,
            scripts,
            // Copy these last two in case the final config is cached and needs to be revalidated on load
            env_files: match &self.env_files {
                Some(env_files) => env_files.to_vec(),
                None => Vec::new(),
            },
            version: self.version.clone(),
        })
    }
fn parse ( & self ) -> Result < schema :: Config , String > { // Parse the default shell let default_shell = match & self . default_shell { // If we're just given a shell string, use it as the generic shell Some ( DefaultShell :: Simple ( generic ) ) => schema :: DefaultShell { generic : generic . parse ( ) , targets : HashMap :: new ( ) , } , // If we have all the information we need, just transform it Some ( DefaultShell :: Complex { generic , targets } ) => schema :: DefaultShell { generic : generic . parse ( ) , targets : match targets { Some ( raw_targets ) => { // This is just transformation logic let mut targets = HashMap :: new ( ) ; for ( target_name , shell ) in raw_targets . iter ( ) { targets . insert ( target_name . to_string ( ) , shell . parse ( ) ) ; } targets } None => HashMap :: new ( ) , // We'll just use the generic if we don't have anything else } , } , // If no default shell is provided, we'll use the default paradigm (see `default_shells.rs`) None => get_default_shells ( ) , } ; // Parse the scripts (brace yourself!) // We do this inside a function because it's recursive // Unfortunately we can't define methods on type aliases, so this goes here // This involves validation logic to ensure invalid property combinations aren't specified, so we need to know whether or not `order` is specified if this is parsing subcommands fn parse_scripts ( raw_scripts : & Scripts , is_order_defined : bool , ) -> Result < schema :: Scripts , String > { let mut scripts : schema :: Scripts = HashMap :: new ( ) ; for ( script_name , raw_command ) in raw_scripts . iter ( ) { let command = match raw_command { Command :: Simple ( raw_command_wrapper ) => schema :: Command { args : Vec :: new ( ) , env_vars : Vec :: new ( ) , subcommands : None , order : None , cmd : Some ( raw_command_wrapper . parse ( ) ) , // In the simple form, a command must be given (no subcommands can be specified) description : None } , Command :: Complex { args , env_vars , subcommands , order , cmd , desc } => schema :: Command { // If `order` is defined at the level above, we can't interpolate environment variables from here (has to be done at the level `order` was specified) args : match is_order_defined { // Unordered subcommands can't take arguments in any case of upper-level `order` definition _ if matches ! ( subcommands Some ( ) ) && matches ! ( order None ) && matches ! ( args Some ( ) ) => return Err ( format ! ( " " script_name ) ) , // If it was and `args` is specified, return an error true if matches ! ( args Some ( ) ) => return Err ( format ! ( " " script_name ) ) , // If it was but args` isn't specified, it doesn't matter and we just give an empty vector instead true => Vec :: new ( ) , // If it wasn't, no validation needed false => args . as_ref ( ) . unwrap_or ( & Vec :: new ( ) ) . to_vec ( ) } , // This doesn't need any transformation, just a simple alternative if it's `None` env_vars : env_vars . as_ref ( ) . unwrap_or ( & Vec :: new ( ) ) . to_vec ( ) , // The subcommands are parsed recursively as scripts using this very function // We parse through whether or not `order` is defined (has validation implications) subcommands : match subcommands { // We can't use `.map()` for this because we need support for `?` Some ( subcommands ) => Some ( parse_scripts ( subcommands , matches ! ( order Some ( ) ) ) ? ) , None => None } , // If `order` is defined at the level above and `subcommands` is defined here, `order` must be defined here too order : match is_order_defined { true if matches ! ( subcommands Some ( ) ) => match order { // If it was required and was given, no problem Some ( order ) => Some ( parse_directive_str ( order ) ? ) , // If it was required but not given, return an error None => return Err ( format ! ( " " script_name ) ) } // If it wasn't required, no validation needed true | false => match order { Some ( order ) => Some ( parse_directive_str ( order ) ? ) , None => None } } , // If subcommands were specified, this is optional, otherwise we return an error cmd : match cmd { // It was given, but there are also ordered subcommands here, so execution will be ambiguous, return an error Some ( _ ) if matches ! ( order Some ( ) ) => return Err ( format ! ( " " script_name ) ) , // It's optional _ if matches ! ( subcommands Some ( ) ) => cmd . as_ref ( ) . map ( | cmd | cmd . parse ( ) ) , // It's mandatory and given Some ( cmd ) => Some ( cmd . parse ( ) ) , // It's mandatory and not given None => return Err ( format ! ( " " script_name ) ) } , description : desc . clone ( ) } , } ; scripts . insert ( script_name . to_string ( ) , command ) ; } Ok ( scripts ) } let scripts = parse_scripts ( & self . scripts , false ) ? ; Ok ( schema :: Config { default_shell , scripts , // Copy these last two in case the final config is cached and needs to be revalidated on load env_files : match & self . env_files { Some ( env_files ) => env_files . to_vec ( ) , None => Vec :: new ( ) , } , version : self . version . clone ( ) , } ) }
Parses the rest of the config into the final form, consuming `self`
A very large portion of Bonnie's logic lives here or is called here (spec transformation)
Parses the rest of the config into the final form consuming ` self ` A very large portion of Bonnie ' s logic lives here or is called here ( spec transformation )
&self