diff --git a/.woodpecker/build.yml b/.woodpecker/build.yml index f792be9..5779344 100644 --- a/.woodpecker/build.yml +++ b/.woodpecker/build.yml @@ -7,4 +7,5 @@ pipeline: build: image: 'rust:1.69' commands: - - cargo build + - cargo build --verbose + - cargo test --verbose diff --git a/affluences-api/src/models/available.rs b/affluences-api/src/models/available.rs index 4b63f5f..dd0980a 100644 --- a/affluences-api/src/models/available.rs +++ b/affluences-api/src/models/available.rs @@ -1,5 +1,5 @@ use super::hh_mm_time_format; -use chrono::NaiveTime; +use chrono::{Duration, NaiveTime}; use serde::Deserialize; #[derive(Deserialize, Debug, Clone, Copy)] @@ -41,3 +41,39 @@ pub struct Resource { pub slots_state: u32, pub hours: Vec, } + +impl Resource { + pub fn condensed_hours(&self) -> Vec<(&HourBlock, Duration)> { + let mut start_hour_opt: Option<&HourBlock> = None; + let mut duration = Duration::seconds(0); + let mut out: Vec<(&HourBlock, Duration)> = Default::default(); + + for hour in &self.hours { + if let Some(start_hour) = start_hour_opt { + if hour.state == start_hour.state { + duration = duration + Duration::minutes(hour.granularity.into()); + } else { + out.push((start_hour, duration)); + start_hour_opt = Some(hour); + duration = Duration::minutes(hour.granularity.into()); + } + } else if hour.state == 1 { + start_hour_opt = Some(hour); + duration = Duration::minutes(hour.granularity.into()); + } + } + + if let Some(start_hour) = start_hour_opt { + out.push((start_hour, duration)); + } + + out + } + + pub fn condensed_available_hours(&self) -> Vec<(&HourBlock, Duration)> { + self.condensed_hours() + .into_iter() + .filter(|(hour, _)| hour.state == 1) + .collect() + } +} diff --git a/src/commands.rs b/src/commands.rs index b955679..dc74e0c 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1,6 +1,5 @@ use crate::{Context, Error}; -use affluences_api::HourBlock; -use chrono::{Duration, NaiveDate}; +use chrono::NaiveDate; use uuid::{uuid, Uuid}; const STERRE_BIB_ID: Uuid = uuid!("4737e57a-ee05-4f7b-901a-7bb541eeb297"); @@ -26,62 +25,6 @@ pub async fn help( Ok(()) } -/// Vote for something -/// -/// Enter `~vote pumpkin` to vote for pumpkins -#[poise::command(prefix_command, slash_command)] -pub async fn vote( - ctx: Context<'_>, - #[description = "What to vote for"] choice: String, -) -> Result<(), Error> { - // Lock the Mutex in a block {} so the Mutex isn't locked across an await point - let num_votes = { - let mut hash_map = ctx.data().votes.lock().unwrap(); - let num_votes = hash_map.entry(choice.clone()).or_default(); - *num_votes += 1; - *num_votes - }; - - let response = format!("Successfully voted for {choice}. {choice} now has {num_votes} votes!"); - ctx.say(response).await?; - Ok(()) -} - -/// Retrieve number of votes -/// -/// Retrieve the number of votes either in general, or for a specific choice: -/// ``` -/// ~getvotes -/// ~getvotes pumpkin -/// ``` -#[poise::command(prefix_command, track_edits, aliases("votes"), slash_command)] -pub async fn getvotes( - ctx: Context<'_>, - #[description = "Choice to retrieve votes for"] choice: Option, -) -> Result<(), Error> { - if let Some(choice) = choice { - let num_votes = *ctx.data().votes.lock().unwrap().get(&choice).unwrap_or(&0); - let response = match num_votes { - 0 => format!("Nobody has voted for {} yet", choice), - _ => format!("{} people have voted for {}", num_votes, choice), - }; - ctx.say(response).await?; - } else { - let mut response = String::new(); - for (choice, num_votes) in ctx.data().votes.lock().unwrap().iter() { - response += &format!("{}: {} votes", choice, num_votes); - } - - if response.is_empty() { - response += "Nobody has voted for anything yet :("; - } - - ctx.say(response).await?; - }; - - Ok(()) -} - /// List available timeslots for day #[poise::command(prefix_command, slash_command)] pub async fn available(ctx: Context<'_>, date: NaiveDate) -> Result<(), Error> { @@ -90,7 +33,9 @@ pub async fn available(ctx: Context<'_>, date: NaiveDate) -> Result<(), Error> { let mut fields: Vec<(String, String, bool)> = Default::default(); for resource in &resources { - if resource.hours.is_empty() { + let available_hours = resource.condensed_available_hours(); + + if available_hours.is_empty() { fields.push(( resource.resource_name.clone(), "Nothing available.".to_string(), @@ -100,45 +45,23 @@ pub async fn available(ctx: Context<'_>, date: NaiveDate) -> Result<(), Error> { continue; } - let mut lines: Vec = Default::default(); - - let mut start_hour_opt: Option<&HourBlock> = None; - let mut duration = Duration::seconds(0); - - for hour in &resource.hours { - if let Some(start_hour) = start_hour_opt { - if hour.state == 1 { - duration = duration + Duration::minutes(hour.granularity.into()); - } else { - let end_hour = start_hour.hour + duration; - lines.push(format!( + fields.push(( + resource.resource_name.clone(), + available_hours + .into_iter() + .map(|(start_block, duration)| { + format!( "{} - {} ({:02}:{:02})", - start_hour.hour.format(TIME_FORMAT), - end_hour.format(TIME_FORMAT), + start_block.hour.format(TIME_FORMAT), + (start_block.hour + duration).format(TIME_FORMAT), duration.num_hours(), duration.num_minutes() % 60 - )); - start_hour_opt = None; - } - } else if hour.state == 1 { - start_hour_opt = Some(hour); - duration = Duration::minutes(hour.granularity.into()); - } - } - - // Print final entry if present - if let Some(start_hour) = start_hour_opt { - let end_hour = start_hour.hour + duration; - lines.push(format!( - "{} - {} ({:02}:{:02})", - start_hour.hour.format(TIME_FORMAT), - end_hour.format(TIME_FORMAT), - duration.num_hours(), - duration.num_minutes() % 60 - )); - } - - fields.push((resource.resource_name.clone(), lines.join("\n"), false)); + ) + }) + .collect::>() + .join("\n"), + false, + )); } ctx.send(|f| { diff --git a/src/main.rs b/src/main.rs index f6e06bb..b18e13e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -36,12 +36,7 @@ async fn main() { // FrameworkOptions contains all of poise's configuration option in one struct // Every option can be omitted to use its default value let options = poise::FrameworkOptions { - commands: vec![ - commands::help(), - commands::vote(), - commands::getvotes(), - commands::available(), - ], + commands: vec![commands::help(), commands::available()], prefix_options: poise::PrefixFrameworkOptions { prefix: Some("~".into()), edit_tracker: Some(poise::EditTracker::for_timespan(Duration::from_secs(3600))),