Skip to content

Commit

Permalink
feat: ✨ added caching logic
Browse files Browse the repository at this point in the history
Closes #14
  • Loading branch information
arctic-hen7 committed Jul 7, 2021
1 parent 8a8cb9c commit c9c4c4d
Show file tree
Hide file tree
Showing 9 changed files with 386 additions and 44 deletions.
260 changes: 260 additions & 0 deletions src/.bonnie.cache.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
{
"default_shell": {
"generic": ["sh", "-c", "{COMMAND}"],
"targets": { "windows": ["cmd", "/C", "{COMMAND}"] }
},
"scripts": {
"append_with_args": {
"args": ["name"],
"env_vars": [],
"subcommands": null,
"order": null,
"cmd": {
"generic": {
"exec": ["echo \"Hi %name! Message is: '%%'.\""],
"shell": null
},
"targets": {}
}
},
"basic": {
"args": [],
"env_vars": [],
"subcommands": null,
"order": null,
"cmd": {
"generic": { "exec": ["echo Test"], "shell": null },
"targets": {}
}
},
"adaptable": {
"args": [],
"env_vars": [],
"subcommands": null,
"order": null,
"cmd": {
"generic": { "exec": ["echo Test"], "shell": null },
"targets": {
"magic_os": {
"exec": ["please print string \"Test\" > console"],
"shell": null
}
}
}
},
"subcommands": {
"args": [],
"env_vars": [],
"subcommands": {
"nested": {
"args": [],
"env_vars": [],
"subcommands": {
"basic": {
"args": [],
"env_vars": [],
"subcommands": null,
"order": null,
"cmd": {
"generic": {
"exec": ["echo Nested"],
"shell": null
},
"targets": {}
}
}
},
"order": ["basic", {}],
"cmd": null
},
"multistage_with_interpolation": {
"args": ["name"],
"env_vars": ["GREETING"],
"subcommands": null,
"order": null,
"cmd": {
"generic": {
"exec": [
"echo \"Greeting: %GREETING\"",
"echo \"Name: %name\""
],
"shell": null
},
"targets": {}
}
},
"basic": {
"args": [],
"env_vars": [],
"subcommands": null,
"order": null,
"cmd": {
"generic": { "exec": ["echo Test"], "shell": null },
"targets": {}
}
}
},
"order": null,
"cmd": {
"generic": { "exec": ["echo Parent"], "shell": null },
"targets": {}
}
},
"interpolation": {
"args": ["name"],
"env_vars": ["GREETING"],
"subcommands": null,
"order": null,
"cmd": {
"generic": { "exec": ["echo %GREETING %name"], "shell": null },
"targets": {}
}
},
"multistage": {
"args": [],
"env_vars": [],
"subcommands": null,
"order": null,
"cmd": {
"generic": {
"exec": ["echo \"Part 1\"", "echo \"Part 2\""],
"shell": null
},
"targets": {}
}
},
"super_versatile": {
"args": [],
"env_vars": [],
"subcommands": null,
"order": null,
"cmd": {
"generic": {
"exec": ["echo Test"],
"shell": ["sh", "-c", "{COMMAND}"]
},
"targets": {
"magic_os": {
"exec": ["please print string \"Test\" > console"],
"shell": [
"please",
"run",
"command",
"{COMMAND}",
"against",
"sys"
]
}
}
}
},
"multistage_with_interpolation": {
"args": ["name"],
"env_vars": ["GREETING"],
"subcommands": null,
"order": null,
"cmd": {
"generic": {
"exec": [
"echo \"Greeting: %GREETING\"",
"echo \"Name: %name\""
],
"shell": null
},
"targets": {}
}
},
"echo": {
"args": [],
"env_vars": [],
"subcommands": null,
"order": null,
"cmd": {
"generic": {
"exec": ["echo \"Message is: '%%'.\""],
"shell": null
},
"targets": {}
}
},
"power": {
"args": ["name"],
"env_vars": [],
"subcommands": {
"error": {
"args": [],
"env_vars": [],
"subcommands": null,
"order": null,
"cmd": {
"generic": {
"exec": ["echo \"An error has occurred.\""],
"shell": null
},
"targets": {}
}
},
"basic": {
"args": [],
"env_vars": [],
"subcommands": null,
"order": null,
"cmd": {
"generic": { "exec": ["echo Test"], "shell": null },
"targets": {}
}
},
"multistage_with_interpolation": {
"args": [],
"env_vars": ["GREETING"],
"subcommands": null,
"order": null,
"cmd": {
"generic": {
"exec": [
"echo \"Greeting: %GREETING\"",
"echo \"Name: %name\""
],
"shell": null
},
"targets": {}
}
},
"nested": {
"args": [],
"env_vars": [],
"subcommands": {
"basic": {
"args": [],
"env_vars": [],
"subcommands": null,
"order": null,
"cmd": {
"generic": {
"exec": ["echo Nested"],
"shell": null
},
"targets": {}
}
}
},
"order": ["basic", {}],
"cmd": null
}
},
"order": [
"basic",
{
"Success": [
"multistage_with_interpolation",
{ "Failure": ["error", {}] }
],
"Failure": ["error", {}]
}
],
"cmd": null
}
},
"env_files": [".env"],
"version": "0.3.0"
}
42 changes: 32 additions & 10 deletions src/bin/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use lib::{get_cfg, Config, BONNIE_VERSION, init, help};
use lib::{cache, cache_exists, get_cfg, help, init, load_from_cache, Config, BONNIE_VERSION};
use std::env;
use std::io::Write;

