<commit_before>use std::fs::File;
use std::path::PathBuf;
use std::io;
use std::io::Write;
use std::fmt::Debug;
use retry_conf::RetryConf;
use rustc_serialize::Encodable;
use record::FluentError;
use forwardable::forward::Forward;
use std::fs::OpenOptions;
use record::Record;

fn ensure_file_with_wca(path: PathBuf) -> Result<File, io::Error> {
    let file = try!(OpenOptions::new().write(true).create(true).append(true).open(path));
    Ok(file)
}

pub fn maybe_write_record<T>(conf: &RetryConf,
                             record: Record<T>,
                             err: FluentError)
                             -> Result<(), FluentError>
    where T: Encodable + Debug
{
    let store_needed = conf.clone().need_to_store();
    let store_path = conf.clone().store_path();
    if store_needed {
        match ensure_file_with_wca(store_path.clone().unwrap()) {
            Ok(mut f) => {
                let mut w = Vec::new();
                write!(&mut w, "{}", record.dump()).unwrap();
                try!(f.write(&w));
                try!(f.sync_data());
                return Err(FluentError::FileStored(format!("stored buffer in specified file: \
                                                            {:?}",
                                                           store_path.unwrap())));
            }
            Err(e) => return Err(From::from(e)),
        }
        Ok(())
    } else {
        Err(err)
    }
}

pub fn maybe_write_records<T>(conf: &RetryConf,
                              forward: Forward<T>,
                              err: FluentError)
                              -> Result<(), FluentError>
    where T: Encodable + Debug
{
    let store_needed = conf.clone().need_to_store();
    let store_path = conf.clone().store_path();
    if store_needed {
        match ensure_file_with_wca(store_path.clone().unwrap()) {
            Ok(mut f) => {
                let mut w = Vec::new();
                write!(&mut w, "{}", forward.dump()).unwrap();
                try!(f.write(&w));
                try!(f.sync_data());
                return Err(FluentError::FileStored(format!("stored buffer in specified file: \
                                                            {:?}",
                                                           store_path.unwrap())));
            }
            Err(e) => return Err(From::from(e)),
        }
        Ok(())
    } else {
        Err(err)
    }
}

#[cfg(test)]
mod tests {
    extern crate tempdir;
    use super::*;
    use time;
    use std::collections::HashMap;
    use self::tempdir::TempDir;
    use record::Record;
    use retry_conf::RetryConf;
    use record::FluentError;
    use forwardable::forward::Forward;

    #[test]
    fn test_write_record() {
        let tag = "fruently".to_string();
        let time = time::now();
        let mut obj: HashMap<String, String> = HashMap::new();
        obj.insert("name".to_string(), "fruently".to_string());
        let record = Record::new(tag.clone(), time, obj.clone());
        let tmp = TempDir::new("fruently").unwrap().into_path().join("buffer");
        let conf = RetryConf::new().store_file(tmp.clone());
        assert!(maybe_write_record(&conf, record, FluentError::Dummy("dummy".to_string()))
            .is_err());
        assert!(tmp.exists())
    }

    #[test]
    fn test_write_record_2_times() {
        let tag = "fruently".to_string();
        let time = time::now();
        let mut obj: HashMap<String, String> = HashMap::new();
        obj.insert("name".to_string(), "fruently".to_string());
        let record = Record::new(tag.clone(), time, obj.clone());
        let tmp = TempDir::new("fruently").unwrap().into_path().join("buffer");
        let conf = RetryConf::new().store_file(tmp.clone());
        assert!(maybe_write_record(&conf, record, FluentError::Dummy("dummy".to_string()))
            .is_err());
        assert!(tmp.exists());
        let mut obj2: HashMap<String, String> = HashMap::new();
        obj2.insert("name2".to_string(), "fruently2".to_string());
        let record2 = Record::new(tag.clone(), time, obj2.clone());
        let conf2 = RetryConf::new().store_file(tmp.clone());
        assert!(maybe_write_record(&conf2, record2, FluentError::Dummy("dummy".to_string()))
            .is_err());
        assert!(tmp.exists())
    }

    #[test]
    fn test_write_records() {
        let tag = "fruently".to_string();
        let mut obj1: HashMap<String, String> = HashMap::new();
        obj1.insert("hey".to_string(), "Rust with forward mode!".to_string());
        let mut obj2: HashMap<String, String> = HashMap::new();
        obj2.insert("yeah".to_string(), "Also sent together!".to_string());
        let time = time::now().to_timespec().sec;
        let entry = (time, obj1);
        let entry2 = (time, obj2);
        let entries = vec![(entry), (entry2)];
        let forward = Forward::new(tag, entries);
        let tmp = TempDir::new("fruently").unwrap().into_path().join("buffer");
        let conf = RetryConf::new().store_file(tmp.clone());
        assert!(maybe_write_records(&conf, forward, FluentError::Dummy("dummy".to_string()))
            .is_err());
        assert!(tmp.exists())
    }
}
<commit_msg>Remove unreachable code<commit_after>use std::fs::File;
use std::path::PathBuf;
use std::io;
use std::io::Write;
use std::fmt::Debug;
use retry_conf::RetryConf;
use rustc_serialize::Encodable;
use record::FluentError;
use forwardable::forward::Forward;
use std::fs::OpenOptions;
use record::Record;

fn ensure_file_with_wca(path: PathBuf) -> Result<File, io::Error> {
    let file = try!(OpenOptions::new().write(true).create(true).append(true).open(path));
    Ok(file)
}

pub fn maybe_write_record<T>(conf: &RetryConf,
                             record: Record<T>,
                             err: FluentError)
                             -> Result<(), FluentError>
    where T: Encodable + Debug
{
    let store_needed = conf.clone().need_to_store();
    let store_path = conf.clone().store_path();
    if store_needed {
        match ensure_file_with_wca(store_path.clone().unwrap()) {
            Ok(mut f) => {
                let mut w = Vec::new();
                write!(&mut w, "{}", record.dump()).unwrap();
                try!(f.write(&w));
                try!(f.sync_data());
                return Err(FluentError::FileStored(format!("stored buffer in specified file: \
                                                            {:?}",
                                                           store_path.unwrap())));
            }
            Err(e) => return Err(From::from(e)),
        }
    } else {
        Err(err)
    }
}

pub fn maybe_write_records<T>(conf: &RetryConf,
                              forward: Forward<T>,
                              err: FluentError)
                              -> Result<(), FluentError>
    where T: Encodable + Debug
{
    let store_needed = conf.clone().need_to_store();
    let store_path = conf.clone().store_path();
    if store_needed {
        match ensure_file_with_wca(store_path.clone().unwrap()) {
            Ok(mut f) => {
                let mut w = Vec::new();
                write!(&mut w, "{}", forward.dump()).unwrap();
                try!(f.write(&w));
                try!(f.sync_data());
                return Err(FluentError::FileStored(format!("stored buffer in specified file: \
                                                            {:?}",
                                                           store_path.unwrap())));
            }
            Err(e) => return Err(From::from(e)),
        }
    } else {
        Err(err)
    }
}

#[cfg(test)]
mod tests {
    extern crate tempdir;
    use super::*;
    use time;
    use std::collections::HashMap;
    use self::tempdir::TempDir;
    use record::Record;
    use retry_conf::RetryConf;
    use record::FluentError;
    use forwardable::forward::Forward;

    #[test]
    fn test_write_record() {
        let tag = "fruently".to_string();
        let time = time::now();
        let mut obj: HashMap<String, String> = HashMap::new();
        obj.insert("name".to_string(), "fruently".to_string());
        let record = Record::new(tag.clone(), time, obj.clone());
        let tmp = TempDir::new("fruently").unwrap().into_path().join("buffer");
        let conf = RetryConf::new().store_file(tmp.clone());
        assert!(maybe_write_record(&conf, record, FluentError::Dummy("dummy".to_string()))
            .is_err());
        assert!(tmp.exists())
    }

    #[test]
    fn test_write_record_2_times() {
        let tag = "fruently".to_string();
        let time = time::now();
        let mut obj: HashMap<String, String> = HashMap::new();
        obj.insert("name".to_string(), "fruently".to_string());
        let record = Record::new(tag.clone(), time, obj.clone());
        let tmp = TempDir::new("fruently").unwrap().into_path().join("buffer");
        let conf = RetryConf::new().store_file(tmp.clone());
        assert!(maybe_write_record(&conf, record, FluentError::Dummy("dummy".to_string()))
            .is_err());
        assert!(tmp.exists());
        let mut obj2: HashMap<String, String> = HashMap::new();
        obj2.insert("name2".to_string(), "fruently2".to_string());
        let record2 = Record::new(tag.clone(), time, obj2.clone());
        let conf2 = RetryConf::new().store_file(tmp.clone());
        assert!(maybe_write_record(&conf2, record2, FluentError::Dummy("dummy".to_string()))
            .is_err());
        assert!(tmp.exists())
    }

    #[test]
    fn test_write_records() {
        let tag = "fruently".to_string();
        let mut obj1: HashMap<String, String> = HashMap::new();
        obj1.insert("hey".to_string(), "Rust with forward mode!".to_string());
        let mut obj2: HashMap<String, String> = HashMap::new();
        obj2.insert("yeah".to_string(), "Also sent together!".to_string());
        let time = time::now().to_timespec().sec;
        let entry = (time, obj1);
        let entry2 = (time, obj2);
        let entries = vec![(entry), (entry2)];
        let forward = Forward::new(tag, entries);
        let tmp = TempDir::new("fruently").unwrap().into_path().join("buffer");
        let conf = RetryConf::new().store_file(tmp.clone());
        assert!(maybe_write_records(&conf, forward, FluentError::Dummy("dummy".to_string()))
            .is_err());
        assert!(tmp.exists())
    }
}
<|endoftext|>


<commit_before>use std::sync::mpsc::{sync_channel, Receiver, SyncSender};

use crate::*;

static ID_GEN: AtomicUsize = AtomicUsize::new(0);

/// An event that happened to a key that a subscriber is interested in.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Event {
    /// A new complete (key, value) pair
    Insert(Arc<[u8]>, IVec),
    /// A deleted key
    Remove(Arc<[u8]>),
}

impl Event {
    /// Return a reference to the key that this `Event` refers to
    pub fn key(&self) -> &[u8] {
        match self {
            Event::Insert(k, ..) | Event::Remove(k) => &*k,
        }
    }
}

impl Clone for Event {
    fn clone(&self) -> Self {
        use self::Event::*;

        match self {
            Insert(k, v) => Insert(k.clone(), v.clone()),
            Remove(k) => Remove(k.clone()),
        }
    }
}

type Senders = Vec<(usize, SyncSender<OneShot<Event>>)>;

