Skip to content

Commit

Permalink
Merge pull request #28 from blake-mealey/dev/logging
Browse files Browse the repository at this point in the history
make logging consistent
  • Loading branch information
blake-mealey authored Nov 9, 2021
2 parents 531d1a8 + 44ba518 commit 5ba2c7d
Show file tree
Hide file tree
Showing 8 changed files with 331 additions and 182 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ difference = "2.0.0"
rusoto_core = "0.47.0"
rusoto_s3 = "0.47.0"
tokio = { version = "1", features = ["full"] }
yansi = "0.5.0"
6 changes: 3 additions & 3 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,17 @@ fn get_app() -> App<'static, 'static> {
)
}

pub async fn run_with(args: Vec<String>) -> Result<(), String> {
pub async fn run_with(args: Vec<String>) -> i32 {
let app = get_app();
let matches = app.get_matches_from(args);
match matches.subcommand() {
("deploy", Some(deploy_matches)) => {
commands::deploy::run(deploy_matches.value_of("PROJECT")).await
}
_ => Err("Unreachable code reached!".to_string()),
_ => unreachable!(),
}
}

pub async fn run() -> Result<(), String> {
pub async fn run() -> i32 {
run_with(env::args().collect()).await
}
127 changes: 102 additions & 25 deletions src/commands/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ use std::{
str,
};

use yansi::Paint;

use crate::{
config::load_config_file,
config::{load_config_file, Config, DeploymentConfig},
logger,
resource_manager::RobloxResourceManager,
resources::{ResourceGraph, ResourceManager},
state::{get_desired_graph, get_previous_state, save_state},
resources::{EvaluateResults, ResourceGraph, ResourceManager},
state::{get_desired_graph, get_previous_state, save_state, ResourceState},
};

fn run_command(command: &str) -> std::io::Result<std::process::Output> {
Expand Down Expand Up @@ -62,23 +65,30 @@ fn parse_project(project: Option<&str>) -> Result<(PathBuf, PathBuf), String> {
} else if project_path.is_file() {
(project_path.parent().unwrap().into(), project_path)
} else {
return Err(format!("Unable to parse project path: {}", project));
return Err(format!("Unable to load project path: {}", project));
};

if config_file.exists() {
return Ok((project_dir, config_file));
}

Err(format!(
"Config file does not exist: {}",
config_file.display()
))
Err(format!("Config file {} not found", config_file.display()))
}

struct Project {
project_path: PathBuf,
next_graph: ResourceGraph,
previous_graph: ResourceGraph,
state: ResourceState,
deployment_config: DeploymentConfig,
config: Config,
}

pub async fn run(project: Option<&str>) -> Result<(), String> {
async fn load_project(project: Option<&str>) -> Result<Option<Project>, String> {
let (project_path, config_file) = parse_project(project)?;

let config = load_config_file(&config_file)?;
logger::log(format!("Loaded config file {}", config_file.display()));

let current_branch = get_current_branch()?;

Expand All @@ -90,35 +100,102 @@ pub async fn run(project: Option<&str>) -> Result<(), String> {
let deployment_config = match deployment_config {
Some(v) => v,
None => {
println!("No deployment configuration found for branch; no deployment necessary.");
return Ok(());
logger::log(format!(
"No deployment configuration found for branch '{}'",
current_branch
));
return Ok(None);
}
};

// Get our resource manager
let mut resource_manager =
ResourceManager::new(Box::new(RobloxResourceManager::new(&project_path)));
logger::log(format!(
"Found deployment configuration '{}' for branch '{}'",
deployment_config.name, current_branch
));

// Get previous state
let mut state = get_previous_state(project_path.as_path(), &config, deployment_config).await?;
let state = get_previous_state(project_path.as_path(), &config, deployment_config).await?;

// Get our resource graphs
let previous_graph =
ResourceGraph::new(state.deployments.get(&deployment_config.name).unwrap());
let mut next_graph = get_desired_graph(project_path.as_path(), &config, deployment_config)?;
let next_graph = get_desired_graph(project_path.as_path(), &config, deployment_config)?;

Ok(Some(Project {
project_path,
next_graph,
previous_graph,
state,
deployment_config: deployment_config.clone(),
config,
}))
}

// Evaluate the resource graph
let result = next_graph.evaluate(&previous_graph, &mut resource_manager);
pub async fn run(project: Option<&str>) -> i32 {
logger::start_action("Loading project:");
let Project {
project_path,
config,
deployment_config,
mut next_graph,
previous_graph,
mut state,
} = match load_project(project).await {
Ok(Some(v)) => v,
Ok(None) => {
logger::end_action("No deployment necessary");
return 0;
}
Err(e) => {
logger::end_action(Paint::red(e));
return 1;
}
};
logger::end_action("Succeeded");

// Save the results to the state file
let mut resource_manager =
ResourceManager::new(Box::new(RobloxResourceManager::new(&project_path)));

logger::start_action("Deploying resources:");
let exit_code = match next_graph.evaluate(&previous_graph, &mut resource_manager) {
Ok(results) => {
match results {
EvaluateResults {
created_count: 0,
updated_count: 0,
deleted_count: 0,
..
} => logger::end_action("No changes required"),
EvaluateResults {
created_count,
updated_count,
deleted_count,
noop_count,
} => logger::end_action(format!(
"Succeeded with {} create(s), {} update(s), {} delete(s), {} noop(s)",
created_count, updated_count, deleted_count, noop_count
)),
};
0
}
Err(e) => {
logger::end_action(Paint::red(e));
1
}
};

logger::start_action("Saving state:");
state.deployments.insert(
deployment_config.name.clone(),
next_graph.get_resource_list(),
);
save_state(&project_path, &config.state, &state).await?;

// If there were errors, return them
result?;
match save_state(&project_path, &config.state, &state).await {
Ok(_) => {}
Err(e) => {
logger::end_action(Paint::red(e));
return 1;
}
};
logger::end_action("Succeeded");

Ok(())
exit_code
}
115 changes: 115 additions & 0 deletions src/logger.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
use std::{
fmt::Display,
panic,
sync::atomic::{AtomicU16, Ordering},
};

use difference::{Changeset, Difference};
use yansi::{Color, Paint, Style};

static ACTION_COUNT: AtomicU16 = AtomicU16::new(0);

fn with_prefix_and_style<S1, S2>(text: S1, prefix: S2, style: Style) -> String
where
S1: Display,
S2: Display,
{
text.to_string()
.split('\n')
.map(|line| {
format!(
"{}{}",
prefix.to_string(),
Paint::new(line).with_style(style)
)
})
.collect::<Vec<_>>()
.join("\n")
}

fn with_prefix<S1, S2>(text: S1, prefix: S2) -> String
where
S1: Display,
S2: Display,
{
with_prefix_and_style(text, prefix, Style::default())
}

fn get_line_prefix() -> String {
" │ ".repeat(ACTION_COUNT.load(Ordering::SeqCst).into())
}

pub fn log<S>(message: S)
where
S: Display,
{
let line_prefix = get_line_prefix();
println!("{}", with_prefix(&message, &line_prefix));
}

pub fn start_action<S>(title: S)
where
S: Display,
{
log(title);
log(" ╷");
ACTION_COUNT.fetch_add(1, Ordering::SeqCst);
}

fn end_action_internal<S1, S2>(message: S1, results: Option<S2>)
where
S1: Display,
S2: Display,
{
if ACTION_COUNT.load(Ordering::SeqCst) == 0 {
panic!("Attempted to end an action that was not started.");
}

log("");
ACTION_COUNT.fetch_sub(1, Ordering::SeqCst);
log(&format!(" ╰─ {}", message));
if let Some(results) = results {
log(&with_prefix_and_style(
results,
" ",
Style::default().dimmed(),
));
}
log("");
}

pub fn end_action<S>(message: S)
where
S: Display,
{
end_action_internal(message, None::<String>);
}

pub fn end_action_with_results<S1, S2>(message: S1, results: S2)
where
S1: Display,
S2: Display,
{
end_action_internal(message, Some(results));
}

pub fn log_changeset(changeset: Changeset) {
log(&changeset
.diffs
.iter()
.map(|diff| match diff {
Difference::Same(same) => with_prefix_and_style(same, " ", Style::default().dimmed()),
Difference::Add(add) => with_prefix_and_style(
add,
&format!("{} ", Paint::green("+")),
Style::new(Color::Green),
),
Difference::Rem(rem) => with_prefix_and_style(
rem,
&format!("{} ", Paint::red("-")),
Style::new(Color::Red),
),
})
.collect::<Vec<String>>()
.join(&changeset.split));
}
13 changes: 3 additions & 10 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod cli;
mod commands;
mod config;
mod logger;
mod resource_manager;
mod resources;
mod roblox_api;
Expand All @@ -9,14 +10,6 @@ mod state;

#[tokio::main]
async fn main() {
let result = cli::run().await;

if let Err(e) = &result {
println!("\n❌ {}", e);
}

std::process::exit(match &result {
Ok(()) => 0,
Err(_) => 1,
});
let exit_code = cli::run().await;
std::process::exit(exit_code);
}
Loading

0 comments on commit 5ba2c7d

Please sign in to comment.