Expand Down Expand Up @@ -35,6 +35,7 @@ fn core() -> Result<i32, String> {
// TODO add a checker for the executable that offers to install Bonnie if it isn't already?
let _executable_name = prog_args.remove(0);
// Check for special arguments
let mut should_cache = false;
if matches!(prog_args.get(0), Some(_)) {
if prog_args[0] == "-v" || prog_args[0] == "--version" {
writeln!(stdout, "You are currently running Bonnie v{}! You can see the latest release at https://github.com/arctic-hen7/bonnie/releases.", BONNIE_VERSION).expect("Failed to write version.");
Expand All @@ -43,22 +44,43 @@ fn core() -> Result<i32, String> {
init(
// See if a template was provided with the `--template`/`-t` flag
match prog_args.get(1).as_ref() {
Some(arg) if &**arg == "-t" || &**arg == "--template" => prog_args.get(2).map(|x| x.to_string()),
_ => None
}
Some(arg) if &**arg == "-t" || &**arg == "--template" => {
prog_args.get(2).map(|x| x.to_string())
}
_ => None,
},
)?;
return Ok(0);
} else if prog_args[0] == "-h" || prog_args[0] == "--help" {
help(stdout);
return Ok(0);
} else if prog_args[0] == "-c" || prog_args[0] == "--cache" {
should_cache = true;
}
}
// Get the config as a string
let cfg_str = get_cfg()?;
// Create a raw config object and parse it fully
// We use `stdout` for printing warnings
// TODO this takes meaningful millseconds for complex configs, so we should be able to cache its results in `.bonnie.cache.json` for speed in extreme cases
let cfg = Config::new(&cfg_str)?.to_final(BONNIE_VERSION, stdout)?;
// Check if there's a cache we should read from
// If there is but we're explicitly recaching, we should of course read directly from the source file
let cfg;
if cache_exists() && !should_cache {
cfg = load_from_cache(stdout)?;
} else {
// Get the config as a string
let cfg_str = get_cfg()?;
// Create a raw config object and parse it fully
// We use `stdout` for printing warnings
cfg = Config::new(&cfg_str)?.to_final(BONNIE_VERSION, stdout)?;
}

// Check if we're caching
if should_cache {
cache(&cfg)?;
writeln!(
stdout,
"Your Bonnie configuration has been successfully cached to './.bonnie.cache.json'! This will be used to speed up future execution. Please note that this cache will NOT be updated until you explicitly run `bonnie -c` again."
).expect("Failed to write caching message.");
return Ok(0);
}

// Determine which command we're actually running
let (command_to_run, command_name, relevant_args) = cfg.get_command_for_args(&prog_args)?;
// Get the Bone (item in Bones execution runtime)
Expand Down
14 changes: 7 additions & 7 deletions src/bones.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Bones is Bonnie's command execution runtime, which mainly handles ordered subcommands

use regex::Regex;
use serde::Deserialize;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::process::Command as OsCommand;

// This enables recursion of ordered subcommands (which would be the most complex use-case of Bonnie thus far)
// This really represents (from Bonnie's perspective) a future for an exit code
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Bone {
Simple(Vec<BonesCore>),
Complex(BonesCommand),
Expand Down Expand Up @@ -46,7 +46,7 @@ impl Bone {
}
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BonesCommand {
// A HashMap of command names to vectors of raw commands to be executed
// The commands to run are expected to have interpolation and target/shell resolution already done
Expand Down Expand Up @@ -112,10 +112,10 @@ impl BonesCommand {

// A directive telling the Bones engine how to progress between ordered subcommands
// This maps the command to run to a set of conditions as to how to proceed based on its exit code
#[derive(Debug, Clone, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BonesDirective(String, HashMap<BonesOperator, Option<BonesDirective>>);
// This is used for direct parsing, before we've had a chance to handle the operators
#[derive(Debug, Clone, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
struct RawBonesDirective(String, HashMap<String, Option<RawBonesDirective>>);
impl RawBonesDirective {
// This converts to a `BonesDirective` by parsing the operator strings into full operators
Expand All @@ -141,7 +141,7 @@ impl RawBonesDirective {
}
// Bones operators can be more than just exit codes, this defines their possibilities
// For deserialization, this is left tagged (we pre-parse)
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, std::hash::Hash)]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, std::hash::Hash)]
pub enum BonesOperator {
// A simple exit code comparison
ExitCode(i32),
Expand Down Expand Up @@ -256,7 +256,7 @@ impl BonesOperator {
}
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BonesCore {
pub cmd: String,
pub shell: Vec<String>, // Vector of executable and arguments thereto
Expand Down
Loading

0 comments on commit c9c4c4d

Please sign in to comment.