/// A subscriber listening on a specified prefix
pub struct Subscriber {
    id: usize,
    rx: Receiver<OneShot<Event>>,
    home: Arc<RwLock<Senders>>,
}

impl Drop for Subscriber {
    fn drop(&mut self) {
        let mut w_senders = self.home.write();
        w_senders.retain(|(id, _)| *id != self.id);
    }
}

impl Iterator for Subscriber {
    type Item = Event;

    fn next(&mut self) -> Option<Event> {
        loop {
            let future_rx = self.rx.recv().ok()?;
            match future_rx.wait() {
                Some(event) => return Some(event),
                None => continue,
            }
        }
    }
}

#[derive(Debug, Default)]
pub(crate) struct Subscriptions {
    watched: RwLock<BTreeMap<Vec<u8>, Arc<RwLock<Senders>>>>,
}

impl Subscriptions {
    pub(crate) fn register(&self, prefix: &[u8]) -> Subscriber {
        let r_mu = {
            let r_mu = self.watched.read();
            if r_mu.contains_key(prefix) {
                r_mu
            } else {
                drop(r_mu);
                let mut w_mu = self.watched.write();
                if !w_mu.contains_key(prefix) {
                    let old = w_mu
                        .insert(prefix.to_vec(), Arc::new(RwLock::new(vec![])));
                    assert!(old.is_none());
                }
                drop(w_mu);
                self.watched.read()
            }
        };

        let (tx, rx) = sync_channel(1024);

        let arc_senders = &r_mu[prefix];
        let mut w_senders = arc_senders.write();

        let id = ID_GEN.fetch_add(1, Relaxed);

        w_senders.push((id, tx));

        Subscriber { id, rx, home: arc_senders.clone() }
    }

    pub(crate) fn reserve<R: AsRef<[u8]>>(
        &self,
        key: R,
    ) -> Option<ReservedBroadcast> {
        let r_mu = self.watched.read();
        let prefixes = r_mu.iter().filter(|(k, _)| key.as_ref().starts_with(k));

        let mut subscribers = vec![];

        for (_, subs_rwl) in prefixes {
            let subs = subs_rwl.read();

            for (_id, sender) in subs.iter() {
                let (tx, rx) = OneShot::pair();
                if sender.send(rx).is_err() {
                    continue;
                }
                subscribers.push(tx);
            }
        }

        if subscribers.is_empty() {
            None
        } else {
            Some(ReservedBroadcast { subscribers })
        }
    }
}

pub(crate) struct ReservedBroadcast {
    subscribers: Vec<OneShotFiller<Event>>,
}

impl ReservedBroadcast {
    pub fn complete(self, event: Event) {
        let len = self.subscribers.len();
        let mut iter = self.subscribers.into_iter();

        let mut sent = 0;

        while sent + 1 < len {
            sent += 1;
            let tx = iter.next().unwrap();
            tx.fill(event.clone());
        }

        if len != 0 {
            let tx = iter.next().unwrap();
            tx.fill(event);
        }
    }
}

#[test]
fn basic_subscription() {
    let subs = Subscriptions::default();

    let mut s2 = subs.register(&[0]);
    let mut s3 = subs.register(&[0, 1]);
    let mut s4 = subs.register(&[1, 2]);

    let r1 = subs.reserve(b"awft");
    assert!(r1.is_none());

    let mut s1 = subs.register(&[]);

    let k2: Arc<[u8]> = vec![].into();
    let r2 = subs.reserve(&k2).unwrap();
    r2.complete(Event::Insert(k2.clone(), IVec::from(k2.clone())));

    let k3: Arc<[u8]> = vec![0].into();
    let r3 = subs.reserve(&k3).unwrap();
    r3.complete(Event::Insert(k3.clone(), IVec::from(k3.clone())));

    let k4: Arc<[u8]> = vec![0, 1].into();
    let r4 = subs.reserve(&k4).unwrap();
    r4.complete(Event::Remove(k4.clone()));

    let k5: Arc<[u8]> = vec![0, 1, 2].into();
    let r5 = subs.reserve(&k5).unwrap();
    r5.complete(Event::Insert(k5.clone(), IVec::from(k5.clone())));

    let k6: Arc<[u8]> = vec![1, 1, 2].into();
    let r6 = subs.reserve(&k6).unwrap();
    r6.complete(Event::Remove(k6.clone()));

    let k7: Arc<[u8]> = vec![1, 1, 2].into();
    let r7 = subs.reserve(&k7).unwrap();
    drop(r7);

    let k8: Arc<[u8]> = vec![1, 2, 2].into();
    let r8 = subs.reserve(&k8).unwrap();
    r8.complete(Event::Insert(k8.clone(), IVec::from(k8.clone())));

    assert_eq!(s1.next().unwrap().key(), &*k2);
    assert_eq!(s1.next().unwrap().key(), &*k3);
    assert_eq!(s1.next().unwrap().key(), &*k4);
    assert_eq!(s1.next().unwrap().key(), &*k5);
    assert_eq!(s1.next().unwrap().key(), &*k6);
    assert_eq!(s1.next().unwrap().key(), &*k8);

    assert_eq!(s2.next().unwrap().key(), &*k3);
    assert_eq!(s2.next().unwrap().key(), &*k4);
    assert_eq!(s2.next().unwrap().key(), &*k5);

    assert_eq!(s3.next().unwrap().key(), &*k4);
    assert_eq!(s3.next().unwrap().key(), &*k5);

    assert_eq!(s4.next().unwrap().key(), &*k8);
}
<commit_msg>auto-tune subscription effort<commit_after>use std::sync::{
    atomic::{AtomicBool, Ordering::Relaxed},
    mpsc::{sync_channel, Receiver, SyncSender},
};

use crate::*;

static ID_GEN: AtomicUsize = AtomicUsize::new(0);

/// An event that happened to a key that a subscriber is interested in.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Event {
    /// A new complete (key, value) pair
    Insert(Arc<[u8]>, IVec),
    /// A deleted key
    Remove(Arc<[u8]>),
}

impl Event {
    /// Return a reference to the key that this `Event` refers to
    pub fn key(&self) -> &[u8] {
        match self {
            Event::Insert(k, ..) | Event::Remove(k) => &*k,
        }
    }
}

impl Clone for Event {
    fn clone(&self) -> Self {
        use self::Event::*;

        match self {
            Insert(k, v) => Insert(k.clone(), v.clone()),
            Remove(k) => Remove(k.clone()),
        }
    }
}

type Senders = Vec<(usize, SyncSender<OneShot<Event>>)>;

/// A subscriber listening on a specified prefix
pub struct Subscriber {
    id: usize,
    rx: Receiver<OneShot<Event>>,
    home: Arc<RwLock<Senders>>,
}

impl Drop for Subscriber {
    fn drop(&mut self) {
        let mut w_senders = self.home.write();
        w_senders.retain(|(id, _)| *id != self.id);
    }
}

impl Iterator for Subscriber {
    type Item = Event;

    fn next(&mut self) -> Option<Event> {
        loop {
            let future_rx = self.rx.recv().ok()?;
            match future_rx.wait() {
                Some(event) => return Some(event),
                None => continue,
            }
        }
    }
}

#[derive(Debug, Default)]
pub(crate) struct Subscriptions {
    watched: RwLock<BTreeMap<Vec<u8>, Arc<RwLock<Senders>>>>,
    ever_used: AtomicBool,
}

impl Subscriptions {
    pub(crate) fn register(&self, prefix: &[u8]) -> Subscriber {
        self.ever_used.store(true, Relaxed);
        let r_mu = {
            let r_mu = self.watched.read();
            if r_mu.contains_key(prefix) {
                r_mu
            } else {
                drop(r_mu);
                let mut w_mu = self.watched.write();
                if !w_mu.contains_key(prefix) {
                    let old = w_mu
                        .insert(prefix.to_vec(), Arc::new(RwLock::new(vec![])));
                    assert!(old.is_none());
                }
                drop(w_mu);
                self.watched.read()
            }
        };

        let (tx, rx) = sync_channel(1024);

        let arc_senders = &r_mu[prefix];
        let mut w_senders = arc_senders.write();

        let id = ID_GEN.fetch_add(1, Relaxed);

        w_senders.push((id, tx));

        Subscriber { id, rx, home: arc_senders.clone() }
    }

    pub(crate) fn reserve<R: AsRef<[u8]>>(
        &self,
        key: R,
    ) -> Option<ReservedBroadcast> {
        if !self.ever_used.load(Relaxed) {
            return None;
        }

        let r_mu = self.watched.read();
        let prefixes = r_mu.iter().filter(|(k, _)| key.as_ref().starts_with(k));

        let mut subscribers = vec![];

        for (_, subs_rwl) in prefixes {
            let subs = subs_rwl.read();

            for (_id, sender) in subs.iter() {
                let (tx, rx) = OneShot::pair();
                if sender.send(rx).is_err() {
                    continue;
                }
                subscribers.push(tx);
            }
        }

        if subscribers.is_empty() {
            None
        } else {
            Some(ReservedBroadcast { subscribers })
        }
    }
}

pub(crate) struct ReservedBroadcast {
    subscribers: Vec<OneShotFiller<Event>>,
}

impl ReservedBroadcast {
    pub fn complete(self, event: Event) {
        let len = self.subscribers.len();
        let mut iter = self.subscribers.into_iter();

        let mut sent = 0;

        while sent + 1 < len {
            sent += 1;
            let tx = iter.next().unwrap();
            tx.fill(event.clone());
        }

        if len != 0 {
            let tx = iter.next().unwrap();
            tx.fill(event);
        }
    }
}

