#![cfg(feature = "simple")]
use bincode::error::{DecodeError, EncodeError};
use std::{path::Path, time::Duration};
const SECS_PER_MINUTE: f32 = 60.;
const ROWS_PER_BEAT: f32 = 8.;
const PREFIX: &str = "rocket";
pub fn print_msg(prefix: &str, msg: &str) {
eprintln!("{prefix}: {msg}");
}
pub fn print_errors(prefix: &str, error: &dyn std::error::Error) {
eprintln!("{prefix}: {error}");
let mut error = error.source();
while let Some(e) = error {
eprintln!(" Caused by: {e}");
error = e.source();
}
}
#[derive(Debug, Copy, Clone)]
pub enum Event {
Seek(Duration),
Pause(bool),
NotConnected,
}
pub struct Rocket<P: AsRef<Path>> {
path: P,
bps: f32,
row: f32,
#[cfg(not(feature = "player"))]
tracker_row: u32,
#[cfg(not(feature = "player"))]
connected: bool, #[cfg(not(feature = "player"))]
connection_attempted: std::time::Instant,
#[cfg(not(feature = "player"))]
rocket: Option<crate::RocketClient>, #[cfg(feature = "player")]
rocket: crate::RocketPlayer,
}
impl<P: AsRef<Path>> Rocket<P> {
pub fn new(path: P, bpm: f32) -> Result<Self, DecodeError> {
#[cfg(not(feature = "player"))]
let rocket = Self::connect().ok();
#[cfg(feature = "player")]
let rocket = {
let mut file = match std::fs::File::open(&path) {
Ok(file) => file,
Err(e) => {
print_msg(
PREFIX,
&format!("Failed to open {}", path.as_ref().display()),
);
print_errors(PREFIX, &e);
return Err(DecodeError::Io {
inner: e,
additional: 0,
});
}
};
let tracks = match bincode::decode_from_std_read(&mut file, bincode::config::standard())
{
Ok(tracks) => tracks,
Err(e) => {
print_msg(
PREFIX,
&format!("Failed to read {}", path.as_ref().display()),
);
print_errors(PREFIX, &e);
return Err(e);
}
};
crate::RocketPlayer::new(tracks)
};
Ok(Self {
path,
bps: bpm / SECS_PER_MINUTE,
row: 0.,
#[cfg(not(feature = "player"))]
tracker_row: 0,
#[cfg(not(feature = "player"))]
connected: rocket.is_some(),
#[cfg(not(feature = "player"))]
connection_attempted: std::time::Instant::now(),
rocket,
})
}
pub fn get_value(&mut self, track: &str) -> f32 {
#[cfg(not(feature = "player"))]
let track = match self
.rocket
.as_mut()
.and_then(|rocket| rocket.get_track_mut(track).ok())
{
Some(track) => track,
None => {
self.connected = false;
return 0.;
}
};
#[cfg(feature = "player")]
let track = self.rocket.get_track(track).unwrap_or_else(|| {
print_msg(
PREFIX,
&format!(
"Track {} doesn't exist in {}",
track,
self.path.as_ref().display()
),
);
panic!("{}: Can't recover", PREFIX);
});
track.get_value(self.row)
}
pub fn set_time(&mut self, time: &Duration) {
let beat = time.as_secs_f32() * self.bps;
self.row = beat * ROWS_PER_BEAT;
#[cfg(not(feature = "player"))]
{
let row = (self.row + 0.5) as u32;
if self.connected && row != self.tracker_row {
match self.rocket.as_mut().map(|rocket| rocket.set_row(row)) {
Some(Ok(())) => self.tracker_row = row,
Some(Err(ref e)) => {
print_errors(PREFIX, e);
self.connected = false;
}
None => self.connected = false,
}
}
}
}
pub fn poll_events(&mut self) -> Result<Option<Event>, EncodeError> {
#[cfg(not(feature = "player"))]
loop {
if !self.connected || self.rocket.is_none() {
if self.connection_attempted.elapsed() < Duration::from_secs(1) {
return Ok(Some(Event::NotConnected));
}
self.connection_attempted = std::time::Instant::now();
match Self::connect() {
Ok(rocket) => {
self.rocket = Some(rocket);
self.connected = true;
}
Err(_) => return Ok(Some(Event::NotConnected)),
}
}
match self.rocket.as_mut().map(|rocket| rocket.poll_events()) {
Some(Ok(Some(event))) => {
let handled = match event {
crate::client::Event::SetRow(row) => {
self.tracker_row = row;
let beat = row as f32 / ROWS_PER_BEAT;
Event::Seek(Duration::from_secs_f32(beat / self.bps))
}
crate::client::Event::Pause(flag) => Event::Pause(flag),
crate::client::Event::SaveTracks => {
self.save_tracks()?;
continue;
}
};
return Ok(Some(handled));
}
Some(Ok(None)) => return Ok(None),
Some(Err(ref e)) => {
print_errors(PREFIX, e);
self.connected = false;
}
None => self.connected = false,
}
}
#[cfg(feature = "player")]
Ok(None)
}
pub fn save_tracks(&self) -> Result<(), EncodeError> {
#[cfg(not(feature = "player"))]
if let Some(rocket) = &self.rocket {
let open_result = std::fs::OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(&self.path);
let mut file = match open_result {
Ok(file) => file,
Err(e) => {
print_msg(
PREFIX,
&format!("Failed to open {}", self.path.as_ref().display()),
);
print_errors(PREFIX, &e);
return Err(EncodeError::Io { inner: e, index: 0 });
}
};
let tracks = rocket.save_tracks();
match bincode::encode_into_std_write(tracks, &mut file, bincode::config::standard()) {
Ok(_) => {
print_msg(
PREFIX,
&format!("Tracks saved to {}", self.path.as_ref().display()),
);
Ok(())
}
Err(e) => {
print_msg(
PREFIX,
&format!("Failed to write to {}", self.path.as_ref().display()),
);
print_errors(PREFIX, &e);
Err(e)
}
}
} else {
print_msg(
PREFIX,
&format!(
"Did not connect, not able to save {}",
self.path.as_ref().display()
),
);
Ok(())
}
#[cfg(feature = "player")]
Ok(())
}
#[cfg(not(feature = "player"))]
fn connect() -> Result<crate::RocketClient, crate::client::Error> {
print_msg(PREFIX, "Connecting...");
crate::RocketClient::new()
}
}
#[cfg(feature = "player")]
impl Rocket<&str> {
pub fn from_std_read<R: std::io::Read>(read: &mut R, bpm: f32) -> Result<Self, DecodeError> {
let tracks = bincode::decode_from_std_read(read, bincode::config::standard())?;
let rocket = crate::RocketPlayer::new(tracks);
Ok(Self {
path: "release",
bps: bpm / SECS_PER_MINUTE,
row: 0.,
rocket,
})
}
}