Skip to content

Commit

Permalink
fix: 🐛 fixed vector ordering in schema
Browse files Browse the repository at this point in the history
  • Loading branch information
arctic-hen7 committed Jul 6, 2021
1 parent 910755d commit 200b3db
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 55 deletions.
35 changes: 18 additions & 17 deletions spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ This syntax specifies the actual form users will write in Bonnie configuration f
```rust
#[derive(Debug, Clone, Deserialize)]
pub struct Config {
version: String, // This will be used to confirm compatibility
version: String, // This will be used to confirm compatibility
env_files: Option<Vec<String>>, // Files specified here have their environment variables loaded into Bonnie
default_shell: Option<DefaultShell>,
scripts: Scripts,
Expand All @@ -22,8 +22,8 @@ enum DefaultShell {
Simple(Shell), // Just a generic shell
Complex {
generic: Shell, // A generic shell must be given
targets: Option<HashMap<String, Shell>>
}
targets: Option<HashMap<String, Shell>>,
},
}
type Shell = Vec<String>; // A vector of the executable followed by raw arguments thereto, the location for command interpolation is specified with '{COMMAND}'
type TargetString = String; // A target like `linux` or `x86_64-unknown-linux-musl` (see `rustup` targets)
Expand All @@ -32,40 +32,41 @@ type Scripts = HashMap<String, Command>;
#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
enum Command {
Simple(CommandBox), // Might be just a string command to run on the default generic shell
Simple(CommandWrapper), // Might be just a string command to run on the default generic shell
Complex {
args: Option<Vec<String>>,
env_vars: Option<Vec<String>>,
subcommands: Option<Scripts>, // Subcommands are fully-fledged commands (mostly)
order: Option<OrderString>, // If this is specified,subcomands must not specify the `args` property, it may be specified at the top-level of this script as a sibling of `order`
cmd: Option<CommandBox>, // This is optional if subcommands are specified
cmd: Option<CommandWrapper>, // This is optional if subcommands are specified
},
}
type OrderString = String; // A string of as yet undefined syntax that defines the progression between subcommands
// This wraps the complexities of having different shell logic for each command in a multi-stage context
// subcommands are specified above this level (see `Command::Complex`)
#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
enum CommandBox {
Simple(CommandWrapper),
MultiStage(Vec<CommandWrapper>),
}
// This wraps the complexities of having different shell logic for each command in a multi-stage context
// subcommands are specified above this level (see `Command::Complex`)
#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
enum CommandWrapper {
Universal(CommandCore), // Just a given command
Specific {
generic: CommandCore,
targets: Option<HashMap<TargetString, CommandCore>>
targets: Option<HashMap<TargetString, CommandCore>>,
},
}
#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
enum CommandCore {
Simple(String), // No shell configuration
Simple(CommandBox), // No shell configuration
WithShell {
exec: String, // We can't call this `cmd` because otherwise we'd have a collision with the higher-level `cmd`, which leads to misinterpretation
shell: Option<Shell>
exec: CommandBox, // We can't call this `cmd` because otherwise we'd have a collision with the higher-level `cmd`, which leads to misinterpretation
shell: Option<Shell>,
},
}
// This represents the possibility of a vector or string at the lowest level
#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
enum CommandBox {
Simple(String),
MultiStage(Vec<String>),
}
```
57 changes: 29 additions & 28 deletions src/raw_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,12 +138,12 @@ impl Config {
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_box) => schema::Command {
Command::Simple(raw_command_wrapper) => schema::Command {
args: Vec::new(),
env_vars: Vec::new(),
subcommands: None,
order: None,
cmd: Some(raw_command_box.parse()) // In the simple form, a command must be given (no subcommands can be specified)
cmd: Some(raw_command_wrapper.parse()) // In the simple form, a command must be given (no subcommands can be specified)
},
Command::Complex {
args,
Expand Down Expand Up @@ -231,39 +231,20 @@ type Scripts = HashMap<String, Command>;
#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
enum Command {
Simple(CommandBox), // Might be just a string command to run on the default generic shell
Simple(CommandWrapper), // Might be just a string command to run on the default generic shell
Complex {
args: Option<Vec<String>>,
env_vars: Option<Vec<String>>,
subcommands: Option<Scripts>, // Subcommands are fully-fledged commands (mostly)
order: Option<OrderString>, // If this is specified,subcomands must not specify the `args` property, it may be specified at the top-level of this script as a sibling of `order`
cmd: Option<CommandBox>, // This is optional if subcommands are specified
cmd: Option<CommandWrapper>, // This is optional if subcommands are specified
},
}
type OrderString = String; // A string of as yet undefined syntax that defines the progression between subcommands
// This wraps the complexities of having different shell logic for each command in a multi-stage context
// subcommands are specified above this level (see `Command::Complex`)
#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
enum CommandBox {
Simple(CommandWrapper),
MultiStage(Vec<CommandWrapper>),
}
impl CommandBox {
// Parses `self` into its final form (`Vec<schema::CommandWrapper>`)
fn parse(&self) -> Vec<schema::CommandWrapper> {
match self {
// In fully parsed form, all command wrappers are inside vectors for simplicity
CommandBox::Simple(raw_command_wrapper) => vec![raw_command_wrapper.parse()],
CommandBox::MultiStage(raw_command_wrappers) => raw_command_wrappers
.iter()
.map(|raw_command_wrapper| raw_command_wrapper.parse())
.collect(),
}
}
}
#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
enum CommandWrapper {
Universal(CommandCore), // Just a given command
Specific {
Expand Down Expand Up @@ -309,9 +290,9 @@ impl CommandWrapper {
#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
enum CommandCore {
Simple(String), // No shell configuration
Simple(CommandBox), // No shell configuration
WithShell {
exec: String, // We can't call this `cmd` because otherwise we'd have a collision with the higher-level `cmd`, which leads to misinterpretation
exec: CommandBox, // We can't call this `cmd` because otherwise we'd have a collision with the higher-level `cmd`, which leads to misinterpretation
shell: Option<Shell>,
},
}
Expand All @@ -320,21 +301,41 @@ impl CommandCore {
fn parse(&self) -> schema::CommandCore {
match self {
CommandCore::Simple(exec) => schema::CommandCore {
exec: exec.to_string(),
exec: exec.parse(),
shell: None,
},
CommandCore::WithShell {
exec,
shell: Some(shell),
} => schema::CommandCore {
exec: exec.to_string(),
exec: exec.parse(),
shell: Some(shell.to_vec()),
},
// If no shell was given in the complex form, the expansion is the same as the simple form
CommandCore::WithShell { exec, shell: None } => schema::CommandCore {
exec: exec.to_string(),
exec: exec.parse(),
shell: None,
},
}
}
}
// This represents the possibility of a vector or string at the lowest level
#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
enum CommandBox {
Simple(String),
MultiStage(Vec<String>),
}
impl CommandBox {
// Parses `self` into its final form (`Vec<schema::CommandWrapper>`)
fn parse(&self) -> Vec<String> {
match self {
// In fully parsed form, all command wrappers are inside vectors for simplicity
CommandBox::Simple(cmd_str) => vec![cmd_str.to_string()],
CommandBox::MultiStage(cmd_strs) => cmd_strs
.iter()
.map(|cmd_str| cmd_str.to_string())
.collect(),
}
}
}
20 changes: 10 additions & 10 deletions src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ pub struct Command {
pub env_vars: Vec<String>,
pub subcommands: Option<Scripts>, // Subcommands are fully-fledged commands (mostly)
pub order: Option<BonesDirective>, // If this is specified, subcomands must not specify the `args` property, it may be specified at the top-level of this script as a sibling of `order`
pub cmd: Option<Vec<CommandWrapper>>, // If subcommands are provided, a root command is optional
pub cmd: Option<CommandWrapper>, // If subcommands are provided, a root command is optional
}
impl Command {
// Prepares a command by interpolating everything and resolving shell/tagret logic
Expand Down Expand Up @@ -148,11 +148,11 @@ impl Command {
{
// We have either a direct command or a parent command that has irrelevant subcommands, either way we're interpolating into `cmd`
// Get the vector of command wrappers
let command_wrapper_vec = self.cmd.as_ref().unwrap(); // Assuming the transformation logic works, an error can't occur here
let command_wrapper = self.cmd.as_ref().unwrap(); // Assuming the transformation logic works, an error can't occur here
// We have to do this in a for loop for `?`
let mut cmd_strs: Vec<BonesCore> = Vec::new();
for command_wrapper in command_wrapper_vec.iter() {
let (cmd_str, shell) = command_wrapper.get_command_and_shell(&default_shell);
let (cmds, shell) = command_wrapper.get_commands_and_shell(&default_shell);
for cmd_str in cmds {
let with_env_vars = Command::interpolate_env_vars(&cmd_str, &self.env_vars)?;
let (with_args, remaining_args) = Command::interpolate_specific_args(
&with_env_vars,
Expand All @@ -165,7 +165,7 @@ impl Command {
Command::interpolate_remaining_arguments(&with_args, &remaining_args);
cmd_strs.push(BonesCore {
cmd: ready_cmd,
shell,
shell: shell.to_vec(),
});
}

Expand Down Expand Up @@ -324,7 +324,7 @@ pub struct CommandWrapper {
impl CommandWrapper {
// Gets the command to run, interpolated into a shell from the ambient OS information
// This critically resolves which target we're running on
fn get_command_and_shell(&self, default_shell: &DefaultShell) -> (String, Shell) {
fn get_commands_and_shell(&self, default_shell: &DefaultShell) -> (Vec<String>, Shell) {
// Get the current target (unfortuantely we can't actually get the value out of `cfg!` yet...)
// If the user needs to set custom commands based on target arch etc., they can write a script for it, this is exhaustive enough!
let running_on = match true {
Expand All @@ -345,8 +345,8 @@ impl CommandWrapper {
Some(command_core) => command_core,
None => &self.generic,
};
// Get the command as a string ready for interpolation
let cmd_str = &command_core.exec;
// Get the commands as a vector ready for interpolation
let cmd = &command_core.exec;
// Get the shell, using the configured per-file default if it was undefined
let shell = match &command_core.shell {
Some(shell) => shell,
Expand All @@ -362,14 +362,14 @@ impl CommandWrapper {
}
};

(cmd_str.to_string(), shell.to_vec())
(cmd.to_vec(), shell.to_vec())
}
}
// This is the lowest level of command specification, there is no more recursion allowed here (thus avoiding circularity)
// Actual command must be specified here are strings (with potential interpolation of arguments and environment variables)
// This can also define which shell the command will use
#[derive(Debug, Clone, Deserialize)]
pub struct CommandCore {
pub exec: String, // This is the actual command that will be run (named differently to avoid collisions)
pub exec: Vec<String>, // These are the actual commands that will be run (named differently to avoid collisions)
pub shell: Option<Shell>, // If given, this is the shell it will be run in, or the `default_shell` config for this target will be used
}

0 comments on commit 200b3db

Please sign in to comment.