#[test]
fn basic_subscription() {
    let subs = Subscriptions::default();

    let mut s2 = subs.register(&[0]);
    let mut s3 = subs.register(&[0, 1]);
    let mut s4 = subs.register(&[1, 2]);

    let r1 = subs.reserve(b"awft");
    assert!(r1.is_none());

    let mut s1 = subs.register(&[]);

    let k2: Arc<[u8]> = vec![].into();
    let r2 = subs.reserve(&k2).unwrap();
    r2.complete(Event::Insert(k2.clone(), IVec::from(k2.clone())));

    let k3: Arc<[u8]> = vec![0].into();
    let r3 = subs.reserve(&k3).unwrap();
    r3.complete(Event::Insert(k3.clone(), IVec::from(k3.clone())));

    let k4: Arc<[u8]> = vec![0, 1].into();
    let r4 = subs.reserve(&k4).unwrap();
    r4.complete(Event::Remove(k4.clone()));

    let k5: Arc<[u8]> = vec![0, 1, 2].into();
    let r5 = subs.reserve(&k5).unwrap();
    r5.complete(Event::Insert(k5.clone(), IVec::from(k5.clone())));

    let k6: Arc<[u8]> = vec![1, 1, 2].into();
    let r6 = subs.reserve(&k6).unwrap();
    r6.complete(Event::Remove(k6.clone()));

    let k7: Arc<[u8]> = vec![1, 1, 2].into();
    let r7 = subs.reserve(&k7).unwrap();
    drop(r7);

    let k8: Arc<[u8]> = vec![1, 2, 2].into();
    let r8 = subs.reserve(&k8).unwrap();
    r8.complete(Event::Insert(k8.clone(), IVec::from(k8.clone())));

    assert_eq!(s1.next().unwrap().key(), &*k2);
    assert_eq!(s1.next().unwrap().key(), &*k3);
    assert_eq!(s1.next().unwrap().key(), &*k4);
    assert_eq!(s1.next().unwrap().key(), &*k5);
    assert_eq!(s1.next().unwrap().key(), &*k6);
    assert_eq!(s1.next().unwrap().key(), &*k8);

    assert_eq!(s2.next().unwrap().key(), &*k3);
    assert_eq!(s2.next().unwrap().key(), &*k4);
    assert_eq!(s2.next().unwrap().key(), &*k5);

    assert_eq!(s3.next().unwrap().key(), &*k4);
    assert_eq!(s3.next().unwrap().key(), &*k5);

    assert_eq!(s4.next().unwrap().key(), &*k8);
}
<|endoftext|>


<commit_before>//! Resource system.
//!
//! Currently, a resource is a disk-cached object that can be hot-reloaded while you use it.
//! Resource can be serialized and deserialized as you see fit. The concept of *caching* and
//! *loading* are split in different code location so that you can easily compose both – provide
//! the loading code and ask the resource system to cache it for you.
//!
//! This great flexibility is exposed in the public interface so that the cache can be augmented
//! with user-provided objects. You might be interested in implementing `Load`, `CacheKey` – from
//! the [any-cache](https://crates.io/crates/any-cache) crate — as well as providing a type wrapper
//! over the key to access to your resource.
//!
//! # Note on keys
//!
//! If you use the resource system, your resources will be cached and accessible by their keys. The
//! key type is not enforced. Resource’s keys are typed to enable namespacing: if you have two
//! resources which ID is `34`, because the key types are different, you can safely cache the
//! resource with the ID `34` without any clashing or undefined behaviors. More in the any-cache
//! crate.

use any_cache::{Cache, HashCache};
pub use any_cache::CacheKey;
use notify::{Op, RawEvent, RecursiveMode, Watcher, raw_watcher};
use notify::op::WRITE;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt::Debug;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::channel;
use std::thread;
use std::time::{Duration, Instant};

/// Loadable object from disk.
///
/// An object can be loaded from disk if given a path it can be output a `LoadResult<_>`. It’s
/// important to note that you’re not supposed to load objects directly from this trait. Instead,
/// you should use a `Store`.
pub trait Load: 'static + Sized {
  type Key: StoreKey<Target = Self>;

  /// Load a resource. The `Store` can be used to load or declare additional resource dependencies.
  /// The result type is used to register for dependency events.
  fn load(key: &Self::Key, cache: &mut Store) -> Result<LoadResult<Self>, LoadError>;
}

/// Result of a resource loading. This type enables you to register a resource for reloading events
/// of others (dependencies). If you don’t need to run specific code on a dependency reloading, use
/// the `.into()` function to lift your return value to `LoadResult<_>`.
pub struct LoadResult<T> {
  /// The loaded object.
  pub res: T,
  /// The list of dependencies to listen for events.
  pub dependencies: Vec<PathBuf>
}

impl<T> LoadResult<T> {
  /// Return a resource along with its dependencies.
  pub fn with_dependencies(res: T, dependencies: Vec<PathBuf>) -> Self {
    LoadResult { res, dependencies }
  }
}

impl<T> From<T> for LoadResult<T> {
  fn from(res: T) -> Self {
    LoadResult {
      res,
      dependencies: Vec::new()
    }
  }
}

/// Error that might occur while loading a resource.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum LoadError {
  /// The file was not found.
  FileNotFound(PathBuf),
  /// The file wasn’t correctly parsed.
  ParseFailed(String),
  /// The file wasn’t correctly converted, even though it might have been parsed.
  ConversionFailed(String)
}

/// Resources are wrapped in this type.
pub type Res<T> = Rc<RefCell<T>>;

/// Time to await after a resource update to establish that it should be reloaded.
const UPDATE_AWAIT_TIME_MS: u64 = 1000;

/// Resource key. This type is used to adapt a key type’s target so that it can be mutably shared.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
struct RKey<K>(K);

impl<K, T> CacheKey for RKey<K> where K: CacheKey<Target = T> {
  type Target = Res<T>;
}

/// Trait used to represent keys in a resource store.
pub trait StoreKey: CacheKey + Clone + Debug {
  /// Convert from a key to its path representation.
  fn key_to_path(&self) -> PathBuf;
}

/// Resource store. Responsible for holding and presenting resources.
pub struct Store {
  // canonicalized root path of resources
  root: PathBuf,
  // resource cache
  cache: HashCache,
  // contains all metadata on resources
  metadata: HashMap<PathBuf, ResMetaData>,
  // dependencies, mapping a dependency to its observers
  dependencies: HashMap<PathBuf, PathBuf>,
  // vector of pairs (path, timestamp) giving indication on resources to reload
  dirty: Arc<Mutex<Vec<(PathBuf, Instant)>>>,
  #[allow(dead_code)]
  watcher_thread: thread::JoinHandle<()>
}

impl Store {
  /// Create a new store. The `root` represents the root directory from all the resources come from.
  pub fn new<P>(root: P) -> Result<Self, StoreError> where P: AsRef<Path> {
    let dirty: Arc<Mutex<Vec<(PathBuf, Instant)>>> = Arc::new(Mutex::new(Vec::new()));
    let dirty_ = dirty.clone();

    let root = root.as_ref().to_owned();
    let root_ = root.clone();
    let canon_root = root.canonicalize().map_err(|_| StoreError::RootDoesDotExit(root_.into()))?;
    let canon_root_ = canon_root.clone();
    let (wsx, wrx) = channel();
    let mut watcher = raw_watcher(wsx).unwrap();

    let join_handle = thread::spawn(move || {
      let _ = watcher.watch(canon_root_.clone(), RecursiveMode::Recursive);

      for event in wrx.iter() {
        match event {
          RawEvent { path: Some(ref path), op: Ok(op), .. } if op | WRITE != Op::empty() => {
            dirty_.lock().unwrap().push((path.strip_prefix(&canon_root_).unwrap().to_owned(), Instant::now()));
          },
          _ => ()
        }
      }
    });

    deb!("resource cache started and listens to file changes in {}", root.display());

    Ok(Store {
      root: canon_root,
      cache: HashCache::new(),
      metadata: HashMap::new(),
      dependencies: HashMap::new(),
      dirty: dirty,
      watcher_thread: join_handle
    })
  }

  /// Inject a new resource in the cache.
  ///
  /// `key` is used to cache the resource and `path` is the path to where to reload the
  /// resource.
  fn inject<K>(&mut self, key: &K, resource: K::Target, dependencies: Vec<PathBuf>) -> Res<K::Target>
      where K: StoreKey,
            K::Target: Load<Key = K> {
    // wrap the resource to make it shared mutably
    let res = Rc::new(RefCell::new(resource));
    let res_ = res.clone();

    // create the path associated with the given key
    let key_ = key.clone();
    let path = self.root.join(K::key_to_path(&key));

    // closure used to reload the object when needed
    let on_reload: Box<for<'a> Fn(&'a mut Store) -> Result<(), LoadError>> = Box::new(move |cache| {
      deb!("reloading {:?}", key_);

      match K::Target::load(&key_, cache) {
        Ok(load_result) => {
          // replace the current resource with the freshly loaded one
          *res_.borrow_mut() = load_result.res;
          deb!("reloaded {:?}", key_);
          Ok(())
        },
        Err(e) => {
          warn!("{:?} failed to reload:\n{:#?}", key_, e);
          Err(e)
        }
      }
    });

    let metadata = ResMetaData {
      on_reload: on_reload,
      last_update_instant: Instant::now(),
    };

    // cache the resource and its meta data
    self.cache.save(RKey(key.clone()), res.clone());
    self.metadata.insert(path.clone(), metadata);

    deb!("cached resource {:?}", key);

    // register the resource as an observer of its dependencies in the dependencies graph
    for dep_key in dependencies {
      self.dependencies.insert(dep_key, path.clone());
    }

    res
  }

  /// Get a resource from the cache and return an error if loading failed.
  fn get_<K>(&mut self, key: &K) -> Result<Res<K::Target>, LoadError> where K: StoreKey, K::Target: Load<Key = K> {
    let rekey = RKey(key.clone());

    match self.cache.get(&rekey).cloned() {
      Some(resource) => {
        deb!("cache hit for {:?}", key);
        Ok(resource)
      },
      None => {
        deb!("cache miss for {:?}", key);

        // specific loading
        info!("loading {:?}", key);
        let load_result = K::Target::load(key, self)?;
        Ok(self.inject(key, load_result.res, load_result.dependencies))
      }
    }
  }

  /// Get a resource from the cache for the given key.
  pub fn get<K>(&mut self, key: &K) -> Option<Res<K::Target>> where K: StoreKey, K::Target: Load<Key = K> {
    deb!("getting {:?}", key);

    match self.get_(key) {
      Ok(resource) => Some(resource),
      Err(e) => {
        err!("cannot get {:?} because:\n{:#?}", key, e);
        None
      }
    }
  }

  /// Get a resource from the store for the given key. If it fails, a proxed version is used, which
  /// will get replaced by the resource once it’s available.
  pub fn get_proxied<K, P>(&mut self, key: &K, proxy: P) -> Result<Res<K::Target>, LoadError>
      where K: StoreKey,
            K::Target: Load<Key = K>,
            P: FnOnce() -> K::Target {
    match self.get_(key) {
      Ok(resource) => Ok(resource),
      Err(e) => {
        warn!("proxied {:?} because:\n{:#?}", key, e);

        // FIXME: we set the dependencies to none here, which is silly; find a better design
        Ok(self.inject(key, proxy(), Vec::new()))
      }
    }
  }

  /// Synchronize the cache by updating the resources that ought to.
  pub fn sync(&mut self) {
    let dirty = self.dirty.clone();
    let mut dirty_ = dirty.lock().unwrap();

    for &(ref path, ref instant) in dirty_.iter() {
      let path = self.root.join(path);
      if let Some(mut metadata) = self.metadata.remove(&path) {
        if instant.duration_since(metadata.last_update_instant) >= Duration::from_millis(UPDATE_AWAIT_TIME_MS) {
          if (metadata.on_reload)(self).is_ok() {
            // if we have successfully reloaded the resource, notify the observers that this
            // dependency has changed
            for dep in self.dependencies.get(path.as_path()).cloned() {
              if let Some(obs_metadata) = self.metadata.remove(dep.as_path()) {
                if let Err(e) = (obs_metadata.on_reload)(self) {
                  warn!("cannot reload {:?} {:?}", dep, e);
                }

                self.metadata.insert(dep, obs_metadata);
              }
            }
          }
        }

        metadata.last_update_instant = *instant;
        self.metadata.insert(path.clone(), metadata);
      }
    }

    dirty_.clear();
  }
}

