diff --git a/src/config_manager.rs b/src/config_manager.rs index 33466f2..0c21be2 100644 --- a/src/config_manager.rs +++ b/src/config_manager.rs @@ -1,18 +1,17 @@ use serde::{Deserialize, Serialize}; +use log::{trace, warn}; use std::path::Path; const CONFIG_FILENAME: &str = "./config.json"; -// TODO REMOVE PUB AND REPLACE BY FN -#[derive(Clone, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize)] pub struct Config { pub host: String, pub token: String, pub projects: Vec } -// TODO REMOVE PUB AND REPLACE BY FN #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Project { pub name: String, @@ -22,15 +21,21 @@ pub struct Project { } pub fn get_config() -> Option { + trace!("Trying to read config file at location '{}'.", CONFIG_FILENAME); if Path::exists(Path::new(CONFIG_FILENAME)) { return serde_json::from_str( - std::fs::read_to_string(CONFIG_FILENAME).unwrap().as_str()) - .unwrap(); + std::fs::read_to_string(CONFIG_FILENAME) + .expect("Cannot read configuration file.").as_str()) + .expect("Cannot deserialize configuration file."); } + warn!("Configuration file doesn't exist at location '{}'.", CONFIG_FILENAME); return None; } pub fn save_config(config: &Config) { + trace!("Saving configuration file with content {:?}.", config); std::fs::write(Path::new(CONFIG_FILENAME), - serde_json::to_string_pretty(config).unwrap()).unwrap(); + serde_json::to_string_pretty(config) + .expect("Could not serialize the configuration class to JSON.")) + .expect("Could not write serialized configuration file."); } diff --git a/src/gitlab_controller.rs b/src/gitlab_controller.rs index 2799e31..463ab32 100644 --- a/src/gitlab_controller.rs +++ b/src/gitlab_controller.rs @@ -2,7 +2,7 @@ use std::{thread::{sleep, self, JoinHandle}, time::Duration}; use launchy::{launchpad_mini::{Button, Color, Output, DoubleBufferingBehavior}, OutputDevice}; use serde::Deserialize; -use log::info; +use log::{warn, trace, debug, error}; use gitlab::{Gitlab, api::{projects::pipelines::{Pipelines, PipelineOrderBy}, Query}}; use crate::config_manager::{Config, Project}; @@ -15,6 +15,7 @@ pub struct Pipeline { status: String } +#[derive(Debug)] enum PipelineStatus { Created, WaitingForResource, @@ -31,6 +32,7 @@ enum PipelineStatus { impl PipelineStatus { fn from(s: &str) -> Option { + trace!("Trying to convert '{}' to PipelineStatus.", s); return match s { "created" => Some(PipelineStatus::Created), "waiting_for_resource" => Some(PipelineStatus::WaitingForResource), @@ -43,29 +45,36 @@ impl PipelineStatus { "skipped" => Some(PipelineStatus::Skipped), "manual" => Some(PipelineStatus::Manual), "scheduled" => Some(PipelineStatus::Scheduled), - _ => None + _ => { warn!("Could not find correspondance for status '{}'.", s); None } } } fn get_color(&self) -> (Color, bool) { + trace!("Getting color for PipelineStatus '{:?}'.", self); return match self { PipelineStatus::Pending => (Color::YELLOW, false), PipelineStatus::Running => (Color::AMBER, true), PipelineStatus::Success => (Color::GREEN, false), PipelineStatus::Failed => (Color::RED, false), - _ => (Color::OFF, false) + _ => { warn!("Unknown color for pipeline status {:?}.", self); + (Color::OFF, false) } }; } } pub fn refresh_on_timer(client: Gitlab, project: Project) { + trace!("Starting a refresh on timer task for project {:?}.", project); let mut output = Output::guess().unwrap(); loop { let pipeline = get_latest_pipelines(&client, &project); - if pipeline.is_none() { return; } + if pipeline.is_none() { + error!("Project {:?} has no existing pipeline. Ignoring.", project); + return; + } let c = PipelineStatus::from(pipeline.unwrap().status.as_str()) .unwrap(); + debug!("Project {:?} has a pipeline status of {:?}, updating.", project, c); output.set_button(Button::GridButton { x: project.abs_x, y: project.abs_y }, c.get_color().0, if c.get_color().1 { DoubleBufferingBehavior::Clear } @@ -75,42 +84,61 @@ pub fn refresh_on_timer(client: Gitlab, project: Project) { } pub fn load_from_config(config: Config) -> Vec> { - info!("Loading gitlab manager from configuration."); + trace!("Loading gitlab manager from configuration."); let mut threads = vec![]; for p in config.projects.iter() { let client = Gitlab::new(config.host.as_str(), config.token.as_str()).unwrap(); let project = p.clone(); + debug!("Spawning refresh thread for project {:?}.", project); threads.push(thread::spawn(move|| refresh_on_timer(client, project))); } + debug!("Spawned {} threads for the refreshes.", threads.len()); return threads; } pub fn get_latest_pipelines(client: &Gitlab, project: &Project) -> Option { + trace!("Getting latest pipeline for project {:?}.", project); let endpoint = Pipelines::builder() .project(project.name.as_str()).ref_(project.ref_.as_str()) .order_by(PipelineOrderBy::UpdatedAt).build().unwrap(); let pipelines: Vec = endpoint.query(client).unwrap_or(vec![]); - return if pipelines.is_empty() { None } else { - Some(pipelines.first().unwrap().to_owned()) }; + if pipelines.is_empty() { + warn!("No pipeline found for project {:?}.", project); + return None; + } else { + let pipeline = pipelines.first().unwrap().to_owned(); + debug!("Found pipeline {:?} as latest pipeline for project {:?}.", + pipeline, project); + return Some(pipeline); + } } pub fn retry_pipeline(host: &str, token: &str, project: &Project) { + trace!("Retrying pipeline for project {:?}.", project); let client = Gitlab::new(host, token).unwrap(); let pipeline = get_latest_pipelines(&client, &project); - if pipeline.is_none() { return; } - let _res: Pipeline; + if pipeline.is_none() { + warn!("{:?} has no pipeline, ignoring.", project); + return; + } + let res: Pipeline; if pipeline.clone().unwrap().status == "success" { + debug!("Latest pipeline for {:?} has a 'success' status, creating another one.", + project); let endpoint = gitlab::api::projects::pipelines::CreatePipeline::builder() .project(project.name.clone()) .ref_(project.ref_.clone()) .build().unwrap(); - _res = endpoint.query(&client).unwrap(); + res = endpoint.query(&client).unwrap(); } else { + debug!("Latest pipeline status for {:?} wasn't a success, retrying.", + project); let endpoint = gitlab::api::projects::pipelines::RetryPipeline::builder() .project(project.name.clone()) .pipeline(pipeline.unwrap().id) .build().unwrap(); - _res = endpoint.query(&client).unwrap(); + res = endpoint.query(&client).unwrap(); } + debug!("API answer was {:?}.", res); } diff --git a/src/launchpad_controller.rs b/src/launchpad_controller.rs index 39080d6..41a8019 100644 --- a/src/launchpad_controller.rs +++ b/src/launchpad_controller.rs @@ -1,24 +1,30 @@ use launchy::{OutputDevice, InputDevice, launchpad_mini::{Output, Input, Buffer, Button, Color, DoubleBuffering, DoubleBufferingBehavior, Message}, MsgPollingWrapper}; +use log::{trace, debug, warn}; use crate::{config_manager::Config, gitlab_controller::retry_pipeline}; -const RESTART_BUTTON: Button = Button::GridButton { x: 8, y: 0 }; +const RESTART_BUTTON: Button = Button::GridButton { x: 8, y: 0 }; // A BUTTON fn engage_restart(output: &mut Output) { + trace!("Engaging restart mode."); output.set_button(RESTART_BUTTON, Color::DIM_GREEN, DoubleBufferingBehavior::Copy).unwrap(); } fn disengage_restart(output: &mut Output) { + trace!("Disengaging restart mode."); output.set_button(RESTART_BUTTON, Color::OFF, DoubleBufferingBehavior::Copy).unwrap(); } fn running_thread(config: Config) { + trace!("Starting a thread dedicated to the Launchpad input."); let mut output = Output::guess().unwrap(); let input = Input::guess_polling().unwrap(); let mut restart = false; for msg in input.iter() { + debug!("Got message from Launchpad: {:?}.", msg); if let Message::Release { button } = msg { if button == RESTART_BUTTON { + debug!("Restart button has been pressed."); if !restart { restart = true; engage_restart(&mut output); @@ -27,16 +33,26 @@ fn running_thread(config: Config) { disengage_restart(&mut output); } } else if button.abs_x() != 8 { + debug!("A project tile has been pressed."); let project = config.projects.iter() .find(|p| p.abs_x == button.abs_x() && p.abs_y + 1 == button.abs_y()); - if project.is_none() { continue; } + if project.is_none() { + warn!("The tile {:?} has no project associated. Ignoring.", button); + continue; + } let project = project.unwrap(); + debug!("Tile {:?} has an associated project {:?}.", button, project); if restart { + debug!("Restart mode is engaged, attempting to restart pipeline for project {:?}.", + project); restart = false; retry_pipeline(config.host.as_str(), config.token.as_str(), project); disengage_restart(&mut output); } else { - open::that_in_background(format!("https://{}/{}", config.host, project.name)); + let url = format!("https://{}/{}", config.host, project.name); + debug!("Restart mode isn't engaged. Trying to open project {:?} in browser with URL {}.", + project, url); + open::that_in_background(url); } } } @@ -44,13 +60,17 @@ fn running_thread(config: Config) { } pub fn init(config: Config) { + trace!("Initializing launchpad."); let mut output = launchy::launchpad_mini::Output::guess().unwrap(); + debug!("Resetting launchpad state."); output.reset().unwrap(); + debug!("Setting up the double buffering behavior for the launchpad."); output.control_double_buffering(DoubleBuffering { copy: false, flash: true, edited_buffer: Buffer::A, displayed_buffer: Buffer::A }).unwrap(); + debug!("Spawning a thread for handling to launchpad I/O."); std::thread::spawn(move || running_thread(config)); } diff --git a/src/main.rs b/src/main.rs index 83debbb..0291982 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use log::error; +use log::{info, error, trace}; use crate::config_manager::Config; use crate::config_manager::Project; @@ -9,8 +9,10 @@ mod gitlab_controller; fn main() { env_logger::init(); + trace!("Starting main application."); let conf = config_manager::get_config(); if conf.is_none() { + info!("Configuration file cannot be loaded, saving a default one."); config_manager::save_config(&Config { host: "gitlab-host".to_string(), token: "your-gitlab-key".to_string(), @@ -27,6 +29,7 @@ fn main() { let conf = conf.unwrap(); launchpad_controller::init(conf.clone()); let threads = gitlab_controller::load_from_config(conf); + trace!("Joining timer threads."); for t in threads { t.join().unwrap(); }