rustimate-service 0.1.0

Application logic used by the rustimate server
use crate::files::FileService;

use rustimate_core::member::{Member, MemberRole};
use rustimate_core::poll::{Poll, PollStatus, Vote};
use rustimate_core::session::EstimateSession;

use anyhow::Result;
use std::sync::{Arc, RwLock};
use uuid::Uuid;

#[derive(Clone, Debug)]
pub struct SessionService {
  files: Arc<RwLock<FileService>>,
  log: slog::Logger
}

impl SessionService {
  pub fn new(files: FileService, logger: &slog::Logger) -> Self {
    let log = logger.new(slog::o!("service" => "session"));
    Self {
      files: Arc::new(RwLock::new(files)),
      log
    }
  }

  pub fn read_session(&self, key: &str) -> Result<EstimateSession> {
    slog::debug!(&self.log, "Loading session [{}]", key);
    let p = format!("session/{}/session.json", key);
    let f = self.files.read().expect("Cannot lock FileService for read");
    if f.exists(&p) {
      f.read_json(&p)
    } else {
      Err(anyhow::anyhow!("No session found with key [{}]", key))
    }
  }

  pub fn write_session(&self, session: &EstimateSession) -> Result<()> {
    slog::debug!(&self.log, "Writing session [{}]", session.key());
    let x = self.files.write().expect("Cannot lock FileService for read");
    x.create_dir_if_needed(&format!("session/{}", session.key()))?;
    x.write_json(session, &format!("session/{}/session.json", session.key()))
  }

  pub fn read_members(&self, key: &str) -> Result<Vec<Member>> {
    slog::debug!(&self.log, "Loading members for session [{}]", key);
    let p = format!("session/{}/members.json", key);
    let f = self.files.read().expect("Cannot lock FileService for read");
    if f.exists(&p) {
      f.read_json(&p)
    } else {
      Ok(vec![])
    }
  }

  pub fn update_member(&self, session_key: &str, user_id: Uuid, name: String, role: Option<MemberRole>) -> Result<Member> {
    let mut current = self.read_members(session_key)?;
    let ret = match current.iter_mut().find(|x| x.user_id() == &user_id) {
      Some(m) => {
        m.set_name(name);
        let _ = role.map(|r| m.set_role(r));
        Ok(m.clone())
      }
      None => {
        let m = Member::new(user_id, name, role.unwrap_or(MemberRole::Participant));
        current.push(m.clone());
        Ok(m)
      }
    };
    self.write_members(session_key, current)?;
    ret
  }

  pub fn write_members(&self, key: &str, vm: Vec<Member>) -> Result<()> {
    let p = format!("session/{}/members.json", key);
    self.files.write().expect("Cannot lock FileService for write").write_json(vm, &p)
  }

  pub fn read_polls(&self, key: &str) -> Result<Vec<Poll>> {
    slog::debug!(&self.log, "Loading polls for session [{}]", key);
    let p = format!("session/{}/polls.json", key);
    let f = self.files.read().expect("Cannot lock FileService for read");
    if f.exists(&p) {
      f.read_json(&p)
    } else {
      Ok(vec![])
    }
  }

  pub fn update_poll(
    &self, session_key: &str, poll_id: Uuid, title: Option<String>, status: Option<PollStatus>, author_id: Uuid
  ) -> Result<Poll> {
    let mut current = self.read_polls(session_key)?;
    let ret = match current.iter_mut().find(|x| x.id() == &poll_id) {
      Some(p) => {
        p.set_title(title.unwrap_or_else(|| p.title().into()));
        p.set_status(status.unwrap_or_else(|| p.status().clone()));
        Ok(p.clone())
      }
      None => {
        let p = Poll::new(
          poll_id,
          current.len() as u32,
          author_id,
          title.unwrap_or_else(|| "New poll".into()),
          status.unwrap_or(PollStatus::Pending)
        );
        current.push(p.clone());
        Ok(p)
      }
    };
    self.write_polls(session_key, current)?;
    ret
  }

  pub fn write_polls(&self, key: &str, vm: Vec<Poll>) -> Result<()> {
    let p = format!("session/{}/polls.json", key);
    self.files.write().expect("Cannot lock FileService for read").write_json(vm, &p)
  }

  pub fn read_votes(&self, key: &str) -> Result<Vec<Vote>> {
    slog::debug!(&self.log, "Loading votes for session [{}]", key);
    let p = format!("session/{}/votes.json", key);
    let f = self.files.read().expect("Cannot lock FileService for read");
    if f.exists(&p) {
      f.read_json(&p)
    } else {
      Ok(vec![])
    }
  }

  pub fn update_vote(&self, key: &str, vote: Vote) -> Result<Vote> {
    let mut current = self.read_votes(key)?;
    let ret = match current
      .iter_mut()
      .find(|x| x.poll_id() == vote.poll_id() && x.user_id() == vote.user_id())
    {
      Some(v) => {
        v.set_choice(vote.choice().into());
        Ok(v.clone())
      }
      None => {
        current.push(vote.clone());
        Ok(vote)
      }
    };
    self.write_votes(key, current)?;
    ret
  }

  pub fn write_votes(&self, key: &str, vm: Vec<Vote>) -> Result<()> {
    let p = format!("session/{}/votes.json", key);
    self.files.write().expect("Cannot lock FileService for read").write_json(vm, &p)
  }
}