/// Meta data about a resource.
struct ResMetaData {
  /// Function to call each time the resource must be reloaded.
  on_reload: Box<Fn(&mut Store) -> Result<(), LoadError>>,
  /// The last time the resource was updated.
  last_update_instant: Instant,
}

/// Error that might happen when creating a resource cache.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum StoreError {
  /// The root path for the resources was not found.
  RootDoesDotExit(PathBuf)
}
<commit_msg>More documentation.<commit_after>//! Resource system.
//!
//! Currently, a resource is a disk-cached object that can be hot-reloaded while you use it.
//! Resource can be serialized and deserialized as you see fit. The concept of *caching* and
//! *loading* are split in different code location so that you can easily compose both – provide
//! the loading code and ask the resource system to cache it for you.
//!
//! This great flexibility is exposed in the public interface so that the cache can be augmented
//! with user-provided objects. You might be interested in implementing `Load`, `CacheKey` – from
//! the [any-cache](https://crates.io/crates/any-cache) crate — as well as providing a type wrapper
//! over the key to access to your resource.
//!
//! In order to have hot-reloading working, you have to call the `Store::sync` function that will
//! perform disk syncing.
//!
//! # Note on keys
//!
//! If you use the resource system, your resources will be cached and accessible by their keys. The
//! key type is not enforced. Resource’s keys are typed to enable namespacing: if you have two
//! resources which ID is `34`, because the key types are different, you can safely cache the
//! resource with the ID `34` without any clashing or undefined behaviors. More in the any-cache
//! crate.

use any_cache::{Cache, HashCache};
pub use any_cache::CacheKey;
use notify::{Op, RawEvent, RecursiveMode, Watcher, raw_watcher};
use notify::op::WRITE;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt::Debug;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::channel;
use std::thread;
use std::time::{Duration, Instant};

/// Loadable object from disk.
///
/// An object can be loaded from disk if given a path it can be output a `LoadResult<_>`. It’s
/// important to note that you’re not supposed to load objects directly from this trait. Instead,
/// you should use a `Store`.
pub trait Load: 'static + Sized {
  type Key: StoreKey<Target = Self>;

  /// Load a resource. The `Store` can be used to load or declare additional resource dependencies.
  /// The result type is used to register for dependency events.
  fn load(key: &Self::Key, cache: &mut Store) -> Result<LoadResult<Self>, LoadError>;
}

/// Result of a resource loading. This type enables you to register a resource for reloading events
/// of others (dependencies). If you don’t need to run specific code on a dependency reloading, use
/// the `.into()` function to lift your return value to `LoadResult<_>`.
pub struct LoadResult<T> {
  /// The loaded object.
  pub res: T,
  /// The list of dependencies to listen for events.
  pub dependencies: Vec<PathBuf>
}

impl<T> LoadResult<T> {
  /// Return a resource along with its dependencies.
  pub fn with_dependencies(res: T, dependencies: Vec<PathBuf>) -> Self {
    LoadResult { res, dependencies }
  }
}

impl<T> From<T> for LoadResult<T> {
  fn from(res: T) -> Self {
    LoadResult {
      res,
      dependencies: Vec::new()
    }
  }
}

/// Error that might occur while loading a resource.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum LoadError {
  /// The file was not found.
  FileNotFound(PathBuf),
  /// The file wasn’t correctly parsed.
  ParseFailed(String),
  /// The file wasn’t correctly converted, even though it might have been parsed.
  ConversionFailed(String)
}

/// Resources are wrapped in this type.
pub type Res<T> = Rc<RefCell<T>>;

/// Time to await after a resource update to establish that it should be reloaded.
const UPDATE_AWAIT_TIME_MS: u64 = 1000;

/// Resource key. This type is used to adapt a key type’s target so that it can be mutably shared.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
struct RKey<K>(K);

impl<K, T> CacheKey for RKey<K> where K: CacheKey<Target = T> {
  type Target = Res<T>;
}

/// Trait used to represent keys in a resource store.
pub trait StoreKey: CacheKey + Clone + Debug {
  /// Convert from a key to its path representation.
  fn key_to_path(&self) -> PathBuf;
}

/// Resource store. Responsible for holding and presenting resources.
pub struct Store {
  // canonicalized root path of resources
  root: PathBuf,
  // resource cache
  cache: HashCache,
  // contains all metadata on resources
  metadata: HashMap<PathBuf, ResMetaData>,
  // dependencies, mapping a dependency to its observers
  dependencies: HashMap<PathBuf, PathBuf>,
  // vector of pairs (path, timestamp) giving indication on resources to reload
  dirty: Arc<Mutex<Vec<(PathBuf, Instant)>>>,
  #[allow(dead_code)]
  watcher_thread: thread::JoinHandle<()>
}

impl Store {
  /// Create a new store. The `root` represents the root directory from all the resources come from.
  pub fn new<P>(root: P) -> Result<Self, StoreError> where P: AsRef<Path> {
    let dirty: Arc<Mutex<Vec<(PathBuf, Instant)>>> = Arc::new(Mutex::new(Vec::new()));
    let dirty_ = dirty.clone();

    let root = root.as_ref().to_owned();
    let root_ = root.clone();
    let canon_root = root.canonicalize().map_err(|_| StoreError::RootDoesDotExit(root_.into()))?;
    let canon_root_ = canon_root.clone();
    let (wsx, wrx) = channel();
    let mut watcher = raw_watcher(wsx).unwrap();

    let join_handle = thread::spawn(move || {
      let _ = watcher.watch(canon_root_.clone(), RecursiveMode::Recursive);

      for event in wrx.iter() {
        match event {
          RawEvent { path: Some(ref path), op: Ok(op), .. } if op | WRITE != Op::empty() => {
            dirty_.lock().unwrap().push((path.strip_prefix(&canon_root_).unwrap().to_owned(), Instant::now()));
          },
          _ => ()
        }
      }
    });

    deb!("resource cache started and listens to file changes in {}", root.display());

    Ok(Store {
      root: canon_root,
      cache: HashCache::new(),
      metadata: HashMap::new(),
      dependencies: HashMap::new(),
      dirty: dirty,
      watcher_thread: join_handle
    })
  }

  /// Inject a new resource in the cache.
  ///
  /// `key` is used to cache the resource and `path` is the path to where to reload the
  /// resource.
  fn inject<K>(&mut self, key: &K, resource: K::Target, dependencies: Vec<PathBuf>) -> Res<K::Target>
      where K: StoreKey,
            K::Target: Load<Key = K> {
    // wrap the resource to make it shared mutably
    let res = Rc::new(RefCell::new(resource));
    let res_ = res.clone();

    // create the path associated with the given key
    let key_ = key.clone();
    let path = self.root.join(K::key_to_path(&key));

    // closure used to reload the object when needed
    let on_reload: Box<for<'a> Fn(&'a mut Store) -> Result<(), LoadError>> = Box::new(move |cache| {
      deb!("reloading {:?}", key_);

      match K::Target::load(&key_, cache) {
        Ok(load_result) => {
          // replace the current resource with the freshly loaded one
          *res_.borrow_mut() = load_result.res;
          deb!("reloaded {:?}", key_);
          Ok(())
        },
        Err(e) => {
          warn!("{:?} failed to reload:\n{:#?}", key_, e);
          Err(e)
        }
      }
    });

    let metadata = ResMetaData {
      on_reload: on_reload,
      last_update_instant: Instant::now(),
    };

    // cache the resource and its meta data
    self.cache.save(RKey(key.clone()), res.clone());
    self.metadata.insert(path.clone(), metadata);

    deb!("cached resource {:?}", key);

    // register the resource as an observer of its dependencies in the dependencies graph
    for dep_key in dependencies {
      self.dependencies.insert(dep_key, path.clone());
    }

    res
  }

  /// Get a resource from the cache and return an error if loading failed.
  fn get_<K>(&mut self, key: &K) -> Result<Res<K::Target>, LoadError> where K: StoreKey, K::Target: Load<Key = K> {
    let rekey = RKey(key.clone());

    match self.cache.get(&rekey).cloned() {
      Some(resource) => {
        deb!("cache hit for {:?}", key);
        Ok(resource)
      },
      None => {
        deb!("cache miss for {:?}", key);

        // specific loading
        info!("loading {:?}", key);
        let load_result = K::Target::load(key, self)?;
        Ok(self.inject(key, load_result.res, load_result.dependencies))
      }
    }
  }

  /// Get a resource from the cache for the given key.
  pub fn get<K>(&mut self, key: &K) -> Option<Res<K::Target>> where K: StoreKey, K::Target: Load<Key = K> {
    deb!("getting {:?}", key);

    match self.get_(key) {
      Ok(resource) => Some(resource),
      Err(e) => {
        err!("cannot get {:?} because:\n{:#?}", key, e);
        None
      }
    }
  }

  /// Get a resource from the store for the given key. If it fails, a proxed version is used, which
  /// will get replaced by the resource once it’s available.
  pub fn get_proxied<K, P>(&mut self, key: &K, proxy: P) -> Result<Res<K::Target>, LoadError>
      where K: StoreKey,
            K::Target: Load<Key = K>,
            P: FnOnce() -> K::Target {
    match self.get_(key) {
      Ok(resource) => Ok(resource),
      Err(e) => {
        warn!("proxied {:?} because:\n{:#?}", key, e);

        // FIXME: we set the dependencies to none here, which is silly; find a better design
        Ok(self.inject(key, proxy(), Vec::new()))
      }
    }
  }

  /// Synchronize the cache by updating the resources that ought to.
  pub fn sync(&mut self) {
    let dirty = self.dirty.clone();
    let mut dirty_ = dirty.lock().unwrap();

    for &(ref path, ref instant) in dirty_.iter() {
      let path = self.root.join(path);
      if let Some(mut metadata) = self.metadata.remove(&path) {
        if instant.duration_since(metadata.last_update_instant) >= Duration::from_millis(UPDATE_AWAIT_TIME_MS) {
          if (metadata.on_reload)(self).is_ok() {
            // if we have successfully reloaded the resource, notify the observers that this
            // dependency has changed
            for dep in self.dependencies.get(path.as_path()).cloned() {
              if let Some(obs_metadata) = self.metadata.remove(dep.as_path()) {
                if let Err(e) = (obs_metadata.on_reload)(self) {
                  warn!("cannot reload {:?} {:?}", dep, e);
                }

                self.metadata.insert(dep, obs_metadata);
              }
            }
          }
        }

        metadata.last_update_instant = *instant;
        self.metadata.insert(path.clone(), metadata);
      }
    }

    dirty_.clear();
  }
}

