timer.rs
1 use std::cmp::Ordering; 2 use std::time::{Duration, Instant}; 3 4 // TODO: 5 // - [ ] add states to get and set timer, also the instants? 6 // - [ ] enhance error handling with error types also for set_timer 7 // - [ ] use struct instead of tuples in get_timer and set_timer 8 // - [ ] add trait to string for state so that get_timer gets str 9 // - [ ] do the opossite to create a strate from str 10 // - [ ] externalize the import and export timer to main by doing the 11 // traits to str into the state too 12 // - [ ] refactor get_duration to return seconds instead of duration? 13 // - [ ] reorganize get_duration and this crate 14 // - [ ] simplify this crate code 15 16 pub fn get_duration(instant: Instant) -> Option<Duration> { 17 Instant::now().checked_duration_since(instant) 18 } 19 20 pub fn get_elapsed_seconds(instant: Instant) -> u64 { 21 match Instant::now().checked_duration_since(instant) { 22 Some(s) => s.as_secs(), 23 None => Duration::from_secs(0).as_secs(), 24 } 25 } 26 27 // Transition To another state 28 #[derive(Debug, Clone, Copy, PartialEq)] 29 pub enum To { 30 Paused, 31 Started, 32 } 33 34 #[derive(Debug, Clone, PartialEq)] 35 pub enum State { 36 Idle, 37 Started, 38 Tranistion(To), 39 Paused, 40 Ended, 41 } 42 43 impl State { 44 pub fn update_state(timer: &mut Timer) -> Result<(), &'static str> { 45 match (timer.started_instant, timer.paused_instant) { 46 (Some(started), Some(paused)) => match started.cmp(&paused) { 47 Ordering::Less => match get_duration(started) { 48 Some(_accumulated_timer) => { 49 timer.state = Self::Tranistion(To::Paused); 50 Ok(()) 51 } 52 //None => Err("This instant is later than started"), 53 None => Err("This instant is later than started"), 54 }, 55 Ordering::Greater => match get_duration(paused) { 56 Some(_accumulated_paused) => { 57 timer.state = Self::Tranistion(To::Started); 58 Ok(()) 59 } 60 //None => Err("This instant is later than paused"), 61 None => Err("This instant is later than paused"), 62 }, 63 Ordering::Equal => Err("On state transiton Instants cannot be Equal"), 64 }, 65 (Some(_started), None) => { 66 timer.state = Self::Started; 67 Ok(()) 68 } 69 (None, Some(_paused)) => { 70 timer.state = Self::Paused; 71 Ok(()) 72 } 73 (None, None) => { 74 timer.state = Self::Idle; 75 Ok(()) 76 } 77 } 78 } 79 } 80 81 #[derive(Debug, Clone)] 82 pub struct Timer { 83 pub state: State, 84 pub started_instant: Option<Instant>, 85 pub paused_instant: Option<Instant>, 86 pub accumulated_timer: Duration, 87 pub accumulated_paused: Duration, 88 } 89 90 impl Timer { 91 pub fn new() -> Self { 92 Self::build( 93 State::Idle, 94 None, 95 None, 96 Duration::from_secs(0), 97 Duration::from_secs(0), 98 ) 99 } 100 101 pub fn build( 102 state: State, 103 started_instant: Option<Instant>, 104 paused_instant: Option<Instant>, 105 accumulated_timer: Duration, 106 accumulated_paused: Duration, 107 ) -> Self { 108 Self { 109 state, 110 started_instant, 111 paused_instant, 112 accumulated_timer, 113 accumulated_paused, 114 } 115 } 116 pub fn get_timer(&self) -> (String, Duration, Duration) { 117 let state = match self.state { 118 State::Started => "started", 119 State::Paused => "paused", 120 State::Idle => "idle", 121 State::Ended => "ended", 122 State::Tranistion(t) => match &t { 123 To::Paused => "transition_to_paused", 124 To::Started => "transition_to_started", 125 }, 126 }; 127 128 let timer_duration = match self.started_instant { 129 Some(timer_instant) => { 130 self.accumulated_timer 131 + get_duration(timer_instant).unwrap_or(Duration::from_secs(0)) 132 } 133 None => self.accumulated_timer, 134 }; 135 let paused_duration = match self.paused_instant { 136 Some(paused_instant) => { 137 self.accumulated_paused 138 + get_duration(paused_instant).unwrap_or(Duration::from_secs(0)) 139 } 140 None => self.accumulated_paused, 141 }; 142 (state.to_string(), timer_duration, paused_duration) 143 } 144 145 pub fn set_timer(&mut self, data: (&str, Duration, Duration)) -> Result<(), &'static str> { 146 let state = match data.0 { 147 "started" => State::Started, 148 "paused" => State::Paused, 149 "idle" => State::Idle, 150 "ended" => State::Ended, 151 "transition_to_paused" => State::Tranistion(To::Paused), 152 "transition_to_started" => State::Tranistion(To::Started), 153 _ => return Err("Unknown State"), 154 }; 155 self.state = state; 156 self.accumulated_timer = data.1; 157 self.accumulated_paused = data.2; 158 Ok(()) 159 } 160 161 // send as a serialized string 162 pub fn export_timer(&self) -> String { 163 let state = match self.state { 164 State::Started => "started", 165 State::Paused => "paused", 166 State::Idle => "idle", 167 State::Ended => "ended", 168 State::Tranistion(t) => match &t { 169 To::Paused => "transition_to_paused", 170 To::Started => "transition_to_started", 171 }, 172 }; 173 174 let elapsed_timer_seconds = match self.started_instant { 175 Some(timer_instant) => { 176 self.accumulated_timer.as_secs() + get_elapsed_seconds(timer_instant) 177 } 178 None => self.accumulated_timer.as_secs(), 179 }; 180 let elapsed_paused_seconds = match self.paused_instant { 181 Some(paused_instant) => { 182 self.accumulated_paused.as_secs() + get_elapsed_seconds(paused_instant) 183 } 184 None => self.accumulated_paused.as_secs(), 185 }; 186 187 // here the structure is state, timer and paused time 188 format!( 189 "{}•{}•{}", 190 state, elapsed_timer_seconds, elapsed_paused_seconds 191 ) 192 } 193 194 // for imports only 195 pub fn import_timer(&mut self, data: String) -> Result<(), &'static str> { 196 let data_vec: Vec<&str> = data.split('•').collect(); 197 198 if data_vec.len() == 3 { 199 self.state = State::Idle; 200 self.started_instant = None; 201 self.paused_instant = None; 202 203 self.accumulated_timer = match data_vec.get(1) { 204 Some(elapsed_timer_string) => match elapsed_timer_string.parse::<u64>() { 205 Ok(elapsed_timer_seconds) => Duration::from_secs(elapsed_timer_seconds), 206 Err(_) => return Err("Error on conversion importing accumulated_timer"), 207 }, 208 None => Duration::from_secs(0), 209 }; 210 211 self.accumulated_paused = match data_vec.get(2) { 212 Some(elapsed_paused_string) => match elapsed_paused_string.parse::<u64>() { 213 Ok(elapsed_paused_seconds) => Duration::from_secs(elapsed_paused_seconds), 214 Err(_) => return Err("Error on conversion importing accumulated_paused"), 215 }, 216 None => Duration::from_secs(0), 217 }; 218 Ok(()) 219 } else { 220 Err("Error on data vector size: it must be 3") 221 } 222 } 223 224 pub fn print_time(&self) { 225 let timer_data = self.export_timer(); 226 let data_vec: Vec<&str> = timer_data.split('•').collect(); 227 228 if data_vec.len() == 3 { 229 println!(); 230 println!("State: {}", data_vec[0]); 231 println!("timer time: {}ms", data_vec[1]); 232 println!("paused time: {}ms", data_vec[2]); 233 } 234 } 235 236 pub fn start(&mut self) -> Result<(), &'static str> { 237 match self.state { 238 State::Started => Err("State: Started, should be IDLE or Paused"), 239 State::Paused => { 240 self.started_instant = Some(Instant::now()); 241 State::update_state(self)?; 242 Ok(()) 243 } 244 State::Idle => { 245 self.started_instant = Some(Instant::now()); 246 State::update_state(self)?; 247 Ok(()) 248 } 249 State::Ended => Err("State: Ended, Timer should be deleted"), 250 State::Tranistion(_) => Err("State: Transition, call transiton method to follow"), 251 } 252 } 253 254 pub fn pause(&mut self) -> Result<(), &'static str> { 255 match self.state { 256 State::Started => { 257 self.paused_instant = Some(Instant::now()); 258 State::update_state(self)?; 259 Ok(()) 260 } 261 State::Paused => Err("State: paused, Should be started"), 262 State::Idle => Err("State: IDLE, call start method"), 263 State::Ended => Err("State: Ended, Timer should be deleted"), 264 State::Tranistion(_) => Err("State: Transition, call transiton method to follow"), 265 } 266 } 267 268 fn _transition(&mut self) -> Result<(), &'static str> { 269 match (self.started_instant, self.paused_instant) { 270 (Some(started), Some(paused)) => match started.cmp(&paused) { 271 Ordering::Less => match get_duration(started) { 272 Some(accumulated_timer) => { 273 self.accumulated_timer += accumulated_timer; 274 self.started_instant = None; 275 State::update_state(self)?; 276 Ok(()) 277 } 278 None => Err("This instant is later than started"), 279 }, 280 Ordering::Greater => match get_duration(paused) { 281 Some(accumulated_paused) => { 282 self.accumulated_paused += accumulated_paused; 283 self.paused_instant = None; 284 State::update_state(self)?; 285 Ok(()) 286 } 287 None => Err("This instant is later than paused"), 288 }, 289 Ordering::Equal => Err("On state transition Instants cannot be Equal"), 290 }, 291 (None, None) => Err("Cannot do a transition: State is Idle"), 292 (None, Some(_paused)) => Err("Cannot do a transition: State is Paused"), 293 (Some(_started), None) => Err("Cannot do a transiton: State is Started"), 294 } 295 } 296 297 pub fn start_pause(&mut self) -> Result<(), &'static str> { 298 match self.state { 299 State::Idle => self.start(), 300 State::Paused => { 301 self.start()?; 302 self._transition()?; 303 Ok(()) 304 } 305 State::Started => { 306 self.pause()?; 307 self._transition()?; 308 Ok(()) 309 } 310 _ => self._transition(), 311 } 312 } 313 } 314 315 // ToDo: Assert timer.state in tests 316 #[cfg(test)] 317 mod tests { 318 use super::*; 319 use std::thread::sleep; 320 321 #[test] 322 fn test_idle_state() { 323 // State: Idle 324 let timer = Timer::new(); 325 326 assert_eq!(timer.paused_instant, None); 327 assert_eq!(timer.started_instant, None); 328 assert_eq!(timer.state, State::Idle); 329 } 330 331 #[test] 332 fn simple_started_state() { 333 // State: Idle 334 let mut timer = Timer::new(); 335 336 // Start timer 337 if let Err(e) = timer.start_pause() { 338 panic!("starting error: {e}"); 339 } 340 341 // State: Working 342 assert!(timer.started_instant.is_some()); 343 assert_eq!(timer.paused_instant, None); 344 assert_eq!(timer.state, State::Started); 345 346 // move time 1 347 sleep(Duration::new(1, 0)); 348 349 // verify started instant 350 assert_eq!( 351 timer 352 .started_instant 353 .expect("Not Elapsed") 354 .elapsed() 355 .as_secs(), 356 Duration::new(1, 0).as_secs() 357 ); 358 } 359 360 #[test] 361 fn simple_paused_state() { 362 // State: Idle 363 let mut timer = Timer::new(); 364 365 // Start timer 366 if let Err(e) = timer.start_pause() { 367 panic!("starting error: {e}"); 368 } 369 370 // State: Transiton -> State: Paused 371 // accumulated_timer = Some(Paused) - Some(Working) 372 // State: Paused 373 if let Err(e) = timer.start_pause() { 374 panic!("pausing error: {e}"); 375 }; 376 377 // State: Paused 378 assert_eq!(timer.started_instant, None); 379 assert!(timer.paused_instant.is_some()); 380 assert_eq!(timer.state, State::Paused); 381 382 // move time 1 383 sleep(Duration::new(1, 0)); 384 385 // verify paused instant 386 assert_eq!( 387 timer 388 .paused_instant 389 .expect("Not Elapsed") 390 .elapsed() 391 .as_secs(), 392 Duration::new(1, 0).as_secs() 393 ); 394 } 395 396 #[test] 397 #[ignore] 398 fn simple_start_stop_cycles_simulate() { 399 // State: Idle 400 let mut timer = Timer::new(); 401 402 for cycle in 1..=10 { 403 match timer.start_pause() { 404 Ok(_) => match (timer.started_instant, timer.paused_instant) { 405 (Some(started), None) => { 406 assert!(timer.started_instant.is_some()); 407 assert_eq!(timer.paused_instant, None); 408 sleep(Duration::new(1, 0)); 409 // the important assert elapsed timer time 410 assert_eq!(started.elapsed().as_secs(), Duration::new(1, 0).as_secs()); 411 // ((cycle - 1) / 2) ecuation maps, 2,4,6,.. into 1,2,3 412 assert_eq!( 413 timer.accumulated_paused.as_secs(), 414 Duration::new((cycle - 1) / 2, 0).as_secs() 415 ); 416 } 417 (None, Some(paused)) => { 418 assert_eq!(timer.started_instant, None); 419 assert!(timer.paused_instant.is_some()); 420 sleep(Duration::new(1, 0)); 421 // the important assert elapsed timer time 422 assert_eq!(paused.elapsed().as_secs(), Duration::new(1, 0).as_secs()); 423 //assert_eq!(timer.accumulated_paused.as_secs(), 424 //Duration::new(((cycle - 1) / 2) + 1, 0).as_secs()); 425 // ((cycle - 1) / 2) + 1 ecuation maps, 1,3,5,.. into 1,2,3 426 assert_eq!( 427 timer.accumulated_timer.as_secs(), 428 Duration::new(((cycle - 1) / 2) + 1, 0).as_secs() 429 ); 430 } 431 (Some(_), Some(_)) | (None, None) => { 432 panic!("transition or IDLE error"); 433 } 434 }, 435 Err(e) => panic!("start_pause error: {e}, on {cycle}"), 436 } 437 } 438 } 439 440 #[test] 441 fn started_state() { 442 // State: Idle 443 let mut timer = Timer::new(); 444 445 // Start timer 446 if let Err(e) = timer.start() { 447 panic!("starting error: {e}"); 448 } 449 450 // State: Started 451 assert!(timer.started_instant.is_some()); 452 assert_eq!(timer.paused_instant, None); 453 assert_eq!(timer.state, State::Started); 454 455 // move time 1 456 sleep(Duration::new(1, 0)); 457 458 // verify started instant 459 assert_eq!( 460 timer 461 .started_instant 462 .expect("Not Elapsed") 463 .elapsed() 464 .as_secs(), 465 Duration::new(1, 0).as_secs() 466 ); 467 } 468 469 #[test] 470 fn paused_state() { 471 // State: Idle 472 let mut timer = Timer::new(); 473 474 // Start timer 475 if let Err(e) = timer.start() { 476 panic!("starting error: {e}"); 477 } 478 479 match timer.pause() { 480 Ok(()) => match timer._transition() { 481 Err(e) => { 482 panic!("transitioning error: {e}"); 483 } 484 Ok(()) => (), 485 }, 486 Err(e) => { 487 panic!("pausing error: {e}"); 488 } 489 } 490 491 // State: Paused 492 assert_eq!(timer.started_instant, None); 493 assert!(timer.paused_instant.is_some()); 494 assert_eq!(timer.state, State::Paused); 495 496 // move time 1 497 sleep(Duration::new(1, 0)); 498 499 // verify paused instant 500 assert_eq!( 501 timer 502 .paused_instant 503 .expect("Not Elapsed") 504 .elapsed() 505 .as_secs(), 506 Duration::new(1, 0).as_secs() 507 ); 508 } 509 510 #[test] 511 fn transiton_to_paused() { 512 // State: Idle 513 let mut timer = Timer::new(); 514 515 // Start timer 516 if let Err(e) = timer.start() { 517 panic!("starting error: {e}"); 518 } 519 520 if let Err(e) = timer.pause() { 521 panic!("pausing error: {e}"); 522 } 523 524 // State: transition started -> paused 525 assert!(timer.started_instant < timer.paused_instant); 526 assert!(timer.started_instant.is_some()); 527 assert!(timer.paused_instant.is_some()); 528 assert_eq!(timer.state, State::Tranistion(To::Paused)); 529 530 if let Err(e) = timer._transition() { 531 panic!("transitioning error: {e}"); 532 }; 533 534 // means the transition passed sucessfully 535 // State: Paused 536 assert_eq!(timer.started_instant, None); 537 assert!(timer.paused_instant.is_some()); 538 assert_eq!(timer.state, State::Paused); 539 } 540 // ToDo: to test transition is required to decompose the start_pause method 541 #[test] 542 fn transiton_to_restarted() { 543 let mut timer = Timer::new(); 544 545 // Start timer 546 if let Err(e) = timer.start() { 547 panic!("starting error: {e}"); 548 } 549 550 if let Err(e) = timer.pause() { 551 panic!("pausing error: {e}"); 552 } 553 554 if let Err(e) = timer._transition() { 555 panic!("transitioning error: {e}"); 556 }; 557 558 if let Err(e) = timer.start() { 559 panic!("starting error: {e}"); 560 } 561 562 // State: transition paused -> started 563 assert!(timer.started_instant > timer.paused_instant); 564 assert!(timer.started_instant.is_some()); 565 assert!(timer.paused_instant.is_some()); 566 assert_eq!(timer.state, State::Tranistion(To::Started)); 567 568 if let Err(e) = timer._transition() { 569 panic!("transitioning error: {e}"); 570 }; 571 572 // means the transition passed sucessfully 573 // State: Started 574 assert!(timer.started_instant.is_some()); 575 assert_eq!(timer.paused_instant, None); 576 assert_eq!(timer.state, State::Started); 577 } 578 579 #[test] 580 fn start_stop_cycles() { 581 let mut timer = Timer::new(); 582 583 // Start timer 584 if let Err(e) = timer.start() { 585 panic!("starting error: {e}"); 586 } 587 588 // State: Started 589 assert!(timer.started_instant.is_some()); 590 assert_eq!(timer.paused_instant, None); 591 592 for _ in 0..10 { 593 if let Err(e) = timer.pause() { 594 panic!("pausing error: {e}"); 595 } 596 // State: transition started -> paused 597 assert!(timer.started_instant < timer.paused_instant); 598 assert!(timer.started_instant.is_some()); 599 assert!(timer.paused_instant.is_some()); 600 601 if let Err(e) = timer._transition() { 602 panic!("transitioning error: {e}"); 603 }; 604 605 // means the transition passed sucessfully 606 // State: Paused 607 assert_eq!(timer.started_instant, None); 608 assert!(timer.paused_instant.is_some()); 609 610 if let Err(e) = timer.start() { 611 panic!("starting error: {e}"); 612 } 613 614 // State: transition paused -> started 615 assert!(timer.started_instant > timer.paused_instant); 616 assert!(timer.started_instant.is_some()); 617 assert!(timer.paused_instant.is_some()); 618 619 if let Err(e) = timer._transition() { 620 panic!("transitioning error: {e}"); 621 }; 622 623 // means the transition passed sucessfully 624 // State: Started 625 assert!(timer.started_instant.is_some()); 626 assert_eq!(timer.paused_instant, None); 627 } 628 } 629 630 #[test] 631 #[ignore] 632 fn start_stop_elapsed_time_cycles() { 633 let mut timer = Timer::new(); 634 635 // Start timer 636 if let Err(e) = timer.start() { 637 panic!("starting error: {e}"); 638 } 639 640 // add 1s to started/working 641 sleep(Duration::new(1, 0)); 642 643 // verify started instant 644 assert_eq!( 645 timer 646 .started_instant 647 .expect("Not Elapsed") 648 .elapsed() 649 .as_secs(), 650 Duration::new(1, 0).as_secs() 651 ); 652 653 for cycle in 0..10 { 654 if let Err(e) = timer.pause() { 655 panic!("pausing error: {e}"); 656 } 657 658 if let Err(e) = timer._transition() { 659 panic!("transitioning error: {e}"); 660 }; 661 662 // Once the transition to paused the started time goes into accumulated_timer 663 assert_eq!( 664 timer.accumulated_timer.as_secs(), 665 Duration::new(cycle + 1, 0).as_secs() 666 ); 667 668 // Add 1s to paused 669 sleep(Duration::new(1, 0)); 670 671 // verify paused instant 672 assert_eq!( 673 timer 674 .paused_instant 675 .expect("Not Elapsed") 676 .elapsed() 677 .as_secs(), 678 Duration::new(1, 0).as_secs() 679 ); 680 681 if let Err(e) = timer.start() { 682 panic!("starting error: {e}"); 683 } 684 685 if let Err(e) = timer._transition() { 686 panic!("transitioning error: {e}"); 687 }; 688 689 // Once the transition to (re)started time goes into accumulated_paused 690 assert_eq!( 691 timer.accumulated_paused.as_secs(), 692 Duration::new(cycle + 1, 0).as_secs() 693 ); 694 695 // add 1s to started/working 696 sleep(Duration::new(1, 0)); 697 698 // verify started instant 699 assert_eq!( 700 timer 701 .started_instant 702 .expect("Not Elapsed") 703 .elapsed() 704 .as_secs(), 705 Duration::new(1, 0).as_secs() 706 ); 707 } 708 } 709 710 #[test] 711 #[ignore] 712 fn get_set_timer() { 713 let mut timer = Timer::new(); 714 let mut timer_data = timer.get_timer(); 715 716 assert_eq!("idle", timer_data.0); 717 assert_eq!(Duration::from_secs(0), timer_data.1); 718 assert_eq!(Duration::from_secs(0), timer_data.2); 719 720 if let Err(e) = timer.start_pause() { 721 panic!("starting error: {e}"); 722 } 723 724 // add 1s to started/working 725 sleep(Duration::new(1, 0)); 726 727 timer_data = timer.get_timer(); 728 729 assert_eq!("started", timer_data.0); 730 assert_eq!(Duration::from_secs(1).as_secs(), timer_data.1.as_secs()); 731 assert_eq!(Duration::from_secs(0).as_secs(), timer_data.2.as_secs()); 732 733 if let Err(e) = timer.start_pause() { 734 panic!("starting error: {e}"); 735 } 736 737 // add 1s to paused 738 sleep(Duration::new(1, 0)); 739 740 timer_data = timer.get_timer(); 741 742 assert_eq!("paused", timer_data.0); 743 assert_eq!(Duration::from_secs(1).as_secs(), timer_data.1.as_secs()); 744 assert_eq!(Duration::from_secs(1).as_secs(), timer_data.2.as_secs()); 745 746 // Now that we tested get_timer we will set_timer and reset it 747 748 if let Err(e) = timer.import_timer("paused•2•2".to_string()) { 749 panic!("set_timer error: {e}"); 750 } 751 752 if let Err(e) = timer.set_timer(("idle", Duration::from_secs(2), Duration::from_secs(2))) { 753 panic!("set_timer error: {e}"); 754 } 755 756 timer_data = timer.get_timer(); 757 758 assert_eq!("idle", timer_data.0); 759 assert_eq!(Duration::from_secs(2).as_secs(), timer_data.1.as_secs()); 760 assert_eq!(Duration::from_secs(2).as_secs(), timer_data.2.as_secs()); 761 762 assert_eq!(timer.state, State::Idle); 763 } 764 765 #[test] 766 #[ignore] 767 fn export_import_timer() { 768 let mut timer = Timer::new(); 769 770 if let Err(e) = timer.start_pause() { 771 panic!("starting error: {e}"); 772 } 773 774 // add 1s to started/working 775 sleep(Duration::new(1, 0)); 776 777 assert_eq!("started•1•0", timer.export_timer()); 778 779 if let Err(e) = timer.start_pause() { 780 panic!("starting error: {e}"); 781 } 782 783 // add 1s to paused 784 sleep(Duration::new(1, 0)); 785 786 assert_eq!("paused•1•1", timer.export_timer()); 787 788 // Now that we played for testing with the timer we will import and reset it 789 790 if let Err(e) = timer.import_timer("paused•2•2".to_string()) { 791 panic!("set_timer error: {e}"); 792 } 793 794 assert_eq!(timer.state, State::Idle); 795 assert_eq!("idle•2•2", timer.export_timer()); 796 } 797 }