/// Meta data about a resource.
struct ResMetaData {
  /// Function to call each time the resource must be reloaded.
  on_reload: Box<Fn(&mut Store) -> Result<(), LoadError>>,
  /// The last time the resource was updated.
  last_update_instant: Instant,
}

/// Error that might happen when creating a resource cache.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum StoreError {
  /// The root path for the resources was not found.
  RootDoesDotExit(PathBuf)
}
<|endoftext|>


<commit_before>use std::fmt::{Display, Formatter, Error};
use std::hash::{Hash, Hasher};
use std::string::String;

use super::binding::{Binding, UniqueBinding};
use super::coerce::AsString;
use super::js_fn::JsFnStruct;
use super::js_obj::JsObjStruct;
use super::js_str::JsStrStruct;
use super::native_fn::NativeFn;

#[derive(Clone, Debug)]
pub struct JsVar {
    pub unique: UniqueBinding,
    pub binding: Binding,
    pub t: JsType,
}

impl Hash for JsVar {
    fn hash<H>(&self, state: &mut H) where H: Hasher {
        self.unique.hash(state);
    }
}

impl JsVar {
    pub fn new(t: JsType) -> JsVar {
        let mut var = JsVar {
            unique: UniqueBinding::dummy(),
            binding: Binding::anon(),
            t: t,
        };
        var.unique = UniqueBinding::mangle(&var.binding);
        var
    }

    pub fn bind(binding: &str, t: JsType) -> JsVar {
        let mut var = JsVar {
            unique: UniqueBinding::mangle_str(binding),
            binding: Binding::new(binding.to_owned()),
            t: t,
        };
        var.unique = UniqueBinding::mangle(&var.binding);
        var
    }

    pub fn type_of(&self) -> String {
        self.t.type_of()
    }
}

impl Display for JsVar {
    fn fmt(&self, mut fmt: &mut Formatter) -> Result<(), Error> {
        if !self.binding.is_anon() {
            return write!(fmt, "{}", self.binding);
        }

        write!(fmt, "{}", self.t)
    }
}

impl PartialEq for JsVar {
    fn eq(&self, other: &Self) -> bool {
        self.unique == other.unique
    }

    fn ne(&self, other: &Self) -> bool {
        !self.eq(other)
    }
}

impl Eq for JsVar {}

#[derive(Clone, Debug)]
pub enum JsPtrEnum {
    JsSym(String),
    JsStr(JsStrStruct),
    JsObj(JsObjStruct),
    JsFn(JsFnStruct),
    NativeFn(NativeFn),
}

impl Display for JsPtrEnum {
    fn fmt(&self, mut fmt: &mut Formatter) -> Result<(), Error> {
        match self {
            &JsPtrEnum::JsSym(ref s) => write!(fmt, "Symbol({})", s),
            &JsPtrEnum::JsStr(ref s) => write!(fmt, "\"{}\"", s),
            &JsPtrEnum::JsObj(ref o) => write!(fmt, "{}", o),
            &JsPtrEnum::JsFn(ref f) => write!(fmt, "{}", f),
            &JsPtrEnum::NativeFn(_) => write!(fmt, "[native code]"),
        }
    }
}

#[derive(Clone, Debug)]
pub enum JsPtrTag {
    JsSym,
    JsStr,
    JsObj,
    JsFn { name: Option<String> },
    NativeFn { name: String },
}

impl JsPtrTag {
    pub fn eq_ptr_type(&self, other: &JsPtrEnum) -> bool {
        match (self, other) {
            (&JsPtrTag::JsSym, &JsPtrEnum::JsSym(_)) |
            (&JsPtrTag::JsStr, &JsPtrEnum::JsStr(_)) |
            (&JsPtrTag::JsObj, &JsPtrEnum::JsObj(_)) |
            (&JsPtrTag::JsFn{..},  &JsPtrEnum::JsFn(_)) |
            (&JsPtrTag::NativeFn{..}, &JsPtrEnum::NativeFn(_)) => true,
            _ => false
        }
    }

    fn type_of(&self) -> String {
        let s = match *self {
            JsPtrTag::JsSym => "symbol",
            JsPtrTag::JsStr => "string",
            JsPtrTag::JsObj => "object",
            JsPtrTag::JsFn{..} | JsPtrTag::NativeFn{..} => "function",
        };

        String::from(s)
    }
}

#[derive(Clone, Debug)]
pub enum JsType {
    JsUndef,
    JsNum(f64),
    JsBool(bool),
    JsPtr(JsPtrTag),
    JsNull, // null is not a ptr since it doesn't actually require heap allocation
}

impl JsType {
    fn type_of(&self) -> String {
        let s = match *self {
            JsType::JsUndef => "undefined",
            JsType::JsNum(_) => "number",
            JsType::JsBool(_) => "boolean",
            JsType::JsNull => "object",
            JsType::JsPtr(ref t) => return t.type_of(),
        };

        String::from(s)
    }
}

impl Display for JsType {
    fn fmt(&self, mut fmt: &mut Formatter) -> Result<(), Error> {
        write!(fmt, "{}", self.as_string())
    }
}

impl PartialEq for JsType {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (&JsType::JsUndef, &JsType::JsUndef) => true,
            (&JsType::JsNum(x), &JsType::JsNum(y)) => x == y,
            (&JsType::JsBool(b1), &JsType::JsBool(b2)) => b1 == b2,
            (&JsType::JsNull, &JsType::JsNull) => true,
            (_, _) => false,
        }
    }

    fn ne(&self, other: &Self) -> bool {
        !self.eq(other)
    }
}

impl Eq for JsType {}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum JsKey {
    JsStr(JsStrStruct),
    JsSym(String),
}

impl Display for JsKey {
    fn fmt(&self, mut fmt: &mut Formatter) -> Result<(), Error> {
        match *self {
            JsKey::JsStr(ref s) => write!(fmt, "{}", s),
            JsKey::JsSym(ref s) => write!(fmt, "Symbol({})", s),
        }
    }
}
<commit_msg>Add deanonymize method to JsVar<commit_after>use std::fmt::{Display, Formatter, Error};
use std::hash::{Hash, Hasher};
use std::string::String;

use super::binding::{Binding, UniqueBinding};
use super::coerce::AsString;
use super::js_fn::JsFnStruct;
use super::js_obj::JsObjStruct;
use super::js_str::JsStrStruct;
use super::native_fn::NativeFn;

#[derive(Clone, Debug)]
pub struct JsVar {
    pub unique: UniqueBinding,
    pub binding: Binding,
    pub t: JsType,
}

impl Hash for JsVar {
    fn hash<H>(&self, state: &mut H) where H: Hasher {
        self.unique.hash(state);
    }
}

impl JsVar {
    pub fn new(t: JsType) -> JsVar {
        let mut var = JsVar {
            unique: UniqueBinding::dummy(),
            binding: Binding::anon(),
            t: t,
        };
        var.unique = UniqueBinding::mangle(&var.binding);
        var
    }

    pub fn bind(binding: &str, t: JsType) -> JsVar {
        let mut var = JsVar {
            unique: UniqueBinding::mangle_str(binding),
            binding: Binding::new(binding.to_owned()),
            t: t,
        };
        var.unique = UniqueBinding::mangle(&var.binding);
        var
    }

    pub fn deanonymize(&mut self, binding: &str) -> bool {
        if self.binding.is_anon() {
            self.unique = UniqueBinding::mangle_str(binding);
            self.binding = Binding::new(binding.to_owned());
            true
        } else {
            false
        }
    }

    pub fn type_of(&self) -> String {
        self.t.type_of()
    }
}

impl Display for JsVar {
    fn fmt(&self, mut fmt: &mut Formatter) -> Result<(), Error> {
        if !self.binding.is_anon() {
            return write!(fmt, "{}", self.binding);
        }

        write!(fmt, "{}", self.t)
    }
}

impl PartialEq for JsVar {
    fn eq(&self, other: &Self) -> bool {
        self.unique == other.unique
    }

    fn ne(&self, other: &Self) -> bool {
        !self.eq(other)
    }
}

impl Eq for JsVar {}

#[derive(Clone, Debug)]
pub enum JsPtrEnum {
    JsSym(String),
    JsStr(JsStrStruct),
    JsObj(JsObjStruct),
    JsFn(JsFnStruct),
    NativeFn(NativeFn),
}

impl Display for JsPtrEnum {
    fn fmt(&self, mut fmt: &mut Formatter) -> Result<(), Error> {
        match self {
            &JsPtrEnum::JsSym(ref s) => write!(fmt, "Symbol({})", s),
            &JsPtrEnum::JsStr(ref s) => write!(fmt, "\"{}\"", s),
            &JsPtrEnum::JsObj(ref o) => write!(fmt, "{}", o),
            &JsPtrEnum::JsFn(ref f) => write!(fmt, "{}", f),
            &JsPtrEnum::NativeFn(_) => write!(fmt, "[native code]"),
        }
    }
}

#[derive(Clone, Debug)]
pub enum JsPtrTag {
    JsSym,
    JsStr,
    JsObj,
    JsFn { name: Option<String> },
    NativeFn { name: String },
}

impl JsPtrTag {
    pub fn eq_ptr_type(&self, other: &JsPtrEnum) -> bool {
        match (self, other) {
            (&JsPtrTag::JsSym, &JsPtrEnum::JsSym(_)) |
            (&JsPtrTag::JsStr, &JsPtrEnum::JsStr(_)) |
            (&JsPtrTag::JsObj, &JsPtrEnum::JsObj(_)) |
            (&JsPtrTag::JsFn{..},  &JsPtrEnum::JsFn(_)) |
            (&JsPtrTag::NativeFn{..}, &JsPtrEnum::NativeFn(_)) => true,
            _ => false
        }
    }

    fn type_of(&self) -> String {
        let s = match *self {
            JsPtrTag::JsSym => "symbol",
            JsPtrTag::JsStr => "string",
            JsPtrTag::JsObj => "object",
            JsPtrTag::JsFn{..} | JsPtrTag::NativeFn{..} => "function",
        };

        String::from(s)
    }
}

#[derive(Clone, Debug)]
pub enum JsType {
    JsUndef,
    JsNum(f64),
    JsBool(bool),
    JsPtr(JsPtrTag),
    JsNull, // null is not a ptr since it doesn't actually require heap allocation
}

impl JsType {
    fn type_of(&self) -> String {
        let s = match *self {
            JsType::JsUndef => "undefined",
            JsType::JsNum(_) => "number",
            JsType::JsBool(_) => "boolean",
            JsType::JsNull => "object",
            JsType::JsPtr(ref t) => return t.type_of(),
        };

        String::from(s)
    }
}

impl Display for JsType {
    fn fmt(&self, mut fmt: &mut Formatter) -> Result<(), Error> {
        write!(fmt, "{}", self.as_string())
    }
}

impl PartialEq for JsType {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (&JsType::JsUndef, &JsType::JsUndef) => true,
            (&JsType::JsNum(x), &JsType::JsNum(y)) => x == y,
            (&JsType::JsBool(b1), &JsType::JsBool(b2)) => b1 == b2,
            (&JsType::JsNull, &JsType::JsNull) => true,
            (_, _) => false,
        }
    }

    fn ne(&self, other: &Self) -> bool {
        !self.eq(other)
    }
}

impl Eq for JsType {}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum JsKey {
    JsStr(JsStrStruct),
    JsSym(String),
}

impl Display for JsKey {
    fn fmt(&self, mut fmt: &mut Formatter) -> Result<(), Error> {
        match *self {
            JsKey::JsStr(ref s) => write!(fmt, "{}", s),
            JsKey::JsSym(ref s) => write!(fmt, "Symbol({})", s),
        }
    }
}
<|endoftext|>


<commit_before>use std::ptr;
use std::from_str::FromStr;

use udev::{
    device,
    libudev_c,
    util,
};

use udev::udev::Udev;
use udev::device::Device;

pub struct Monitor<'u> {
    pub udev: &'u Udev,
    monitor: libudev_c::udev_monitor
}

#[deriving(Show)]
pub enum Action {
    AddAction,
    RemoveAction,
    ChangeAction,
    MoveAction,
    OnlineAction,
    OfflineAction,
    OtherAction(String)
}

#[deriving(Show)]
pub struct Event {
    pub action: Action,
    pub seqnum: u64
}

#[doc(hidden)]
pub struct MonitorIterator<'m, 'u: 'm> {
    monitor: &'m Monitor<'u>
}

pub unsafe fn monitor(udev: &Udev, monitor: libudev_c::udev_monitor) -> Monitor {
    Monitor {
        udev: udev,
        monitor: monitor
    }
}

impl<'u> Monitor<'u> {
    /// Filter by subsystem.
    ///
    /// Exclude devices that don't match the specified subsystem or a previously specified
    /// subsystem.
    pub fn filter_by_subsystem(self, subsystem: &str) -> Monitor<'u> {
        subsystem.with_c_str(|subsystem| util::handle_error(unsafe {
            libudev_c::udev_monitor_filter_add_match_subsystem_devtype(self.monitor, subsystem, ptr::null())
        }));
        self
    }
    /// Filter by subsystem/devtype combination.
    ///
    /// Exclude devices that don't match the specified subsystem/devtype combination or a
    /// previously specified subsystem/devtype combination (or any subsystem previously specified
    /// in a `filter_subsystem` invocation).
    pub fn filter_by_subsystem_devtype(self, subsystem: &str, devtype: &str) -> Monitor<'u> {
        subsystem.with_c_str(|subsystem| devtype.with_c_str(|devtype| util::handle_error(unsafe {
            libudev_c::udev_monitor_filter_add_match_subsystem_devtype(self.monitor, subsystem, devtype)
        })));
        self
    }
    /// Filter by tag.
    ///
    /// Exclude devices that don't match the specified tag or a previously specified tag.
    pub fn filter_by_tag(self, tag: &str) -> Monitor<'u> {
        tag.with_c_str(|tag| util::handle_error(unsafe {
            libudev_c::udev_monitor_filter_add_match_tag(self.monitor, tag)
        }));
        self
    }

    /// Reset all filters on this monitor. No devices will be excluded.
    pub fn clear_filters(self) -> Monitor<'u> {
        util::handle_error(unsafe {
            libudev_c::udev_monitor_filter_remove(self.monitor)
        });
        self
    }

    /// Iterate over udev events.
    ///
    /// 1. The returned iterator will block on calls to next until their a device is available.
    /// 2. The returned iterator will never end (next will never return None).
    pub fn iter<'m>(&'m self) -> MonitorIterator<'m, 'u> {
        util::handle_error(unsafe {
            // Technically this mutates but we're single threaded anyways. Basically, having two
            // iterators existing at the same time won't cause any problems because next() can't be
            // called at the same time (single threaded).
            libudev_c::udev_monitor_enable_receiving(self.monitor)
        });
        MonitorIterator::<'m, 'u> {
            monitor: self
        }
    }
}

#[unsafe_destructor]
impl<'u> Drop for Monitor<'u> {
    fn drop(&mut self) {
        unsafe {
            libudev_c::udev_monitor_unref(self.monitor);
        }
    }
}

impl FromStr for Action {
    fn from_str(s: &str) -> Option<Action> {
        match s {
            "add"       => Some(AddAction),
            "remove"    => Some(RemoveAction),
            "change"    => Some(ChangeAction),
            "move"      => Some(MoveAction),
            "online"    => Some(OnlineAction),
            "offline"   => Some(OfflineAction),
            _           => Some(OtherAction(s.to_string())),
        }
    }
}

impl<'m, 'u> Iterator<(Event, Device<'u>)> for MonitorIterator<'m, 'u> {
    fn next(&mut self) -> Option<(Event, Device<'u>)> {
        loop {
            if let Ok(Some(dev)) = util::check_errno(|| unsafe {
                libudev_c::udev_monitor_receive_device(self.monitor.monitor)
            }) {
                return Some((
                    Event {
                        action: from_str(unsafe {
                                    util::c_to_str(libudev_c::udev_device_get_action(dev))
                                }.unwrap()).unwrap(),
                        seqnum: unsafe {
                                    libudev_c::udev_device_get_seqnum(dev)
                                }
                    },
                    unsafe { device::device(self.monitor.udev, dev) }
                ));
            }
        }
    }
}

<commit_msg>Fix FromStr path.<commit_after>use std::ptr;
use std::str::FromStr;

use udev::{
    device,
    libudev_c,
    util,
};

use udev::udev::Udev;
use udev::device::Device;

pub struct Monitor<'u> {
    pub udev: &'u Udev,
    monitor: libudev_c::udev_monitor
}

#[deriving(Show)]
pub enum Action {
    AddAction,
    RemoveAction,
    ChangeAction,
    MoveAction,
    OnlineAction,
    OfflineAction,
    OtherAction(String)
}

#[deriving(Show)]
pub struct Event {
    pub action: Action,
    pub seqnum: u64
}

#[doc(hidden)]
pub struct MonitorIterator<'m, 'u: 'm> {
    monitor: &'m Monitor<'u>
}

pub unsafe fn monitor(udev: &Udev, monitor: libudev_c::udev_monitor) -> Monitor {
    Monitor {
        udev: udev,
        monitor: monitor
    }
}

impl<'u> Monitor<'u> {
    /// Filter by subsystem.
    ///
    /// Exclude devices that don't match the specified subsystem or a previously specified
    /// subsystem.
    pub fn filter_by_subsystem(self, subsystem: &str) -> Monitor<'u> {
        subsystem.with_c_str(|subsystem| util::handle_error(unsafe {
            libudev_c::udev_monitor_filter_add_match_subsystem_devtype(self.monitor, subsystem, ptr::null())
        }));
        self
    }
    /// Filter by subsystem/devtype combination.
    ///
    /// Exclude devices that don't match the specified subsystem/devtype combination or a
    /// previously specified subsystem/devtype combination (or any subsystem previously specified
    /// in a `filter_subsystem` invocation).
    pub fn filter_by_subsystem_devtype(self, subsystem: &str, devtype: &str) -> Monitor<'u> {
        subsystem.with_c_str(|subsystem| devtype.with_c_str(|devtype| util::handle_error(unsafe {
            libudev_c::udev_monitor_filter_add_match_subsystem_devtype(self.monitor, subsystem, devtype)
        })));
        self
    }
    /// Filter by tag.
    ///
    /// Exclude devices that don't match the specified tag or a previously specified tag.
    pub fn filter_by_tag(self, tag: &str) -> Monitor<'u> {
        tag.with_c_str(|tag| util::handle_error(unsafe {
            libudev_c::udev_monitor_filter_add_match_tag(self.monitor, tag)
        }));
        self
    }

    /// Reset all filters on this monitor. No devices will be excluded.
    pub fn clear_filters(self) -> Monitor<'u> {
        util::handle_error(unsafe {
            libudev_c::udev_monitor_filter_remove(self.monitor)
        });
        self
    }

    /// Iterate over udev events.
    ///
    /// 1. The returned iterator will block on calls to next until their a device is available.
    /// 2. The returned iterator will never end (next will never return None).
    pub fn iter<'m>(&'m self) -> MonitorIterator<'m, 'u> {
        util::handle_error(unsafe {
            // Technically this mutates but we're single threaded anyways. Basically, having two
            // iterators existing at the same time won't cause any problems because next() can't be
            // called at the same time (single threaded).
            libudev_c::udev_monitor_enable_receiving(self.monitor)
        });
        MonitorIterator::<'m, 'u> {
            monitor: self
        }
    }
}

#[unsafe_destructor]
impl<'u> Drop for Monitor<'u> {
    fn drop(&mut self) {
        unsafe {
            libudev_c::udev_monitor_unref(self.monitor);
        }
    }
}

impl FromStr for Action {
    fn from_str(s: &str) -> Option<Action> {
        match s {
            "add"       => Some(AddAction),
            "remove"    => Some(RemoveAction),
            "change"    => Some(ChangeAction),
            "move"      => Some(MoveAction),
            "online"    => Some(OnlineAction),
            "offline"   => Some(OfflineAction),
            _           => Some(OtherAction(s.to_string())),
        }
    }
}

impl<'m, 'u> Iterator<(Event, Device<'u>)> for MonitorIterator<'m, 'u> {
    fn next(&mut self) -> Option<(Event, Device<'u>)> {
        loop {
            if let Ok(Some(dev)) = util::check_errno(|| unsafe {
                libudev_c::udev_monitor_receive_device(self.monitor.monitor)
            }) {
                return Some((
                    Event {
                        action: from_str(unsafe {
                                    util::c_to_str(libudev_c::udev_device_get_action(dev))
                                }.unwrap()).unwrap(),
                        seqnum: unsafe {
                                    libudev_c::udev_device_get_seqnum(dev)
                                }
                    },
                    unsafe { device::device(self.monitor.udev, dev) }
                ));
            }
        }
    }
}

<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::env::{current_dir, vars};
use std::io::{self, Write};

use anyhow::{bail, Context, Result};
use log::error;

use pueue::message::*;
use pueue::platform::socket::*;
use pueue::protocol::*;
use pueue::settings::Settings;

use crate::cli::{Opt, SubCommand};
use crate::commands::edit::*;
use crate::commands::local_follow::*;
use crate::commands::restart::*;
use crate::output::*;

/// This struct contains the base logic for the client.
/// The client is responsible for connecting to the daemon, sending instructions
/// and interpreting their responses.
///
/// Most commands are a simple ping-pong. However, some commands require a more complex
/// communication pattern, such as the `follow` command, which continuously streams the output of a task.
pub struct Client {
    opt: Opt,
    settings: Settings,
    socket: Socket,
}

impl Client {
    /// Connect to the daemon, authorize via secret and return a new initialized Client.
    pub async fn new(settings: Settings, opt: Opt) -> Result<Self> {
        // // Commandline argument overwrites the configuration files values for address
        // let address = if let Some(address) = opt.address.clone() {
        //     address
        // } else {
        //     settings.client.daemon_address
        // };

        // Commandline argument overwrites the configuration files values for port
        let (unix_socket_path, port) = {
            // Return the unix socket path, if we're supposed to use it.
            if settings.shared.use_unix_socket {
                (Some(settings.shared.unix_socket_path.clone()), None)
            } else {
                // Otherwise use tcp sockets and a given port
                // Commandline argument overwrites the configuration files values for port.
                let port = if let Some(port) = opt.port.clone() {
                    port
                } else {
                    settings.shared.port.clone()
                };

                (None, Some(port))
            }
        };

        let mut socket = get_client(unix_socket_path, port).await?;

        // Send the secret to the daemon
        let secret = settings.shared.secret.clone().into_bytes();
        send_bytes(secret, &mut socket).await?;

        Ok(Client {
            opt,
            settings,
            socket,
        })
    }

    /// This is the function where the actual communication and logic starts.
    /// At this point everything is initialized, the connection is up and
    /// we can finally start doing stuff.
    ///
    /// The command handling is splitted into "simple" and "complex" commands.
    pub async fn start(&mut self) -> Result<()> {
        // Return early, if the command has already been handled.
        if self.handle_complex_command().await? {
            return Ok(());
        }

        // The handling of "generic" commands is encapsulated in this function.
        self.handle_simple_command().await?;

        Ok(())
    }

    /// Handle all complex client-side functionalities.
    /// Complex functionalities need some special handling and are contained
    /// in their own functions with their own communication code.
    /// Such functionalities includes reading local files, data streaming
    /// and sending multiple messages.
    ///
    /// Returns `true`, if the current command has been handled by this function.
    /// This indicates that the client can now shut down.
    async fn handle_complex_command(&mut self) -> Result<bool> {
        // This match handles all "complex" commands.
        match &self.opt.cmd {
            SubCommand::Edit { task_id, path } => {
                let message = edit(&mut self.socket, *task_id, *path).await?;
                self.handle_response(message);
                Ok(true)
            }
            SubCommand::Restart {
                task_ids,
                start_immediately,
                stashed,
                edit,
                path,
            } => {
                restart(
                    &mut self.socket,
                    task_ids.clone(),
                    *start_immediately,
                    *stashed,
                    *edit,
                    *path,
                )
                .await?;
                Ok(true)
            }
            SubCommand::Follow { task_id, err } => {
                // Simple log output follows for local logs don't need any communication with the daemon.
                // Thereby we handle this separately over here.
                if self.settings.client.read_local_logs {
                    local_follow(
                        &mut self.socket,
                        self.settings.shared.pueue_directory.clone(),
                        task_id,
                        *err,
                    )
                    .await?;
                    return Ok(true);
                }
                Ok(false)
            }
            _ => Ok(false),
        }
    }

    /// Handle logic that's super generic on the client-side.
    /// This always follows a singular ping-pong pattern.
    /// One message to the daemon, one response, Done.
    async fn handle_simple_command(&mut self) -> Result<()> {
        // Create the message that should be sent to the daemon
        // depending on the given commandline options.
        let message = self.get_message_from_opt()?;

        // Create the message payload and send it to the daemon.
        send_message(message, &mut self.socket).await?;

        // Check if we can receive the response from the daemon
        let mut response = receive_message(&mut self.socket).await?;

        // Check if we can receive the response from the daemon
        while self.handle_response(response) {
            response = receive_message(&mut self.socket).await?;
        }

        Ok(())
    }

    /// Most returned messages can be handled in a generic fashion.
    /// However, some commands require continuous receiving of messages (streaming).
    ///
    /// If this function returns `Ok(true)`, the parent function will continue to receive
    /// and handle messages from the daemon. Otherwise the client will simply exit.
    fn handle_response(&self, message: Message) -> bool {
        match message {
            Message::Success(text) => print_success(&text),
            Message::Failure(text) => print_error(&text),
            Message::StatusResponse(state) => print_state(state, &self.opt.cmd),
            Message::LogResponse(task_logs) => print_logs(task_logs, &self.opt.cmd, &self.settings),
            Message::Stream(text) => {
                print!("{}", text);
                io::stdout().flush().unwrap();
                return true;
            }
            _ => error!("Received unhandled response message"),
        };

        false
    }

    /// Prints a warning and prompt for given action and tasks.
    /// Returns `Ok(())` if the action was confirmed.
    fn handle_user_confirmation(&self, action :&str, task_ids: &Vec<usize>) -> Result<()> {
        // printing warning and prompt
        println!("You are trying to {}: {}", action, task_ids
            .into_iter()
            .map(|t| format!("task{}", t.to_string()))
            .collect::<Vec<String>>()
            .join(", ")
        );
        print!("\nDo you want to continue [Y/n]: ");

        let mut input = String::new();
        io::stdout().flush().unwrap();
        io::stdin().read_line(&mut input)?;

        if input.starts_with("n") || input.starts_with("N") {
            bail!("User did not confirm");
        }

        Ok(())
    }

    /// Convert the cli command into the message that's being sent to the server,
    /// so it can be understood by the daemon.
    fn get_message_from_opt(&self) -> Result<Message> {
        match &self.opt.cmd {
            SubCommand::Add {
                command,
                start_immediately,
                stashed,
                group,
                delay_until,
                dependencies,
            } => {
                let cwd_pathbuf = current_dir()?;
                let cwd = cwd_pathbuf
                    .to_str()
                    .context("Cannot parse current working directory (Invalid utf8?)")?;

                let mut envs = HashMap::new();
                // Save all environment variables for later injection into the started task
                for (key, value) in vars() {
                    envs.insert(key, value);
                }

                Ok(Message::Add(AddMessage {
                    command: command.join(" "),
                    path: cwd.to_string(),
                    envs,
                    start_immediately: *start_immediately,
                    stashed: *stashed,
                    group: group.clone(),
                    enqueue_at: *delay_until,
                    dependencies: dependencies.to_vec(),
                    ignore_aliases: false,
                }))
            }
            SubCommand::Remove { task_ids } => {
                if self.settings.client.print_remove_warnings {
                    match self.handle_user_confirmation("remove", task_ids) {
                        Ok(_) => (),
                        Err(_) => std::process::exit(1)
                    }
                }
                Ok(Message::Remove(task_ids.clone()))
            },
            SubCommand::Stash { task_ids } => Ok(Message::Stash(task_ids.clone())),
            SubCommand::Switch {
                task_id_1,
                task_id_2,
            } => {
                let message = SwitchMessage {
                    task_id_1: *task_id_1,
                    task_id_2: *task_id_2,
                };
                Ok(Message::Switch(message))
            }
            SubCommand::Enqueue {
                task_ids,
                delay_until,
            } => {
                let message = EnqueueMessage {
                    task_ids: task_ids.clone(),
                    enqueue_at: *delay_until,
                };
                Ok(Message::Enqueue(message))
            }
            SubCommand::Start {
                task_ids,
                group,
                all,
                children,
            } => {
                let message = StartMessage {
                    task_ids: task_ids.clone(),
                    group: group.clone(),
                    all: *all,
                    children: *children,
                };
                Ok(Message::Start(message))
            }
            SubCommand::Pause {
                task_ids,
                group,
                wait,
                all,
                children,
            } => {
                let message = PauseMessage {
                    task_ids: task_ids.clone(),
                    group: group.clone(),
                    wait: *wait,
                    all: *all,
                    children: *children,
                };
                Ok(Message::Pause(message))
            }
            SubCommand::Kill {
                task_ids,
                group,
                default,
                all,
                children,
            } => {
                if self.settings.client.print_remove_warnings {
                    match self.handle_user_confirmation("kill", task_ids) {
                        Ok(_) => (),
                        Err(_) => std::process::exit(1)
                    }
                }
                let message = KillMessage {
                    task_ids: task_ids.clone(),
                    group: group.clone(),
                    default: *default,
                    all: *all,
                    children: *children,
                };
                Ok(Message::Kill(message))
            }
            SubCommand::Send { task_id, input } => {
                let message = SendMessage {
                    task_id: *task_id,
                    input: input.clone(),
                };
                Ok(Message::Send(message))
            }
            SubCommand::Group { add, remove } => {
                let message = GroupMessage {
                    add: add.clone(),
                    remove: remove.clone(),
                };
                Ok(Message::Group(message))
            }
            SubCommand::Status { .. } => Ok(Message::Status),
            SubCommand::Log { task_ids, .. } => {
                let message = LogRequestMessage {
                    task_ids: task_ids.clone(),
                    send_logs: !self.settings.client.read_local_logs,
                };
                Ok(Message::Log(message))
            }
            SubCommand::Follow { task_id, err } => {
                let message = StreamRequestMessage {
                    task_id: *task_id,
                    err: *err,
                };
                Ok(Message::StreamRequest(message))
            }
            SubCommand::Clean => Ok(Message::Clean),
            SubCommand::Reset { children } => Ok(Message::Reset(*children)),
            SubCommand::Shutdown => Ok(Message::DaemonShutdown),
            SubCommand::Parallel {
                parallel_tasks,
                group,
            } => {
                let message = ParallelMessage {
                    parallel_tasks: *parallel_tasks,
                    group: group.clone(),
                };
                Ok(Message::Parallel(message))
            }
            SubCommand::Completions { .. } => bail!("Completions have to be handled earlier"),
            SubCommand::Restart { .. } => bail!("Restarts have to be handled earlier"),
            SubCommand::Edit { .. } => bail!("Edits have to be handled earlier"),
        }
    }
}
<commit_msg>Fix formatting<commit_after>use std::collections::HashMap;
use std::env::{current_dir, vars};
use std::io::{self, Write};

use anyhow::{bail, Context, Result};
use log::error;

use pueue::message::*;
use pueue::platform::socket::*;
use pueue::protocol::*;
use pueue::settings::Settings;

use crate::cli::{Opt, SubCommand};
use crate::commands::edit::*;
use crate::commands::local_follow::*;
use crate::commands::restart::*;
use crate::output::*;

/// This struct contains the base logic for the client.
/// The client is responsible for connecting to the daemon, sending instructions
/// and interpreting their responses.
///
/// Most commands are a simple ping-pong. However, some commands require a more complex
/// communication pattern, such as the `follow` command, which continuously streams the output of a task.
pub struct Client {
    opt: Opt,
    settings: Settings,
    socket: Socket,
}

impl Client {
    /// Connect to the daemon, authorize via secret and return a new initialized Client.
    pub async fn new(settings: Settings, opt: Opt) -> Result<Self> {
        // // Commandline argument overwrites the configuration files values for address
        // let address = if let Some(address) = opt.address.clone() {
        //     address
        // } else {
        //     settings.client.daemon_address
        // };

        // Commandline argument overwrites the configuration files values for port
        let (unix_socket_path, port) = {
            // Return the unix socket path, if we're supposed to use it.
            if settings.shared.use_unix_socket {
                (Some(settings.shared.unix_socket_path.clone()), None)
            } else {
                // Otherwise use tcp sockets and a given port
                // Commandline argument overwrites the configuration files values for port.
                let port = if let Some(port) = opt.port.clone() {
                    port
                } else {
                    settings.shared.port.clone()
                };

                (None, Some(port))
            }
        };

        let mut socket = get_client(unix_socket_path, port).await?;

        // Send the secret to the daemon
        let secret = settings.shared.secret.clone().into_bytes();
        send_bytes(secret, &mut socket).await?;

        Ok(Client {
            opt,
            settings,
            socket,
        })
    }

    /// This is the function where the actual communication and logic starts.
    /// At this point everything is initialized, the connection is up and
    /// we can finally start doing stuff.
    ///
    /// The command handling is splitted into "simple" and "complex" commands.
    pub async fn start(&mut self) -> Result<()> {
        // Return early, if the command has already been handled.
        if self.handle_complex_command().await? {
            return Ok(());
        }

        // The handling of "generic" commands is encapsulated in this function.
        self.handle_simple_command().await?;

        Ok(())
    }

    /// Handle all complex client-side functionalities.
    /// Complex functionalities need some special handling and are contained
    /// in their own functions with their own communication code.
    /// Such functionalities includes reading local files, data streaming
    /// and sending multiple messages.
    ///
    /// Returns `true`, if the current command has been handled by this function.
    /// This indicates that the client can now shut down.
    async fn handle_complex_command(&mut self) -> Result<bool> {
        // This match handles all "complex" commands.
        match &self.opt.cmd {
            SubCommand::Edit { task_id, path } => {
                let message = edit(&mut self.socket, *task_id, *path).await?;
                self.handle_response(message);
                Ok(true)
            }
            SubCommand::Restart {
                task_ids,
                start_immediately,
                stashed,
                edit,
                path,
            } => {
                restart(
                    &mut self.socket,
                    task_ids.clone(),
                    *start_immediately,
                    *stashed,
                    *edit,
                    *path,
                )
                .await?;
                Ok(true)
            }
            SubCommand::Follow { task_id, err } => {
                // Simple log output follows for local logs don't need any communication with the daemon.
                // Thereby we handle this separately over here.
                if self.settings.client.read_local_logs {
                    local_follow(
                        &mut self.socket,
                        self.settings.shared.pueue_directory.clone(),
                        task_id,
                        *err,
                    )
                    .await?;
                    return Ok(true);
                }
                Ok(false)
            }
            _ => Ok(false),
        }
    }

    /// Handle logic that's super generic on the client-side.
    /// This always follows a singular ping-pong pattern.
    /// One message to the daemon, one response, Done.
    async fn handle_simple_command(&mut self) -> Result<()> {
        // Create the message that should be sent to the daemon
        // depending on the given commandline options.
        let message = self.get_message_from_opt()?;

        // Create the message payload and send it to the daemon.
        send_message(message, &mut self.socket).await?;

        // Check if we can receive the response from the daemon
        let mut response = receive_message(&mut self.socket).await?;

        // Check if we can receive the response from the daemon
        while self.handle_response(response) {
            response = receive_message(&mut self.socket).await?;
        }

        Ok(())
    }

    /// Most returned messages can be handled in a generic fashion.
    /// However, some commands require continuous receiving of messages (streaming).
    ///
    /// If this function returns `Ok(true)`, the parent function will continue to receive
    /// and handle messages from the daemon. Otherwise the client will simply exit.
    fn handle_response(&self, message: Message) -> bool {
        match message {
            Message::Success(text) => print_success(&text),
            Message::Failure(text) => print_error(&text),
            Message::StatusResponse(state) => print_state(state, &self.opt.cmd),
            Message::LogResponse(task_logs) => print_logs(task_logs, &self.opt.cmd, &self.settings),
            Message::Stream(text) => {
                print!("{}", text);
                io::stdout().flush().unwrap();
                return true;
            }
            _ => error!("Received unhandled response message"),
        };

        false
    }

    /// Prints a warning and prompt for given action and tasks.
    /// Returns `Ok(())` if the action was confirmed.
    fn handle_user_confirmation(&self, action: &str, task_ids: &Vec<usize>) -> Result<()> {
        // printing warning and prompt
        println!(
            "You are trying to {}: {}",
            action,
            task_ids
                .into_iter()
                .map(|t| format!("task{}", t.to_string()))
                .collect::<Vec<String>>()
                .join(", ")
        );
        print!("\nDo you want to continue [Y/n]: ");

        let mut input = String::new();
        io::stdout().flush().unwrap();
        io::stdin().read_line(&mut input)?;

        if input.starts_with("n") || input.starts_with("N") {
            bail!("User did not confirm");
        }

        Ok(())
    }

    /// Convert the cli command into the message that's being sent to the server,
    /// so it can be understood by the daemon.
    fn get_message_from_opt(&self) -> Result<Message> {
        match &self.opt.cmd {
            SubCommand::Add {
                command,
                start_immediately,
                stashed,
                group,
                delay_until,
                dependencies,
            } => {
                let cwd_pathbuf = current_dir()?;
                let cwd = cwd_pathbuf
                    .to_str()
                    .context("Cannot parse current working directory (Invalid utf8?)")?;

                let mut envs = HashMap::new();
                // Save all environment variables for later injection into the started task
                for (key, value) in vars() {
                    envs.insert(key, value);
                }

                Ok(Message::Add(AddMessage {
                    command: command.join(" "),
                    path: cwd.to_string(),
                    envs,
                    start_immediately: *start_immediately,
                    stashed: *stashed,
                    group: group.clone(),
                    enqueue_at: *delay_until,
                    dependencies: dependencies.to_vec(),
                    ignore_aliases: false,
                }))
            }
            SubCommand::Remove { task_ids } => {
                if self.settings.client.print_remove_warnings {
                    match self.handle_user_confirmation("remove", task_ids) {
                        Ok(_) => (),
                        Err(_) => std::process::exit(1),
                    }
                }
                Ok(Message::Remove(task_ids.clone()))
            }
            SubCommand::Stash { task_ids } => Ok(Message::Stash(task_ids.clone())),
            SubCommand::Switch {
                task_id_1,
                task_id_2,
            } => {
                let message = SwitchMessage {
                    task_id_1: *task_id_1,
                    task_id_2: *task_id_2,
                };
                Ok(Message::Switch(message))
            }
            SubCommand::Enqueue {
                task_ids,
                delay_until,
            } => {
                let message = EnqueueMessage {
                    task_ids: task_ids.clone(),
                    enqueue_at: *delay_until,
                };
                Ok(Message::Enqueue(message))
            }
            SubCommand::Start {
                task_ids,
                group,
                all,
                children,
            } => {
                let message = StartMessage {
                    task_ids: task_ids.clone(),
                    group: group.clone(),
                    all: *all,
                    children: *children,
                };
                Ok(Message::Start(message))
            }
            SubCommand::Pause {
                task_ids,
                group,
                wait,
                all,
                children,
            } => {
                let message = PauseMessage {
                    task_ids: task_ids.clone(),
                    group: group.clone(),
                    wait: *wait,
                    all: *all,
                    children: *children,
                };
                Ok(Message::Pause(message))
            }
            SubCommand::Kill {
                task_ids,
                group,
                default,
                all,
                children,
            } => {
                if self.settings.client.print_remove_warnings {
                    match self.handle_user_confirmation("kill", task_ids) {
                        Ok(_) => (),
                        Err(_) => std::process::exit(1),
                    }
                }
                let message = KillMessage {
                    task_ids: task_ids.clone(),
                    group: group.clone(),
                    default: *default,
                    all: *all,
                    children: *children,
                };
                Ok(Message::Kill(message))
            }
            SubCommand::Send { task_id, input } => {
                let message = SendMessage {
                    task_id: *task_id,
                    input: input.clone(),
                };
                Ok(Message::Send(message))
            }
            SubCommand::Group { add, remove } => {
                let message = GroupMessage {
                    add: add.clone(),
                    remove: remove.clone(),
                };
                Ok(Message::Group(message))
            }
            SubCommand::Status { .. } => Ok(Message::Status),
            SubCommand::Log { task_ids, .. } => {
                let message = LogRequestMessage {
                    task_ids: task_ids.clone(),
                    send_logs: !self.settings.client.read_local_logs,
                };
                Ok(Message::Log(message))
            }
            SubCommand::Follow { task_id, err } => {
                let message = StreamRequestMessage {
                    task_id: *task_id,
                    err: *err,
                };
                Ok(Message::StreamRequest(message))
            }
            SubCommand::Clean => Ok(Message::Clean),
            SubCommand::Reset { children } => Ok(Message::Reset(*children)),
            SubCommand::Shutdown => Ok(Message::DaemonShutdown),
            SubCommand::Parallel {
                parallel_tasks,
                group,
            } => {
                let message = ParallelMessage {
                    parallel_tasks: *parallel_tasks,
                    group: group.clone(),
                };
                Ok(Message::Parallel(message))
            }
            SubCommand::Completions { .. } => bail!("Completions have to be handled earlier"),
            SubCommand::Restart { .. } => bail!("Restarts have to be handled earlier"),
            SubCommand::Edit { .. } => bail!("Edits have to be handled earlier"),
        }
    }
}
<|endoftext|>