<commit_before>

use visit::IntoNeighbors;
use visit::{VisitMap, Visitable};

/// Strictly monotonically increasing event time for a depth first search.
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Default, Hash)]
pub struct Time(pub usize);

/// A depth first search (DFS) visitor event.
#[derive(Copy, Clone, Debug)]
pub enum DfsEvent<N> {
    Discover(N, Time),
    /// An edge of the tree formed by the traversal.
    TreeEdge(N, N),
    /// An edge to an already visited node.
    BackEdge(N, N),
    /// A cross or forward edge.
    ///
    /// For an edge *(u, v)*, if the discover time of *v* is greater than *u*,
    /// then it is a forward edge, else a cross edge.
    CrossForwardEdge(N, N),
    /// All edges from a node have been reported.
    Finish(N, Time),
}

/// Return if the expression is a break value, execute the provided statement
/// if it is a prune value.
macro_rules! try_control {
    ($e:expr, $p:stmt) => {
        match $e {
            x => if x.should_break() {
                return x;
            } else if x.should_prune() {
                $p;
            }
        }
    }
}

/// Control flow for `depth_first_search` callbacks.
#[derive(Copy, Clone, Debug)]
pub enum Control<B> {
    /// Continue the DFS traversal as normal.
    Continue,
    /// Prune the current node from the DFS traversal. No more edges from this
    /// node will be reported to the callback. A `DfsEvent::Finish` for this
    /// node will still be reported. This can be returned in response to any
    /// `DfsEvent`, except `Finish`, which will panic.
    Prune,
    /// Stop the DFS traversal and return the provided value.
    Break(B),
}

impl<B> Control<B> {
    pub fn breaking() -> Control<()> { Control::Break(()) }
    /// Get the value in `Control::Break(_)`, if present.
    pub fn break_value(self) -> Option<B> {
        match self {
            Control::Continue | Control::Prune => None,
            Control::Break(b) => Some(b),
        }
    }
}

/// Control flow for callbacks.
///
/// The empty return value `()` is equivalent to continue.
pub trait ControlFlow {
    fn continuing() -> Self;
    fn should_break(&self) -> bool;
    fn should_prune(&self) -> bool;
}

impl ControlFlow for () {
    fn continuing() { }
    #[inline]
    fn should_break(&self) -> bool { false }
    #[inline]
    fn should_prune(&self) -> bool { false }
}

impl<B> ControlFlow for Control<B> {
    fn continuing() -> Self { Control::Continue }
    fn should_break(&self) -> bool {
        if let Control::Break(_) = *self { true } else { false }
    }
    fn should_prune(&self) -> bool {
        match *self {
            Control::Prune => true,
            Control::Continue | Control::Break(_) => false,
        }
    }
}

impl<C: ControlFlow, E> ControlFlow for Result<C, E> {
    fn continuing() -> Self { Ok(C::continuing()) }
    fn should_break(&self) -> bool {
        self.is_err()
    }
    fn should_prune(&self) -> bool {
        if let Ok(ref c) = *self { c.should_prune() } else { false }
    }
}

/// The default is `Continue`.
impl<B> Default for Control<B> {
    fn default() -> Self { Control::Continue }
}

/// A recursive depth first search.
///
/// Starting points are the nodes in the iterator `starts` (specify just one
/// start vertex *x* by using `Some(x)`).
///
/// The traversal emits discovery and finish events for each reachable vertex,
/// and edge classification of each reachable edge. `visitor` is called for each
/// event, see [`DfsEvent`][de] for possible values.
///
/// If the return value of the visitor is simply `()`, the visit runs until it
/// is finished. If the return value is a `Control<B>`, it can be used to
/// change the control flow of the search. `Control::Break` will stop the
/// the visit early, returning the contained value from the function.
/// `Control::Prune` will stop traversing any additional edges from the current
/// node and proceed immediately to the `Finish` event. Attempting to prune a
/// node from its `Finish` event will cause a panic.
///
/// [de]: enum.DfsEvent.html
///
/// # Example
///
/// Find a path from vertex 0 to 5, and exit the visit as soon as we reach
/// the goal vertex.
///
/// ```
/// use petgraph::prelude::*;
/// use petgraph::graph::node_index as n;
/// use petgraph::visit::depth_first_search;
/// use petgraph::visit::{DfsEvent, Control};
///
/// let gr: Graph<(), ()> = Graph::from_edges(&[
///     (0, 1), (0, 2), (0, 3),
///     (1, 3),
///     (2, 3), (2, 4),
///     (4, 0), (4, 5),
/// ]);
///
/// // record each predecessor, mapping node → node
/// let mut predecessor = vec![NodeIndex::end(); gr.node_count()];
/// let start = n(0);
/// let goal = n(5);
/// depth_first_search(&gr, Some(start), |event| {
///     if let DfsEvent::TreeEdge(u, v) = event {
///         predecessor[v.index()] = u;
///         if v == goal {
///             return Control::Break(v);
///         }
///     }
///     Control::Continue
/// });
///
/// let mut next = goal;
/// let mut path = vec![next];
/// while next != start {
///     let pred = predecessor[next.index()];
///     path.push(pred);
///     next = pred;
/// }
/// path.reverse();
/// assert_eq!(&path, &[n(0), n(2), n(4), n(5)]);
/// ```
pub fn depth_first_search<G, I, F, C>(graph: G, starts: I, mut visitor: F) -> C
    where G: IntoNeighbors + Visitable,
          I: IntoIterator<Item=G::NodeId>,
          F: FnMut(DfsEvent<G::NodeId>) -> C,
          C: ControlFlow,
{
    let time = &mut Time(0);
    let discovered = &mut graph.visit_map();
    let finished = &mut graph.visit_map();

    for start in starts {
        try_control!(dfs_visitor(graph, start, &mut visitor, discovered, finished, time),
                     unreachable!());
    }
    C::continuing()
}

fn dfs_visitor<G, F, C>(graph: G, u: G::NodeId, visitor: &mut F,
                     discovered: &mut G::Map, finished: &mut G::Map,
                     time: &mut Time) -> C
    where G: IntoNeighbors + Visitable,
          F: FnMut(DfsEvent<G::NodeId>) -> C,
          C: ControlFlow,
{
    if !discovered.visit(u) {
        return C::continuing();
    }

    'prune: loop {
        try_control!(visitor(DfsEvent::Discover(u, time_post_inc(time))), break 'prune);
        for v in graph.neighbors(u) {
            if !discovered.is_visited(&v) {
                try_control!(visitor(DfsEvent::TreeEdge(u, v)), continue);
                try_control!(dfs_visitor(graph, v, visitor, discovered, finished, time),
                             unreachable!());
            } else if !finished.is_visited(&v) {
                try_control!(visitor(DfsEvent::BackEdge(u, v)), continue);
            } else {
                try_control!(visitor(DfsEvent::CrossForwardEdge(u, v)), continue);
            }
        }

        break;
    }
    let first_finish = finished.visit(u);
    debug_assert!(first_finish);
    try_control!(visitor(DfsEvent::Finish(u, time_post_inc(time))),
                 panic!("Pruning on the `DfsEvent::Finish` is not supported!"));
    C::continuing()
}

fn time_post_inc(x: &mut Time) -> Time {
    let v = *x;
    x.0 += 1;
    v
}
<commit_msg>Address review comments.<commit_after>

use visit::IntoNeighbors;
use visit::{VisitMap, Visitable};

/// Strictly monotonically increasing event time for a depth first search.
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Default, Hash)]
pub struct Time(pub usize);

/// A depth first search (DFS) visitor event.
#[derive(Copy, Clone, Debug)]
pub enum DfsEvent<N> {
    Discover(N, Time),
    /// An edge of the tree formed by the traversal.
    TreeEdge(N, N),
    /// An edge to an already visited node.
    BackEdge(N, N),
    /// A cross or forward edge.
    ///
    /// For an edge *(u, v)*, if the discover time of *v* is greater than *u*,
    /// then it is a forward edge, else a cross edge.
    CrossForwardEdge(N, N),
    /// All edges from a node have been reported.
    Finish(N, Time),
}

/// Return if the expression is a break value, execute the provided statement
/// if it is a prune value.
macro_rules! try_control {
    ($e:expr, $p:stmt) => {
        match $e {
            x => if x.should_break() {
                return x;
            } else if x.should_prune() {
                $p;
            }
        }
    }
}

/// Control flow for `depth_first_search` callbacks.
#[derive(Copy, Clone, Debug)]
pub enum Control<B> {
    /// Continue the DFS traversal as normal.
    Continue,
    /// Prune the current node from the DFS traversal. No more edges from this
    /// node will be reported to the callback. A `DfsEvent::Finish` for this
    /// node will still be reported. This can be returned in response to any
    /// `DfsEvent`, except `Finish`, which will panic.
    Prune,
    /// Stop the DFS traversal and return the provided value.
    Break(B),
}

impl<B> Control<B> {
    pub fn breaking() -> Control<()> { Control::Break(()) }
    /// Get the value in `Control::Break(_)`, if present.
    pub fn break_value(self) -> Option<B> {
        match self {
            Control::Continue | Control::Prune => None,
            Control::Break(b) => Some(b),
        }
    }
}

/// Control flow for callbacks.
///
/// The empty return value `()` is equivalent to continue.
pub trait ControlFlow {
    fn continuing() -> Self;
    fn should_break(&self) -> bool;
    fn should_prune(&self) -> bool;
}

impl ControlFlow for () {
    fn continuing() { }
    #[inline]
    fn should_break(&self) -> bool { false }
    #[inline]
    fn should_prune(&self) -> bool { false }
}

impl<B> ControlFlow for Control<B> {
    fn continuing() -> Self { Control::Continue }
    fn should_break(&self) -> bool {
        if let Control::Break(_) = *self { true } else { false }
    }
    fn should_prune(&self) -> bool {
        match *self {
            Control::Prune => true,
            Control::Continue | Control::Break(_) => false,
        }
    }
}

impl<C: ControlFlow, E> ControlFlow for Result<C, E> {
    fn continuing() -> Self { Ok(C::continuing()) }
    fn should_break(&self) -> bool {
        if let Ok(ref c) = *self { c.should_break() } else { true }
    }
    fn should_prune(&self) -> bool {
        if let Ok(ref c) = *self { c.should_prune() } else { false }
    }
}

/// The default is `Continue`.
impl<B> Default for Control<B> {
    fn default() -> Self { Control::Continue }
}

/// A recursive depth first search.
///
/// Starting points are the nodes in the iterator `starts` (specify just one
/// start vertex *x* by using `Some(x)`).
///
/// The traversal emits discovery and finish events for each reachable vertex,
/// and edge classification of each reachable edge. `visitor` is called for each
/// event, see [`DfsEvent`][de] for possible values.
///
/// If the return value of the visitor is simply `()`, the visit runs until it
/// is finished. If the return value is a `Control<B>`, it can be used to
/// change the control flow of the search. `Control::Break` will stop the
/// the visit early, returning the contained value from the function.
/// `Control::Prune` will stop traversing any additional edges from the current
/// node and proceed immediately to the `Finish` event.
///
/// ***Panics** if you attempt to prune a node from its `Finish` event.
///
/// [de]: enum.DfsEvent.html
///
/// # Example
///
/// Find a path from vertex 0 to 5, and exit the visit as soon as we reach
/// the goal vertex.
///
/// ```
/// use petgraph::prelude::*;
/// use petgraph::graph::node_index as n;
/// use petgraph::visit::depth_first_search;
/// use petgraph::visit::{DfsEvent, Control};
///
/// let gr: Graph<(), ()> = Graph::from_edges(&[
///     (0, 1), (0, 2), (0, 3),
///     (1, 3),
///     (2, 3), (2, 4),
///     (4, 0), (4, 5),
/// ]);
///
/// // record each predecessor, mapping node → node
/// let mut predecessor = vec![NodeIndex::end(); gr.node_count()];
/// let start = n(0);
/// let goal = n(5);
/// depth_first_search(&gr, Some(start), |event| {
///     if let DfsEvent::TreeEdge(u, v) = event {
///         predecessor[v.index()] = u;
///         if v == goal {
///             return Control::Break(v);
///         }
///     }
///     Control::Continue
/// });
///
/// let mut next = goal;
/// let mut path = vec![next];
/// while next != start {
///     let pred = predecessor[next.index()];
///     path.push(pred);
///     next = pred;
/// }
/// path.reverse();
/// assert_eq!(&path, &[n(0), n(2), n(4), n(5)]);
/// ```
pub fn depth_first_search<G, I, F, C>(graph: G, starts: I, mut visitor: F) -> C
    where G: IntoNeighbors + Visitable,
          I: IntoIterator<Item=G::NodeId>,
          F: FnMut(DfsEvent<G::NodeId>) -> C,
          C: ControlFlow,
{
    let time = &mut Time(0);
    let discovered = &mut graph.visit_map();
    let finished = &mut graph.visit_map();

    for start in starts {
        try_control!(dfs_visitor(graph, start, &mut visitor, discovered, finished, time),
                     unreachable!());
    }
    C::continuing()
}

fn dfs_visitor<G, F, C>(graph: G, u: G::NodeId, visitor: &mut F,
                     discovered: &mut G::Map, finished: &mut G::Map,
                     time: &mut Time) -> C
    where G: IntoNeighbors + Visitable,
          F: FnMut(DfsEvent<G::NodeId>) -> C,
          C: ControlFlow,
{
    if !discovered.visit(u) {
        return C::continuing();
    }

    'prune: loop {
        try_control!(visitor(DfsEvent::Discover(u, time_post_inc(time))), break 'prune);
        for v in graph.neighbors(u) {
            if !discovered.is_visited(&v) {
                try_control!(visitor(DfsEvent::TreeEdge(u, v)), continue);
                try_control!(dfs_visitor(graph, v, visitor, discovered, finished, time),
                             unreachable!());
            } else if !finished.is_visited(&v) {
                try_control!(visitor(DfsEvent::BackEdge(u, v)), continue);
            } else {
                try_control!(visitor(DfsEvent::CrossForwardEdge(u, v)), continue);
            }
        }

        break;
    }
    let first_finish = finished.visit(u);
    debug_assert!(first_finish);
    try_control!(visitor(DfsEvent::Finish(u, time_post_inc(time))),
                 panic!("Pruning on the `DfsEvent::Finish` is not supported!"));
    C::continuing()
}

fn time_post_inc(x: &mut Time) -> Time {
    let v = *x;
    x.0 += 1;
    v
}
<|endoftext|>


<commit_before>//! Directory module for managing Algorithmia Data Directories
//!
//! # Examples
//!
//! ```no_run
//! use algorithmia::Algorithmia;
//! use algorithmia::data::DataAcl;
//! use std::fs::File;
//!
//! let client = Algorithmia::client("111112222233333444445555566");
//! let my_dir = client.dir(".my/my_dir");
//!
//! my_dir.create(DataAcl::default());
//! my_dir.put_file("/path/to/file");
//! ```

extern crate chrono;

use client::HttpClient;
use error::{self, Error};
use data::{self, DataFile, DeletedResult, XDataType};
use data::file::FileAdded;
use data::HasDataPath;

use std::io::Read;
use std::fs::File;
use std::path::Path;
use std::vec::IntoIter;
use std::ops::Deref;
use std::error::Error as StdError;

use hyper::header::ContentType;
use hyper::mime::{Mime, TopLevel, SubLevel};
use hyper::Url;
use rustc_serialize::{json, Decoder, Decodable};
use self::chrono::{DateTime, UTC};

/// Algorithmia Data Directory
pub struct DataDir {
    path: String,
    client: HttpClient,
}

#[derive(RustcDecodable, Debug)]
pub struct DirectoryUpdated {
    pub acl: Option<DataAcl>,
}

/// Response when deleting a new Directory
#[derive(RustcDecodable, Debug)]
pub struct DirectoryDeleted {
    // Omitting deleted.number and error.number for now
    pub result: DeletedResult,
}

#[derive(RustcDecodable, RustcEncodable, Debug)]
struct FolderItem {
    pub name: String,
    pub acl: Option<DataAcl>,
}

#[derive(Debug)]
struct FileItem {
    pub filename: String,
    pub size: u64,
    pub last_modified: DateTime<UTC>,
}

// Manual implemented Decodable: https://github.com/lifthrasiir/rust-chrono/issues/43
impl Decodable for FileItem {
    fn decode<D: Decoder>(d: &mut D) -> Result<FileItem, D::Error> {
        d.read_struct("root", 0, |d| {
            Ok(FileItem{
                filename: try!(d.read_struct_field("filename", 0, |d| Decodable::decode(d))),
                size: try!(d.read_struct_field("size", 0, |d| Decodable::decode(d))),
                last_modified: {
                    let json_str: String = try!(d.read_struct_field("last_modified", 0, |d| Decodable::decode(d)));
                    match json_str.parse() {
                        Ok(datetime) => datetime,
                        Err(err) => return Err(d.error(err.description())),
                    }
                },
            })
        })
    }
}

/// ACL that indicates permissions for a DataDirectory
/// See also: [ReadAcl](enum.ReadAcl.html) enum to construct a DataACL
#[derive(RustcDecodable, RustcEncodable, Debug)]
pub struct DataAcl {
    pub read: Vec<String>
}

pub enum ReadAcl {
  /// Readable only by owner
  Private,
  /// Readable by owner's algorithms (regardless of caller)
  MyAlgorithms,
  /// Readable by any user
  Public,
}

impl Default for DataAcl {
  fn default() -> Self {
    ReadAcl::MyAlgorithms.into()
  }
}

impl From<ReadAcl> for DataAcl {
    fn from(acl: ReadAcl) -> Self {
      match acl {
        ReadAcl::Private => DataAcl { read: vec![] },
        ReadAcl::MyAlgorithms => DataAcl { read: vec!["algo://.my/*".into()] },
        ReadAcl::Public => DataAcl { read: vec!["user://*".into()] },
      }
    }
}

/// Response when querying an existing Directory
#[derive(RustcDecodable, Debug)]
struct DirectoryShow {
    pub acl: Option<DataAcl>,
    pub folders: Option<Vec<FolderItem>>,
    pub files: Option<Vec<FileItem>>,
    pub marker: Option<String>,
}

pub struct DirectoryListing<'a> {
    pub acl: Option<DataAcl>,
    dir: &'a DataDir,
    folders: IntoIter<FolderItem>,
    files: IntoIter<FileItem>,
    marker: Option<String>,
    query_count: u32,
}

impl <'a> DirectoryListing<'a> {
    fn new(dir: &'a DataDir) -> DirectoryListing<'a> {
        DirectoryListing {
            acl: None,
            dir: dir,
            folders: Vec::new().into_iter(),
            files: Vec::new().into_iter(),
            marker: None,
            query_count: 0,
        }
    }
}

pub struct DataFileEntry {
    pub size: u64,
    pub last_modified: DateTime<UTC>,
    file: DataFile,
}

impl Deref for DataFileEntry {
    type Target = DataFile;
    fn deref(&self) -> &DataFile {&self.file}
}

pub enum DirEntry {
    File(DataFileEntry),
    Dir(DataDir),
}

impl <'a> Iterator for DirectoryListing<'a> {
    type Item = Result<DirEntry, Error>;

    fn next(&mut self) -> Option<Self::Item> {
        match self.folders.next() {
            // Return folders first
            Some(d) => Some(Ok(DirEntry::Dir(self.dir.child(&d.name)))),
            None => match self.files.next() {
                // Return files second
                Some(f) => Some(Ok(DirEntry::File(
                    DataFileEntry{
                        size: f.size,
                        last_modified: f.last_modified,
                        file: self.dir.child(&f.filename),
                    }
                ))),
                None => {
                    // Query if there is another page of files/folders
                    if self.query_count == 0 || self.marker.is_some() {
                        self.query_count = self.query_count + 1;
                        match get_directory(self.dir, self.marker.clone()) {
                            Ok(ds) => {
                                self.folders = ds.folders.unwrap_or(Vec::new()).into_iter();
                                self.files = ds.files.unwrap_or(Vec::new()).into_iter();
                                self.marker = ds.marker;
                                self.next()
                            }
                            Err(err) => Some(Err(err)),
                        }
                    } else {
                        None
                    }
                }
            }
        }
    }
}

fn get_directory(dir: &DataDir, marker: Option<String>) -> Result<DirectoryShow, Error> {
    let url = match marker {
        Some(m) => Url::parse(&format!("{}?marker={}", dir.to_url(), m)).unwrap(),
        None => dir.to_url(),
    };

    let req = dir.client.get(url);
    let mut res = try!(req.send());

    if res.status.is_success() {
        if let Some(data_type) = res.headers.get::<XDataType>() {
            if "directory" != data_type.to_string() {
                return Err(Error::DataTypeError(format!("Expected directory, Received {}", data_type)));
            }
        }
    }

    let mut res_json = String::new();
    try!(res.read_to_string(&mut res_json));

    match res.status.is_success() {
        true => json::decode(&res_json).map_err(|err| err.into()),
        false => Err(error::decode(&res_json)),
    }
}

impl HasDataPath for DataDir {
    fn new(client: HttpClient, path: &str) -> Self { DataDir { client: client, path: data::parse_data_uri(path).to_string() } }
    fn path(&self) -> &str { &self.path }
    fn client(&self) -> &HttpClient { &self.client }
}

impl DataDir {
    /// Display Directory details if it exists
    ///
    /// # Examples
    /// ```no_run
    /// # use algorithmia::Algorithmia;
    /// # use algorithmia::data::{DirEntry, HasDataPath};
    /// let client = Algorithmia::client("111112222233333444445555566");
    /// let my_dir = client.dir(".my/my_dir");
    /// let dir_list = my_dir.list();
    /// for entry in dir_list {
    ///     match entry {
    ///         Ok(DirEntry::File(f)) => println!("File: {}", f.to_data_uri()),
    ///         Ok(DirEntry::Dir(d)) => println!("Dir: {}", d.to_data_uri()),
    ///         Err(err) => { println!("Error: {}", err); break; },
    ///     }
    /// };
    /// ```
    pub fn list(&self) -> DirectoryListing {
        DirectoryListing::new(self)
    }

    /// Create a Directory
    ///
    /// Use `DataAcl::default()` or the `ReadAcl` enum to set the ACL
    ///
    /// # Examples
    /// ```no_run
    /// # use algorithmia::Algorithmia;
    /// # use algorithmia::data::DataAcl;
    /// let client = Algorithmia::client("111112222233333444445555566");
    /// let my_dir = client.dir(".my/my_dir");
    /// match my_dir.create(DataAcl::default()) {
    ///   Ok(_) => println!("Successfully created Directory"),
    ///   Err(e) => println!("Error created directory: {}", e),
    /// };
    /// ```
    pub fn create<Acl: Into<DataAcl>>(&self, acl: Acl) -> Result<(), Error> {
        let parent = try!(self.parent().ok_or(Error::DataPathError("has no parent".into())));
        let url = parent.to_url();

        // TODO: address complete abuse of this structure
        let input_data = FolderItem {
            name: try!(self.basename().ok_or(Error::DataPathError("has no basename".into()))).into(),
            acl: Some(acl.into()),
        };
        let raw_input = try!(json::encode(&input_data));

        // POST request
        let req = self.client.post(url)
            .header(ContentType(Mime(TopLevel::Application, SubLevel::Json, vec![])))
            .body(&raw_input);

        // Parse response
        let mut res = try!(req.send());

        match res.status.is_success() {
            true => Ok(()),
            false => {
                let mut res_json = String::new();
                try!(res.read_to_string(&mut res_json));
                Err(error::decode(&res_json))
            }
        }
    }

    /// Delete a Directory
    ///
    /// # Examples
    /// ```no_run
    /// # use algorithmia::Algorithmia;
    /// let client = Algorithmia::client("111112222233333444445555566");
    /// let my_dir = client.dir(".my/my_dir");
    /// match my_dir.delete(false) {
    ///   Ok(_) => println!("Successfully deleted Directory"),
    ///   Err(err) => println!("Error deleting directory: {}", err),
    /// };
    /// ```
    pub fn delete(&self, force: bool) -> Result<DirectoryDeleted, Error> {
        // DELETE request
        let url_string = format!("{}?force={}", self.to_url(), force.to_string());
        let url = Url::parse(&url_string).unwrap();
        let req = self.client.delete(url);

        // Parse response
        let mut res = try!(req.send());
        let mut res_json = String::new();
        try!(res.read_to_string(&mut res_json));

        match res.status.is_success() {
            true => json::decode(&res_json).map_err(|err| err.into()),
            false => Err(error::decode(&res_json)),
        }
    }

    /// Upload a file to an existing Directory
    ///
    /// # Examples
    /// ```no_run
    /// # use algorithmia::Algorithmia;
    /// let client = Algorithmia::client("111112222233333444445555566");
    /// let my_dir = client.dir(".my/my_dir");
    ///
    /// match my_dir.put_file("/path/to/file") {
    ///   Ok(response) => println!("Successfully uploaded to: {}", response.result),
    ///   Err(err) => println!("Error uploading file: {}", err),
    /// };
    /// ```
    pub fn put_file<P: AsRef<Path>>(&self, file_path: P) -> Result<FileAdded, Error> {
        // FIXME: A whole lot of unwrap going on here...
        let path_ref = file_path.as_ref();
        let url_string = format!("{}/{}",
            self.to_url(),
            path_ref.file_name().unwrap().to_str().unwrap()
        );
        let url = Url::parse(&url_string).unwrap();

        let mut file = File::open(path_ref).unwrap();
        let req = self.client.put(url).body(&mut file);

        let mut res = try!(req.send());
        let mut res_json = String::new();
        try!(res.read_to_string(&mut res_json));

        match res.status.is_success() {
            true => json::decode(&res_json).map_err(|err| err.into()),
            false => Err(error::decode(&res_json)),
        }
    }

    pub fn child<T: HasDataPath>(&self, filename: &str) -> T {
        T::new(self.client.clone(), &format!("{}/{}", self.to_data_uri(), filename))
    }
}

#[cfg(test)]
mod tests {
    use data::HasDataPath;
    use super::*;
    use Algorithmia;

    fn mock_client() -> Algorithmia { Algorithmia::client("") }

    #[test]
    fn test_to_url() {
        let dir = mock_client().dir("data://anowell/foo");
        assert_eq!(dir.to_url().serialize_path().unwrap(), "/v1/data/anowell/foo");
    }

    #[test]
    fn test_to_data_uri() {
        let dir = mock_client().dir("/anowell/foo");
        assert_eq!(dir.to_data_uri(), "data://anowell/foo".to_string());
    }

    #[test]
    fn test_parent() {
        let dir = mock_client().dir("data://anowell/foo");
        let expected = mock_client().dir("data://anowell");
        assert_eq!(dir.parent().unwrap().path, expected.path);
    }

    #[test]
    fn test_default_acl() {
        let acl: DataAcl = DataAcl::default();
        assert_eq!(acl.read, vec!["algo://.my/*".to_string()]);
    }

    #[test]
    fn test_private_acl() {
        let acl: DataAcl = ReadAcl::Private.into();
        assert!(acl.read.is_empty());
    }

    #[test]
    fn test_public_acl() {
        let acl: DataAcl = ReadAcl::Public.into();
        assert_eq!(acl.read, vec!["user://*".to_string()]);
    }

    #[test]
    fn test_myalgos_acl() {
        let acl: DataAcl = ReadAcl::MyAlgorithms.into();
        assert_eq!(acl.read, vec!["algo://.my/*".to_string()]);
    }

}<commit_msg>Fix .child to work from root<commit_after>//! Directory module for managing Algorithmia Data Directories
//!
//! # Examples
//!
//! ```no_run
//! use algorithmia::Algorithmia;
//! use algorithmia::data::DataAcl;
//! use std::fs::File;
//!
//! let client = Algorithmia::client("111112222233333444445555566");
//! let my_dir = client.dir(".my/my_dir");
//!
//! my_dir.create(DataAcl::default());
//! my_dir.put_file("/path/to/file");
//! ```

extern crate chrono;

use client::HttpClient;
use error::{self, Error};
use data::{self, DataFile, DeletedResult, XDataType};
use data::file::FileAdded;
use data::HasDataPath;

use std::io::Read;
use std::fs::File;
use std::path::Path;
use std::vec::IntoIter;
use std::ops::Deref;
use std::error::Error as StdError;

use hyper::header::ContentType;
use hyper::mime::{Mime, TopLevel, SubLevel};
use hyper::Url;
use rustc_serialize::{json, Decoder, Decodable};
use self::chrono::{DateTime, UTC};

/// Algorithmia Data Directory
pub struct DataDir {
    path: String,
    client: HttpClient,
}

#[derive(RustcDecodable, Debug)]
pub struct DirectoryUpdated {
    pub acl: Option<DataAcl>,
}

/// Response when deleting a new Directory
#[derive(RustcDecodable, Debug)]
pub struct DirectoryDeleted {
    // Omitting deleted.number and error.number for now
    pub result: DeletedResult,
}

#[derive(RustcDecodable, RustcEncodable, Debug)]
struct FolderItem {
    pub name: String,
    pub acl: Option<DataAcl>,
}

#[derive(Debug)]
struct FileItem {
    pub filename: String,
    pub size: u64,
    pub last_modified: DateTime<UTC>,
}

// Manual implemented Decodable: https://github.com/lifthrasiir/rust-chrono/issues/43
impl Decodable for FileItem {
    fn decode<D: Decoder>(d: &mut D) -> Result<FileItem, D::Error> {
        d.read_struct("root", 0, |d| {
            Ok(FileItem{
                filename: try!(d.read_struct_field("filename", 0, |d| Decodable::decode(d))),
                size: try!(d.read_struct_field("size", 0, |d| Decodable::decode(d))),
                last_modified: {
                    let json_str: String = try!(d.read_struct_field("last_modified", 0, |d| Decodable::decode(d)));
                    match json_str.parse() {
                        Ok(datetime) => datetime,
                        Err(err) => return Err(d.error(err.description())),
                    }
                },
            })
        })
    }
}

/// ACL that indicates permissions for a DataDirectory
/// See also: [ReadAcl](enum.ReadAcl.html) enum to construct a DataACL
#[derive(RustcDecodable, RustcEncodable, Debug)]
pub struct DataAcl {
    pub read: Vec<String>
}

pub enum ReadAcl {
  /// Readable only by owner
  Private,
  /// Readable by owner's algorithms (regardless of caller)
  MyAlgorithms,
  /// Readable by any user
  Public,
}

impl Default for DataAcl {
  fn default() -> Self {
    ReadAcl::MyAlgorithms.into()
  }
}

impl From<ReadAcl> for DataAcl {
    fn from(acl: ReadAcl) -> Self {
      match acl {
        ReadAcl::Private => DataAcl { read: vec![] },
        ReadAcl::MyAlgorithms => DataAcl { read: vec!["algo://.my/*".into()] },
        ReadAcl::Public => DataAcl { read: vec!["user://*".into()] },
      }
    }
}

/// Response when querying an existing Directory
#[derive(RustcDecodable, Debug)]
struct DirectoryShow {
    pub acl: Option<DataAcl>,
    pub folders: Option<Vec<FolderItem>>,
    pub files: Option<Vec<FileItem>>,
    pub marker: Option<String>,
}

pub struct DirectoryListing<'a> {
    pub acl: Option<DataAcl>,
    dir: &'a DataDir,
    folders: IntoIter<FolderItem>,
    files: IntoIter<FileItem>,
    marker: Option<String>,
    query_count: u32,
}

impl <'a> DirectoryListing<'a> {
    fn new(dir: &'a DataDir) -> DirectoryListing<'a> {
        DirectoryListing {
            acl: None,
            dir: dir,
            folders: Vec::new().into_iter(),
            files: Vec::new().into_iter(),
            marker: None,
            query_count: 0,
        }
    }
}

pub struct DataFileEntry {
    pub size: u64,
    pub last_modified: DateTime<UTC>,
    file: DataFile,
}

impl Deref for DataFileEntry {
    type Target = DataFile;
    fn deref(&self) -> &DataFile {&self.file}
}

pub enum DirEntry {
    File(DataFileEntry),
    Dir(DataDir),
}

impl <'a> Iterator for DirectoryListing<'a> {
    type Item = Result<DirEntry, Error>;

    fn next(&mut self) -> Option<Self::Item> {
        match self.folders.next() {
            // Return folders first
            Some(d) => Some(Ok(DirEntry::Dir(self.dir.child(&d.name)))),
            None => match self.files.next() {
                // Return files second
                Some(f) => Some(Ok(DirEntry::File(
                    DataFileEntry{
                        size: f.size,
                        last_modified: f.last_modified,
                        file: self.dir.child(&f.filename),
                    }
                ))),
                None => {
                    // Query if there is another page of files/folders
                    if self.query_count == 0 || self.marker.is_some() {
                        self.query_count = self.query_count + 1;
                        match get_directory(self.dir, self.marker.clone()) {
                            Ok(ds) => {
                                self.folders = ds.folders.unwrap_or(Vec::new()).into_iter();
                                self.files = ds.files.unwrap_or(Vec::new()).into_iter();
                                self.marker = ds.marker;
                                self.next()
                            }
                            Err(err) => Some(Err(err)),
                        }
                    } else {
                        None
                    }
                }
            }
        }
    }
}

fn get_directory(dir: &DataDir, marker: Option<String>) -> Result<DirectoryShow, Error> {
    let url = match marker {
        Some(m) => Url::parse(&format!("{}?marker={}", dir.to_url(), m)).unwrap(),
        None => dir.to_url(),
    };

    let req = dir.client.get(url);
    let mut res = try!(req.send());

    if res.status.is_success() {
        if let Some(data_type) = res.headers.get::<XDataType>() {
            if "directory" != data_type.to_string() {
                return Err(Error::DataTypeError(format!("Expected directory, Received {}", data_type)));
            }
        }
    }

    let mut res_json = String::new();
    try!(res.read_to_string(&mut res_json));

    match res.status.is_success() {
        true => json::decode(&res_json).map_err(|err| err.into()),
        false => Err(error::decode(&res_json)),
    }
}

impl HasDataPath for DataDir {
    fn new(client: HttpClient, path: &str) -> Self { DataDir { client: client, path: data::parse_data_uri(path).to_string() } }
    fn path(&self) -> &str { &self.path }
    fn client(&self) -> &HttpClient { &self.client }
}

impl DataDir {
    /// Display Directory details if it exists
    ///
    /// # Examples
    /// ```no_run
    /// # use algorithmia::Algorithmia;
    /// # use algorithmia::data::{DirEntry, HasDataPath};
    /// let client = Algorithmia::client("111112222233333444445555566");
    /// let my_dir = client.dir(".my/my_dir");
    /// let dir_list = my_dir.list();
    /// for entry in dir_list {
    ///     match entry {
    ///         Ok(DirEntry::File(f)) => println!("File: {}", f.to_data_uri()),
    ///         Ok(DirEntry::Dir(d)) => println!("Dir: {}", d.to_data_uri()),
    ///         Err(err) => { println!("Error: {}", err); break; },
    ///     }
    /// };
    /// ```
    pub fn list(&self) -> DirectoryListing {
        DirectoryListing::new(self)
    }

    /// Create a Directory
    ///
    /// Use `DataAcl::default()` or the `ReadAcl` enum to set the ACL
    ///
    /// # Examples
    /// ```no_run
    /// # use algorithmia::Algorithmia;
    /// # use algorithmia::data::DataAcl;
    /// let client = Algorithmia::client("111112222233333444445555566");
    /// let my_dir = client.dir(".my/my_dir");
    /// match my_dir.create(DataAcl::default()) {
    ///   Ok(_) => println!("Successfully created Directory"),
    ///   Err(e) => println!("Error created directory: {}", e),
    /// };
    /// ```
    pub fn create<Acl: Into<DataAcl>>(&self, acl: Acl) -> Result<(), Error> {
        let parent = try!(self.parent().ok_or(Error::DataPathError("has no parent".into())));
        let url = parent.to_url();

        // TODO: address complete abuse of this structure
        let input_data = FolderItem {
            name: try!(self.basename().ok_or(Error::DataPathError("has no basename".into()))).into(),
            acl: Some(acl.into()),
        };
        let raw_input = try!(json::encode(&input_data));

        // POST request
        let req = self.client.post(url)
            .header(ContentType(Mime(TopLevel::Application, SubLevel::Json, vec![])))
            .body(&raw_input);

        // Parse response
        let mut res = try!(req.send());

        match res.status.is_success() {
            true => Ok(()),
            false => {
                let mut res_json = String::new();
                try!(res.read_to_string(&mut res_json));
                Err(error::decode(&res_json))
            }
        }
    }

    /// Delete a Directory
    ///
    /// # Examples
    /// ```no_run
    /// # use algorithmia::Algorithmia;
    /// let client = Algorithmia::client("111112222233333444445555566");
    /// let my_dir = client.dir(".my/my_dir");
    /// match my_dir.delete(false) {
    ///   Ok(_) => println!("Successfully deleted Directory"),
    ///   Err(err) => println!("Error deleting directory: {}", err),
    /// };
    /// ```
    pub fn delete(&self, force: bool) -> Result<DirectoryDeleted, Error> {
        // DELETE request
        let url_string = format!("{}?force={}", self.to_url(), force.to_string());
        let url = Url::parse(&url_string).unwrap();
        let req = self.client.delete(url);

        // Parse response
        let mut res = try!(req.send());
        let mut res_json = String::new();
        try!(res.read_to_string(&mut res_json));

        match res.status.is_success() {
            true => json::decode(&res_json).map_err(|err| err.into()),
            false => Err(error::decode(&res_json)),
        }
    }

    /// Upload a file to an existing Directory
    ///
    /// # Examples
    /// ```no_run
    /// # use algorithmia::Algorithmia;
    /// let client = Algorithmia::client("111112222233333444445555566");
    /// let my_dir = client.dir(".my/my_dir");
    ///
    /// match my_dir.put_file("/path/to/file") {
    ///   Ok(response) => println!("Successfully uploaded to: {}", response.result),
    ///   Err(err) => println!("Error uploading file: {}", err),
    /// };
    /// ```
    pub fn put_file<P: AsRef<Path>>(&self, file_path: P) -> Result<FileAdded, Error> {
        // FIXME: A whole lot of unwrap going on here...
        let path_ref = file_path.as_ref();
        let url_string = format!("{}/{}",
            self.to_url(),
            path_ref.file_name().unwrap().to_str().unwrap()
        );
        let url = Url::parse(&url_string).unwrap();

        let mut file = File::open(path_ref).unwrap();
        let req = self.client.put(url).body(&mut file);

        let mut res = try!(req.send());
        let mut res_json = String::new();
        try!(res.read_to_string(&mut res_json));

        match res.status.is_success() {
            true => json::decode(&res_json).map_err(|err| err.into()),
            false => Err(error::decode(&res_json)),
        }
    }

    pub fn child<T: HasDataPath>(&self, filename: &str) -> T {
        let new_uri = match self.to_data_uri() {
            ref uri if uri.ends_with("/") => format!("{}{}", uri, filename),
            uri => format!("{}/{}", uri, filename),
        };
        T::new(self.client.clone(), &new_uri)
    }
}

#[cfg(test)]
mod tests {
    use data::HasDataPath;
    use super::*;
    use Algorithmia;

    fn mock_client() -> Algorithmia { Algorithmia::client("") }

    #[test]
    fn test_to_url() {
        let dir = mock_client().dir("data://anowell/foo");
        assert_eq!(dir.to_url().serialize_path().unwrap(), "/v1/data/anowell/foo");
    }

    #[test]
    fn test_to_data_uri() {
        let dir = mock_client().dir("/anowell/foo");
        assert_eq!(dir.to_data_uri(), "data://anowell/foo".to_string());
    }

    #[test]
    fn test_parent() {
        let dir = mock_client().dir("data://anowell/foo");
        let expected = mock_client().dir("data://anowell");
        assert_eq!(dir.parent().unwrap().path, expected.path);
    }

    #[test]
    fn test_default_acl() {
        let acl: DataAcl = DataAcl::default();
        assert_eq!(acl.read, vec!["algo://.my/*".to_string()]);
    }

    #[test]
    fn test_private_acl() {
        let acl: DataAcl = ReadAcl::Private.into();
        assert!(acl.read.is_empty());
    }

    #[test]
    fn test_public_acl() {
        let acl: DataAcl = ReadAcl::Public.into();
        assert_eq!(acl.read, vec!["user://*".to_string()]);
    }

    #[test]
    fn test_myalgos_acl() {
        let acl: DataAcl = ReadAcl::MyAlgorithms.into();
        assert_eq!(acl.read, vec!["algo://.my/*".to_string()]);
    }

}<|endoftext|>


<commit_before>use crate::bosun::{Metadata, Rate, Sample, Tags};
use crate::collectors::*;
use crate::config::Config;

use regex::Regex;
use std::collections::HashMap;
use std::process::{Command, Output};
use std::io::Result as IoResult;

static METRIC_NAME_GC: &'static str = "jvm.gc.stats";

#[derive(Debug)]
#[derive(Clone)]
#[derive(RustcDecodable)]
#[allow(non_snake_case)]
pub struct JvmConfig {
    Command: String,
    Name: String,
}

pub struct Jvm {
    id: Id,
    jvms: Vec<JvmConfig>,
    metadata: HashMap<String, Metadata>,
}

pub fn create_instances(config: &Config) -> Vec<Box<dyn Collector + Send>> {
    if !config.Jvm.is_empty() {
        let id = "jvm".to_string();
        info!("Created instance of JVM collector: {}", id);

        let metadata = metadata();
        let collector = Jvm { id: id, jvms: config.Jvm.clone(), metadata: metadata };
        vec![Box::new(collector)]
    } else {
        Vec::new()
    }
}

impl Collector for Jvm {
    fn init(&mut self) -> Result<(), Box<Error>> {
        let result = Command::new("/usr/bin/jps").arg("-help").output();
        if let Err(err) = handle_command_output("jps", result) {
            return Err(Box::new(err))
        };
        let result = Command::new("/usr/bin/jstat").arg("-help").output();
        if let Err(err) = handle_command_output("jstat", result) {
            return Err(Box::new(err))
        };

        Ok(())
    }

    fn id(&self) -> &Id {
        &self.id
    }

    fn collect(&self) -> Result<Vec<Sample>, Error> {
        let jvm_processes = r#try!(get_jps());
        let results: Vec<Result<Vec<GcStat>, Error>> = jvm_processes.iter()
            .map(|jp| identify_jvms(&self.jvms, jp))
            .filter(|jvm| jvm.name.is_some())
            .map(|jvm| sample_gc_stats(&jvm))
            .collect();

        let (oks, fails): (Vec<_>, Vec<_>) = results.into_iter().partition(Result::is_ok);
        if !fails.is_empty() {
            for f in fails {
                if let Err(err) = f {
                    warn!("Failed to sample gc stats: {}", err);
                }
            }
        }

        let result = oks.into_iter()
            .flat_map(|ok| ok) // Res -> Vec
            .flat_map(|gcs| gcs.into_iter()) // Vec<Vec> -> Vec
            .filter_map(|gc| gcstat_to_sample(&self.metadata, gc)) // None, Some(GC), None -> Some(Sample)
            .collect();
        trace!("Collected these GC samples: '{:#?}'", result);

        Ok(result)
    }

    fn shutdown(&mut self) {}

    fn metadata(&self) -> Vec<Metadata> {
        let mut metadata = metadata();
        let result = metadata.drain().map(|(_, v)| v).collect();
        result
    }
}

fn metadata() -> HashMap<String, Metadata> {
    let mut metadata: HashMap<String, Metadata> = HashMap::new();
    metadata.insert("S0C".to_string(),
        Metadata::new(format!("{}.survivor_space_0_capacity", METRIC_NAME_GC),
                      Rate::Gauge,
                      "kB",
                      "S0C: Current survivor space 1 capacity"));
    metadata.insert("S1C".to_string(),
        Metadata::new(format!("{}.survivor_space_1_capacity", METRIC_NAME_GC),
                      Rate::Gauge,
                      "kB",
                      "S1C: Current survivor space 1 capacity"));
    metadata.insert("S0U".to_string(),
        Metadata::new(format!("{}.survivor_space_0_utilization", METRIC_NAME_GC),
                      Rate::Gauge,
                      "kB",
                      "S0U: Survivor space 1 utilization"));
    metadata.insert("S1U".to_string(),
        Metadata::new(format!("{}.survivor_space_1_utilization", METRIC_NAME_GC),
                      Rate::Gauge,
                      "kB",
                      "S1U: Survivor space 1 utilization"));
    metadata.insert("EC".to_string(),
        Metadata::new(format!("{}.current_eden_space_capacity", METRIC_NAME_GC),
                      Rate::Gauge,
                      "kB",
                      "EC: Current eden space capacity"));
    metadata.insert("EU".to_string(),
        Metadata::new(format!("{}.eden_space_utilization", METRIC_NAME_GC),
                      Rate::Gauge,
                      "kB",
                      "EU: Eden space utilization"));
    metadata.insert("OC".to_string(),
        Metadata::new(format!("{}.current_old_space_capacity", METRIC_NAME_GC),
                      Rate::Gauge,
                      "kB",
                      "OC: Current old capacity"));
    metadata.insert("OU".to_string(),
        Metadata::new(format!("{}.old_space_utilization", METRIC_NAME_GC),
                      Rate::Gauge,
                      "kB",
                      "OU: Old space utilization"));
    metadata.insert("PC".to_string(),
        Metadata::new(format!("{}.current_permanent_space_capacity", METRIC_NAME_GC),
                      Rate::Gauge,
                      "kB",
                      "PC: Current permanent space capacity"));
    metadata.insert("PU".to_string(),
        Metadata::new(format!("{}.permanent_space_utilization", METRIC_NAME_GC),
                      Rate::Gauge,
                      "kB",
                      "PU: permanent space utilization"));
    metadata.insert("MC".to_string(),
        Metadata::new(format!("{}.metaspace_capacity", METRIC_NAME_GC),
                      Rate::Gauge,
                      "kB",
                      "MC: metaspace capacity"));
    metadata.insert("MU".to_string(),
        Metadata::new(format!("{}.metaspace_utilization", METRIC_NAME_GC),
                      Rate::Gauge,
                      "kB",
                      "MU: metaspace utilization"));
    metadata.insert("CCSC".to_string(),
        Metadata::new(format!("{}.compressed_class_space_capacity", METRIC_NAME_GC),
                      Rate::Gauge,
                      "kB",
                      "CCSC: Compressed class space capacity"));
    metadata.insert("CCSU".to_string(),
        Metadata::new(format!("{}.compressed_class_space_used", METRIC_NAME_GC),
                      Rate::Gauge,
                      "kB",
                      "CCSU: Compressed class space used"));
    metadata.insert("YGC".to_string(),
        Metadata::new(format!("{}.young_generation_gc_events", METRIC_NAME_GC),
                      Rate::Counter,
                      "Event",
                      "YGC: Number of young generation garbage collection events"));
    metadata.insert("YGCT".to_string(),
        Metadata::new(format!("{}.young_generation_gc_time", METRIC_NAME_GC),
                      Rate::Counter,
                      "s",
                      "YGCT: Young generation garbage collection time"));
    metadata.insert("FGC".to_string(),
        Metadata::new(format!("{}.full_gc_events", METRIC_NAME_GC),
                      Rate::Counter,
                      "Event",
                      "FGC: Number of full GC events."));
    metadata.insert("FGCT".to_string(),
        Metadata::new(format!("{}.full_gc_time", METRIC_NAME_GC),
                      Rate::Counter,
                      "s",
                      "FGCT: Full garbage collection time"));
    metadata.insert("GCT".to_string(),
        Metadata::new(format!("{}.total_gc_time", METRIC_NAME_GC),
                      Rate::Counter,
                      "s",
                      "GCT: Total garbage collection time"));

    metadata
}

fn handle_command_output(command: &str, result: IoResult<Output>) -> Result<Output, Error> {
    match result {
        Ok(output) => {
            if output.status.success() {
                debug!("Successfully found and run {}.", command);
                Ok(output)
            } else {
                let exit_code = output.status.code().map_or("<received signal>".to_string(), |i| format!("{}", i));
                let stdout = String::from_utf8_lossy(&output.stdout);
                let stderr = String::from_utf8_lossy(&output.stderr);
                let msg = format!("Running {} returned exit code {}: out '{}', err '{}'.", command, exit_code, stdout, stderr);
                debug!("{}", msg);
                Err(Error::InitError(msg))
            }
        },
        Err(err) => {
            let msg = format!("Failed to run {}, because '{}'.", command, err.description());
            debug!("{}", msg);
            Err(Error::InitError(msg))
        },
    }
}

#[derive(Debug)]
struct JvmProcess {
    pid: u16,
    class: String,
    cmdline: String,
}

fn get_jps() -> Result<Vec<JvmProcess>, Error>{
    let result = execute_jps();
    let output = r#try!(handle_command_output("jps", result));
    let stdout = String::from_utf8_lossy(&output.stdout);
    let lines: Vec<&str> = stdout.lines().collect();
    if lines.is_empty() {
        let msg = format!("Failed to parse jps output");
        trace!("Failed to parse jps output for lines: '{}'", stdout);
        return Err(Error::CollectionError(msg));
    }

    let mut jps = Vec::new();
    for line in lines {
        let cols: Vec<&str> = line.split_whitespace().collect();
        if cols.len() < 2 {
            let msg = format!("Failed to parse jps output; expected 'PID CLASS CMDLINE'");
            trace!("Failed to parse jps output for lines: '{}'", stdout);
            return Err(Error::CollectionError(msg));
        }
        let pid: u16 = r#try!(cols[0].parse::<u16>());
        let jp = JvmProcess { pid: pid, class: cols[1].to_string(), cmdline: cols[2..].join(" ").to_string() };
        trace!("Found JVM Process '{:?}'", jp);
        jps.push(jp)
    }
    Ok(jps)
}

fn execute_jps() -> IoResult<Output> {
    // TODO: use timeout for execution
    Command::new("/usr/bin/jps").arg("-vl").output()
}

#[derive(Debug)]
struct IdentifiedJvm {
    pid: u16,
    name: Option<String>,
}

fn identify_jvms(jvm_configs: &Vec<JvmConfig>, jp: &JvmProcess) -> IdentifiedJvm {
    let mut name = None;
    for jvm_config in jvm_configs {
        if let Ok(re) = Regex::new(&jvm_config.Command) {
            if re.is_match(&jp.class) || re.is_match(&jp.cmdline) {
                name = Some(jvm_config.Name.clone());
                break;
            } else {
                debug!("Regular expression '{:?}' did not match JVM process '{:?}'", jvm_config, jp);
            }
        }
    }
    IdentifiedJvm {pid: jp.pid, name: name }
}

#[derive(Debug)]
struct GcStat {
    jvm_name: String,
    name: String,
    value: f64,
}

fn sample_gc_stats(jvm: &IdentifiedJvm) -> Result<Vec<GcStat>, Error> {
    let result = execute_jstat(jvm);
    let output = r#try!(handle_command_output("jstat", result));
    let stdout = String::from_utf8_lossy(&output.stdout);
    let lines: Vec<&str> = stdout.lines().collect();
    if lines.len() != 2  {
        let msg = format!("Failed to parse jstat output for pid {}", jvm.pid);
        trace!("Failed to parse jstat out for lines: '{}'", stdout);
        return Err(Error::CollectionError(msg));
    }
    let names: Vec<&str> = lines[0].split_whitespace().collect();
    let values: Vec<&str> = lines[1].split_whitespace().collect();

    let mut gcstats = Vec::new();
    for i in 0..values.len() {
        let name = names[i];
        let value = r#try!(values[i].parse::<f64>());
        // Unwrap is safe, but only due to the filter in the main algorithm
        let gcstat = GcStat{ jvm_name: jvm.name.as_ref().unwrap().clone(), name: name.to_string(), value: value };
        trace!("Successfully run gcstat for JVM Process '{:?}': '{:?}'", jvm, gcstat);
        gcstats.push(gcstat);
    }

    Ok(gcstats)
}

fn execute_jstat(jvm: &IdentifiedJvm) -> IoResult<Output> {
    // TODO: use timeout for execution
    let pid = format!("{}", jvm.pid);
    Command::new("/usr/bin/jstat").arg("-gc").arg(pid).output()
}

fn gcstat_to_sample(metadata: &HashMap<String, Metadata>, gcstat: GcStat) -> Option<Sample> {
    let mut tags = Tags::new();
    tags.insert("jvm_name".to_string(), gcstat.jvm_name );
    let metric_name = metadata.get(&gcstat.name).map(|m| m.metric.to_string());
    if let Some(name) = metric_name {
        let sample = Sample::new_with_tags(name, gcstat.value, tags);
        Some(sample)
    } else {
        None
    }
}

<commit_msg>[#181045591] Use larger type to capture jvm process ids<commit_after>use crate::bosun::{Metadata, Rate, Sample, Tags};
use crate::collectors::*;
use crate::config::Config;

use regex::Regex;
use std::collections::HashMap;
use std::process::{Command, Output};
use std::io::Result as IoResult;

static METRIC_NAME_GC: &'static str = "jvm.gc.stats";

#[derive(Debug)]
#[derive(Clone)]
#[derive(RustcDecodable)]
#[allow(non_snake_case)]
pub struct JvmConfig {
    Command: String,
    Name: String,
}

pub struct Jvm {
    id: Id,
    jvms: Vec<JvmConfig>,
    metadata: HashMap<String, Metadata>,
}

pub fn create_instances(config: &Config) -> Vec<Box<dyn Collector + Send>> {
    if !config.Jvm.is_empty() {
        let id = "jvm".to_string();
        info!("Created instance of JVM collector: {}", id);

        let metadata = metadata();
        let collector = Jvm { id: id, jvms: config.Jvm.clone(), metadata: metadata };
        vec![Box::new(collector)]
    } else {
        Vec::new()
    }
}

impl Collector for Jvm {
    fn init(&mut self) -> Result<(), Box<Error>> {
        let result = Command::new("/usr/bin/jps").arg("-help").output();
        if let Err(err) = handle_command_output("jps", result) {
            return Err(Box::new(err))
        };
        let result = Command::new("/usr/bin/jstat").arg("-help").output();
        if let Err(err) = handle_command_output("jstat", result) {
            return Err(Box::new(err))
        };

        Ok(())
    }

    fn id(&self) -> &Id {
        &self.id
    }

    fn collect(&self) -> Result<Vec<Sample>, Error> {
        let jvm_processes = r#try!(get_jps());
        let results: Vec<Result<Vec<GcStat>, Error>> = jvm_processes.iter()
            .map(|jp| identify_jvms(&self.jvms, jp))
            .filter(|jvm| jvm.name.is_some())
            .map(|jvm| sample_gc_stats(&jvm))
            .collect();

        let (oks, fails): (Vec<_>, Vec<_>) = results.into_iter().partition(Result::is_ok);
        if !fails.is_empty() {
            for f in fails {
                if let Err(err) = f {
                    warn!("Failed to sample gc stats: {}", err);
                }
            }
        }

        let result = oks.into_iter()
            .flat_map(|ok| ok) // Res -> Vec
            .flat_map(|gcs| gcs.into_iter()) // Vec<Vec> -> Vec
            .filter_map(|gc| gcstat_to_sample(&self.metadata, gc)) // None, Some(GC), None -> Some(Sample)
            .collect();
        trace!("Collected these GC samples: '{:#?}'", result);

        Ok(result)
    }

    fn shutdown(&mut self) {}

    fn metadata(&self) -> Vec<Metadata> {
        let mut metadata = metadata();
        let result = metadata.drain().map(|(_, v)| v).collect();
        result
    }
}

fn metadata() -> HashMap<String, Metadata> {
    let mut metadata: HashMap<String, Metadata> = HashMap::new();
    metadata.insert("S0C".to_string(),
        Metadata::new(format!("{}.survivor_space_0_capacity", METRIC_NAME_GC),
                      Rate::Gauge,
                      "kB",
                      "S0C: Current survivor space 1 capacity"));
    metadata.insert("S1C".to_string(),
        Metadata::new(format!("{}.survivor_space_1_capacity", METRIC_NAME_GC),
                      Rate::Gauge,
                      "kB",
                      "S1C: Current survivor space 1 capacity"));
    metadata.insert("S0U".to_string(),
        Metadata::new(format!("{}.survivor_space_0_utilization", METRIC_NAME_GC),
                      Rate::Gauge,
                      "kB",
                      "S0U: Survivor space 1 utilization"));
    metadata.insert("S1U".to_string(),
        Metadata::new(format!("{}.survivor_space_1_utilization", METRIC_NAME_GC),
                      Rate::Gauge,
                      "kB",
                      "S1U: Survivor space 1 utilization"));
    metadata.insert("EC".to_string(),
        Metadata::new(format!("{}.current_eden_space_capacity", METRIC_NAME_GC),
                      Rate::Gauge,
                      "kB",
                      "EC: Current eden space capacity"));
    metadata.insert("EU".to_string(),
        Metadata::new(format!("{}.eden_space_utilization", METRIC_NAME_GC),
                      Rate::Gauge,
                      "kB",
                      "EU: Eden space utilization"));
    metadata.insert("OC".to_string(),
        Metadata::new(format!("{}.current_old_space_capacity", METRIC_NAME_GC),
                      Rate::Gauge,
                      "kB",
                      "OC: Current old capacity"));
    metadata.insert("OU".to_string(),
        Metadata::new(format!("{}.old_space_utilization", METRIC_NAME_GC),
                      Rate::Gauge,
                      "kB",
                      "OU: Old space utilization"));
    metadata.insert("PC".to_string(),
        Metadata::new(format!("{}.current_permanent_space_capacity", METRIC_NAME_GC),
                      Rate::Gauge,
                      "kB",
                      "PC: Current permanent space capacity"));
    metadata.insert("PU".to_string(),
        Metadata::new(format!("{}.permanent_space_utilization", METRIC_NAME_GC),
                      Rate::Gauge,
                      "kB",
                      "PU: permanent space utilization"));
    metadata.insert("MC".to_string(),
        Metadata::new(format!("{}.metaspace_capacity", METRIC_NAME_GC),
                      Rate::Gauge,
                      "kB",
                      "MC: metaspace capacity"));
    metadata.insert("MU".to_string(),
        Metadata::new(format!("{}.metaspace_utilization", METRIC_NAME_GC),
                      Rate::Gauge,
                      "kB",
                      "MU: metaspace utilization"));
    metadata.insert("CCSC".to_string(),
        Metadata::new(format!("{}.compressed_class_space_capacity", METRIC_NAME_GC),
                      Rate::Gauge,
                      "kB",
                      "CCSC: Compressed class space capacity"));
    metadata.insert("CCSU".to_string(),
        Metadata::new(format!("{}.compressed_class_space_used", METRIC_NAME_GC),
                      Rate::Gauge,
                      "kB",
                      "CCSU: Compressed class space used"));
    metadata.insert("YGC".to_string(),
        Metadata::new(format!("{}.young_generation_gc_events", METRIC_NAME_GC),
                      Rate::Counter,
                      "Event",
                      "YGC: Number of young generation garbage collection events"));
    metadata.insert("YGCT".to_string(),
        Metadata::new(format!("{}.young_generation_gc_time", METRIC_NAME_GC),
                      Rate::Counter,
                      "s",
                      "YGCT: Young generation garbage collection time"));
    metadata.insert("FGC".to_string(),
        Metadata::new(format!("{}.full_gc_events", METRIC_NAME_GC),
                      Rate::Counter,
                      "Event",
                      "FGC: Number of full GC events."));
    metadata.insert("FGCT".to_string(),
        Metadata::new(format!("{}.full_gc_time", METRIC_NAME_GC),
                      Rate::Counter,
                      "s",
                      "FGCT: Full garbage collection time"));
    metadata.insert("GCT".to_string(),
        Metadata::new(format!("{}.total_gc_time", METRIC_NAME_GC),
                      Rate::Counter,
                      "s",
                      "GCT: Total garbage collection time"));

    metadata
}

fn handle_command_output(command: &str, result: IoResult<Output>) -> Result<Output, Error> {
    match result {
        Ok(output) => {
            if output.status.success() {
                debug!("Successfully found and run {}.", command);
                Ok(output)
            } else {
                let exit_code = output.status.code().map_or("<received signal>".to_string(), |i| format!("{}", i));
                let stdout = String::from_utf8_lossy(&output.stdout);
                let stderr = String::from_utf8_lossy(&output.stderr);
                let msg = format!("Running {} returned exit code {}: out '{}', err '{}'.", command, exit_code, stdout, stderr);
                debug!("{}", msg);
                Err(Error::InitError(msg))
            }
        },
        Err(err) => {
            let msg = format!("Failed to run {}, because '{}'.", command, err.description());
            debug!("{}", msg);
            Err(Error::InitError(msg))
        },
    }
}

#[derive(Debug)]
struct JvmProcess {
    pid: u32,
    class: String,
    cmdline: String,
}

fn get_jps() -> Result<Vec<JvmProcess>, Error>{
    let result = execute_jps();
    let output = r#try!(handle_command_output("jps", result));
    let stdout = String::from_utf8_lossy(&output.stdout);
    let lines: Vec<&str> = stdout.lines().collect();
    if lines.is_empty() {
        let msg = format!("Failed to parse jps output");
        trace!("Failed to parse jps output for lines: '{}'", stdout);
        return Err(Error::CollectionError(msg));
    }

    let mut jps = Vec::new();
    for line in lines {
        let cols: Vec<&str> = line.split_whitespace().collect();
        if cols.len() < 2 {
            let msg = format!("Failed to parse jps output; expected 'PID CLASS CMDLINE'");
            trace!("Failed to parse jps output for lines: '{}'", stdout);
            return Err(Error::CollectionError(msg));
        }
        let pid: u32 = r#try!(cols[0].parse());
        let jp = JvmProcess { pid: pid, class: cols[1].to_string(), cmdline: cols[2..].join(" ").to_string() };
        trace!("Found JVM Process '{:?}'", jp);
        jps.push(jp)
    }
    Ok(jps)
}

fn execute_jps() -> IoResult<Output> {
    // TODO: use timeout for execution
    Command::new("/usr/bin/jps").arg("-vl").output()
}

#[derive(Debug)]
struct IdentifiedJvm {
    pid: u32,
    name: Option<String>,
}

fn identify_jvms(jvm_configs: &Vec<JvmConfig>, jp: &JvmProcess) -> IdentifiedJvm {
    let mut name = None;
    for jvm_config in jvm_configs {
        if let Ok(re) = Regex::new(&jvm_config.Command) {
            if re.is_match(&jp.class) || re.is_match(&jp.cmdline) {
                name = Some(jvm_config.Name.clone());
                break;
            } else {
                debug!("Regular expression '{:?}' did not match JVM process '{:?}'", jvm_config, jp);
            }
        }
    }
    IdentifiedJvm {pid: jp.pid, name: name }
}

#[derive(Debug)]
struct GcStat {
    jvm_name: String,
    name: String,
    value: f64,
}

fn sample_gc_stats(jvm: &IdentifiedJvm) -> Result<Vec<GcStat>, Error> {
    let result = execute_jstat(jvm);
    let output = r#try!(handle_command_output("jstat", result));
    let stdout = String::from_utf8_lossy(&output.stdout);
    let lines: Vec<&str> = stdout.lines().collect();
    if lines.len() != 2  {
        let msg = format!("Failed to parse jstat output for pid {}", jvm.pid);
        trace!("Failed to parse jstat out for lines: '{}'", stdout);
        return Err(Error::CollectionError(msg));
    }
    let names: Vec<&str> = lines[0].split_whitespace().collect();
    let values: Vec<&str> = lines[1].split_whitespace().collect();

    let mut gcstats = Vec::new();
    for i in 0..values.len() {
        let name = names[i];
        let value = r#try!(values[i].parse::<f64>());
        // Unwrap is safe, but only due to the filter in the main algorithm
        let gcstat = GcStat{ jvm_name: jvm.name.as_ref().unwrap().clone(), name: name.to_string(), value: value };
        trace!("Successfully run gcstat for JVM Process '{:?}': '{:?}'", jvm, gcstat);
        gcstats.push(gcstat);
    }

    Ok(gcstats)
}

fn execute_jstat(jvm: &IdentifiedJvm) -> IoResult<Output> {
    // TODO: use timeout for execution
    let pid = format!("{}", jvm.pid);
    Command::new("/usr/bin/jstat").arg("-gc").arg(pid).output()
}

fn gcstat_to_sample(metadata: &HashMap<String, Metadata>, gcstat: GcStat) -> Option<Sample> {
    let mut tags = Tags::new();
    tags.insert("jvm_name".to_string(), gcstat.jvm_name );
    let metric_name = metadata.get(&gcstat.name).map(|m| m.metric.to_string());
    if let Some(name) = metric_name {
        let sample = Sample::new_with_tags(name, gcstat.value, tags);
        Some(sample)
    } else {
        None
    }
}

<|endoftext|>


<commit_before>use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use serde_json;
use std::ffi::OsStr;
use std::io::{BufReader, ErrorKind as IoErrorKind, Read, Result as IoResult};
use std::process::{Child, Command, Stdio};
use super::{AudioSource, AudioType, DcaError, DcaMetadata, VoiceError};
use internal::prelude::*;

use std::fs::File;
use std::result::Result as StdResult;

struct ChildContainer(Child);

impl Read for ChildContainer {
    fn read(&mut self, buffer: &mut [u8]) -> IoResult<usize> {
        self.0.stdout.as_mut().unwrap().read(buffer)
    }
}

struct InputSource<R: Read + Send + 'static> {
    stereo: bool,
    reader: R,
    kind: AudioType,
}

impl<R: Read + Send> AudioSource for InputSource<R> {
    fn is_stereo(&mut self) -> bool { self.stereo }

    fn get_type(&self) -> AudioType { self.kind.clone() }

    fn read_pcm_frame(&mut self, buffer: &mut [i16]) -> Option<usize> {
        for (i, v) in buffer.iter_mut().enumerate() {
            *v = match self.reader.read_i16::<LittleEndian>() {
                Ok(v) => v,
                Err(ref e) => {
                    return if e.kind() == IoErrorKind::UnexpectedEof {
                        Some(i)
                    } else {
                        None
                    }
                },
            }
        }

        Some(buffer.len())
    }

    fn read_opus_frame(&mut self) -> Option<Vec<u8>> {
        match self.reader.read_i16::<LittleEndian>() {
            Ok(size) => {
                let mut frame = Vec::with_capacity(size as usize + 2);
                frame.write_i16::<LittleEndian>(size);
                match self.reader.read_exact(&mut frame[2..]) {
                    Ok(_) => Some(frame),
                    _ => None,
                }
            },
            Err(ref e) => {
                return if e.kind() == IoErrorKind::UnexpectedEof {
                    Some(Vec::new())
                } else {
                    None
                }
            },
        }
    }
}

/// Opens an audio file through `ffmpeg` and creates an audio source.
pub fn ffmpeg<P: AsRef<OsStr>>(path: P) -> Result<Box<AudioSource>> {
    let path = path.as_ref();

    // Will fail if the path is not to a file on the fs. Likely a YouTube URI.
    let is_stereo = is_stereo(path).unwrap_or(false);
    let stereo_val = if is_stereo { "2" } else { "1" };

    let args = [
        "-f",
        "s16le",
        "-ac",
        stereo_val,
        "-ar",
        "48000",
        "-acodec",
        "pcm_s16le",
        "-",
    ];

    let command = Command::new("ffmpeg")
        .arg("-i")
        .arg(path)
        .args(&args)
        .stderr(Stdio::null())
        .stdin(Stdio::null())
        .stdout(Stdio::piped())
        .spawn()?;

    Ok(pcm(is_stereo, ChildContainer(command)))
}

/// Creates a streamed audio source from a DCA file.
/// Currently only accepts the DCA1 format.
pub fn dca<P: AsRef<OsStr>>(path: P) -> StdResult<Box<AudioSource>, DcaError> {
    let file = File::open(path.as_ref()).map_err(DcaError::IoError)?;

    let mut reader = BufReader::new(file);

    let mut header = [0u8; 4];

    // Read in the magic number to verify it's a DCA file.
    reader.read_exact(&mut header).map_err(DcaError::IoError)?;

    if header != b"DCA1"[..] {
        return Err(DcaError::InvalidHeader);
    }

    reader.read_exact(&mut header).map_err(DcaError::IoError)?;

    let size = (&header[..]).read_i32::<LittleEndian>().unwrap();

    // Sanity check
    if size < 2 {
        return Err(DcaError::InvalidSize(size));
    }

    info!("Size: {}", size);

    let mut raw_json = Vec::with_capacity(size as usize);
    reader.read_exact(raw_json.as_mut_slice()).map_err(DcaError::IoError)?;

    info!("Raw JSON: {:?}", raw_json);

    let metadata = serde_json::from_slice::<DcaMetadata>(raw_json.as_slice()).map_err(DcaError::InvalidMetadata)?;

    Ok(opus(metadata.is_stereo(), reader))
}

/// Creates an Opus audio source.
pub fn opus<R: Read + Send + 'static>(is_stereo: bool, reader: R) -> Box<AudioSource> {
    Box::new(InputSource {
        stereo: is_stereo,
        reader: reader,
        kind: AudioType::Opus
    })
}

/// Creates a PCM audio source.
pub fn pcm<R: Read + Send + 'static>(is_stereo: bool, reader: R) -> Box<AudioSource> {
    Box::new(InputSource {
        stereo: is_stereo,
        reader: reader,
        kind: AudioType::Pcm
    })
}

/// Creates a streamed audio source with `youtube-dl` and `ffmpeg`.
pub fn ytdl(uri: &str) -> Result<Box<AudioSource>> {
    let args = [
        "-f",
        "webm[abr>0]/bestaudio/best",
        "--no-playlist",
        "--print-json",
        "--skip-download",
        uri,
    ];

    let out = Command::new("youtube-dl")
        .args(&args)
        .stdin(Stdio::null())
        .output()?;

    if !out.status.success() {
        return Err(Error::Voice(VoiceError::YouTubeDLRun(out)));
    }

    let value = serde_json::from_reader(&out.stdout[..])?;
    let mut obj = match value {
        Value::Object(obj) => obj,
        other => return Err(Error::Voice(VoiceError::YouTubeDLProcessing(other))),
    };

    let uri = match obj.remove("url") {
        Some(v) => {
            match v {
                Value::String(uri) => uri,
                other => return Err(Error::Voice(VoiceError::YouTubeDLUrl(other))),
            }
        },
        None => return Err(Error::Voice(VoiceError::YouTubeDLUrl(Value::Object(obj)))),
    };

    ffmpeg(&uri)
}

fn is_stereo(path: &OsStr) -> Result<bool> {
    let args = ["-v", "quiet", "-of", "json", "-show-streams", "-i"];

    let out = Command::new("ffprobe")
        .args(&args)
        .arg(path)
        .stdin(Stdio::null())
        .output()?;

    let value: Value = serde_json::from_reader(&out.stdout[..])?;

    let streams = value
        .as_object()
        .and_then(|m| m.get("streams"))
        .and_then(|v| v.as_array())
        .ok_or(Error::Voice(VoiceError::Streams))?;

    let check = streams.iter().any(|stream| {
        let channels = stream.as_object().and_then(|m| {
            m.get("channels").and_then(|v| v.as_i64())
        });

        channels == Some(2)
    });

    Ok(check)
}
<commit_msg>Fix metadata reading being blank<commit_after>use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use serde_json;
use std::ffi::OsStr;
use std::io::{BufReader, ErrorKind as IoErrorKind, Read, Result as IoResult};
use std::process::{Child, Command, Stdio};
use super::{AudioSource, AudioType, DcaError, DcaMetadata, VoiceError};
use internal::prelude::*;

use std::fs::File;
use std::result::Result as StdResult;

struct ChildContainer(Child);

impl Read for ChildContainer {
    fn read(&mut self, buffer: &mut [u8]) -> IoResult<usize> {
        self.0.stdout.as_mut().unwrap().read(buffer)
    }
}

struct InputSource<R: Read + Send + 'static> {
    stereo: bool,
    reader: R,
    kind: AudioType,
}

impl<R: Read + Send> AudioSource for InputSource<R> {
    fn is_stereo(&mut self) -> bool { self.stereo }

    fn get_type(&self) -> AudioType { self.kind.clone() }

    fn read_pcm_frame(&mut self, buffer: &mut [i16]) -> Option<usize> {
        for (i, v) in buffer.iter_mut().enumerate() {
            *v = match self.reader.read_i16::<LittleEndian>() {
                Ok(v) => v,
                Err(ref e) => {
                    return if e.kind() == IoErrorKind::UnexpectedEof {
                        Some(i)
                    } else {
                        None
                    }
                },
            }
        }

        Some(buffer.len())
    }

    fn read_opus_frame(&mut self) -> Option<Vec<u8>> {
        match self.reader.read_i16::<LittleEndian>() {
            Ok(size) => {
                let mut frame = Vec::with_capacity(size as usize + 2);
                frame.write_i16::<LittleEndian>(size);
                match self.reader.read_exact(&mut frame[2..]) {
                    Ok(_) => Some(frame),
                    _ => None,
                }
            },
            Err(ref e) => {
                return if e.kind() == IoErrorKind::UnexpectedEof {
                    Some(Vec::new())
                } else {
                    None
                }
            },
        }
    }
}

/// Opens an audio file through `ffmpeg` and creates an audio source.
pub fn ffmpeg<P: AsRef<OsStr>>(path: P) -> Result<Box<AudioSource>> {
    let path = path.as_ref();

    // Will fail if the path is not to a file on the fs. Likely a YouTube URI.
    let is_stereo = is_stereo(path).unwrap_or(false);
    let stereo_val = if is_stereo { "2" } else { "1" };

    let args = [
        "-f",
        "s16le",
        "-ac",
        stereo_val,
        "-ar",
        "48000",
        "-acodec",
        "pcm_s16le",
        "-",
    ];

    let command = Command::new("ffmpeg")
        .arg("-i")
        .arg(path)
        .args(&args)
        .stderr(Stdio::null())
        .stdin(Stdio::null())
        .stdout(Stdio::piped())
        .spawn()?;

    Ok(pcm(is_stereo, ChildContainer(command)))
}

/// Creates a streamed audio source from a DCA file.
/// Currently only accepts the DCA1 format.
pub fn dca<P: AsRef<OsStr>>(path: P) -> StdResult<Box<AudioSource>, DcaError> {
    let file = File::open(path.as_ref()).map_err(DcaError::IoError)?;

    let mut reader = BufReader::new(file);

    let mut header = [0u8; 4];

    // Read in the magic number to verify it's a DCA file.
    reader.read_exact(&mut header).map_err(DcaError::IoError)?;

    if header != b"DCA1"[..] {
        return Err(DcaError::InvalidHeader);
    }

    reader.read_exact(&mut header).map_err(DcaError::IoError)?;

    let size = (&header[..]).read_i32::<LittleEndian>().unwrap();

    // Sanity check
    if size < 2 {
        return Err(DcaError::InvalidSize(size));
    }

    info!("Size: {}", size);

    let mut raw_json = Vec::with_capacity(size as usize);

    {
        let json_reader = reader.by_ref();
        json_reader.take(size as u64).read_to_end(&mut raw_json).map_err(DcaError::IoError)?;
    }

    info!("Raw JSON: {:?}", raw_json);

    let metadata = serde_json::from_slice::<DcaMetadata>(raw_json.as_slice()).map_err(DcaError::InvalidMetadata)?;

    Ok(opus(metadata.is_stereo(), reader))
}

/// Creates an Opus audio source.
pub fn opus<R: Read + Send + 'static>(is_stereo: bool, reader: R) -> Box<AudioSource> {
    Box::new(InputSource {
        stereo: is_stereo,
        reader: reader,
        kind: AudioType::Opus
    })
}

/// Creates a PCM audio source.
pub fn pcm<R: Read + Send + 'static>(is_stereo: bool, reader: R) -> Box<AudioSource> {
    Box::new(InputSource {
        stereo: is_stereo,
        reader: reader,
        kind: AudioType::Pcm
    })
}

/// Creates a streamed audio source with `youtube-dl` and `ffmpeg`.
pub fn ytdl(uri: &str) -> Result<Box<AudioSource>> {
    let args = [
        "-f",
        "webm[abr>0]/bestaudio/best",
        "--no-playlist",
        "--print-json",
        "--skip-download",
        uri,
    ];

    let out = Command::new("youtube-dl")
        .args(&args)
        .stdin(Stdio::null())
        .output()?;

    if !out.status.success() {
        return Err(Error::Voice(VoiceError::YouTubeDLRun(out)));
    }

    let value = serde_json::from_reader(&out.stdout[..])?;
    let mut obj = match value {
        Value::Object(obj) => obj,
        other => return Err(Error::Voice(VoiceError::YouTubeDLProcessing(other))),
    };

    let uri = match obj.remove("url") {
        Some(v) => {
            match v {
                Value::String(uri) => uri,
                other => return Err(Error::Voice(VoiceError::YouTubeDLUrl(other))),
            }
        },
        None => return Err(Error::Voice(VoiceError::YouTubeDLUrl(Value::Object(obj)))),
    };

    ffmpeg(&uri)
}

fn is_stereo(path: &OsStr) -> Result<bool> {
    let args = ["-v", "quiet", "-of", "json", "-show-streams", "-i"];

    let out = Command::new("ffprobe")
        .args(&args)
        .arg(path)
        .stdin(Stdio::null())
        .output()?;

    let value: Value = serde_json::from_reader(&out.stdout[..])?;

    let streams = value
        .as_object()
        .and_then(|m| m.get("streams"))
        .and_then(|v| v.as_array())
        .ok_or(Error::Voice(VoiceError::Streams))?;

    let check = streams.iter().any(|stream| {
        let channels = stream.as_object().and_then(|m| {
            m.get("channels").and_then(|v| v.as_i64())
        });

        channels == Some(2)
    });

    Ok(check)
}
<|endoftext|>


<commit_before>// edition:2018
#![warn(clippy::wrong_self_convention)]
#![warn(clippy::wrong_pub_self_convention)]
#![allow(dead_code)]

fn main() {}

mod issue6983 {
    pub struct Thing;
    pub trait Trait {
        fn to_thing(&self) -> Thing;
    }

    impl Trait for u8 {
        // don't trigger, e.g. `ToString` from `std` requires `&self`
        fn to_thing(&self) -> Thing {
            Thing
        }
    }

    trait ToU64 {
        fn to_u64(self) -> u64;
    }

    struct FooNoCopy;
    // don't trigger
    impl ToU64 for FooNoCopy {
        fn to_u64(self) -> u64 {
            2
        }
    }
}

mod issue7032 {
    trait Foo {
        fn from_usize(x: usize) -> Self;
    }
    // don't trigger
    impl Foo for usize {
        fn from_usize(x: usize) -> Self {
            x
        }
    }
}

mod issue7179 {
    pub struct S(i32);

    impl S {
        // don't trigger (`s` is not `self`)
        pub fn from_be(s: Self) -> Self {
            S(i32::from_be(s.0))
        }

        // lint
        pub fn from_be_self(self) -> Self {
            S(i32::from_be(self.0))
        }
    }

    trait T {
        // don't trigger (`s` is not `self`)
        fn from_be(s: Self) -> Self;
        // lint
        fn from_be_self(self) -> Self;
    }
}
<commit_msg>Add sized trait for `wrong_self_convention` lint test<commit_after>// edition:2018
#![warn(clippy::wrong_self_convention)]
#![warn(clippy::wrong_pub_self_convention)]
#![allow(dead_code)]

fn main() {}

mod issue6983 {
    pub struct Thing;
    pub trait Trait {
        fn to_thing(&self) -> Thing;
    }

    impl Trait for u8 {
        // don't trigger, e.g. `ToString` from `std` requires `&self`
        fn to_thing(&self) -> Thing {
            Thing
        }
    }

    trait ToU64 {
        fn to_u64(self) -> u64;
    }

    struct FooNoCopy;
    // don't trigger
    impl ToU64 for FooNoCopy {
        fn to_u64(self) -> u64 {
            2
        }
    }
}

mod issue7032 {
    trait Foo {
        fn from_usize(x: usize) -> Self;
    }
    // don't trigger
    impl Foo for usize {
        fn from_usize(x: usize) -> Self {
            x
        }
    }
}

mod issue7179 {
    pub struct S(i32);

    impl S {
        // don't trigger (`s` is not `self`)
        pub fn from_be(s: Self) -> Self {
            S(i32::from_be(s.0))
        }

        // lint
        pub fn from_be_self(self) -> Self {
            S(i32::from_be(self.0))
        }
    }

    trait T {
        // don't trigger (`s` is not `self`)
        fn from_be(s: Self) -> Self;
        // lint
        fn from_be_self(self) -> Self;
    }

    trait Foo: Sized {
        fn as_byte_slice(slice: &[Self]) -> &[u8];
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use parsing::parse;
use style::parser::Parse;
use style::values::specified::length::Length;

#[test]
fn test_calc() {
    assert!(parse(Length::parse, "calc(1px+ 2px)").is_err());
}
<commit_msg>style: Add a test for other valid uses of calc()<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use parsing::parse;
use style::parser::Parse;
use style::values::specified::length::Length;

#[test]
fn test_calc() {
    assert!(parse(Length::parse, "calc(1px+ 2px)").is_err());
    assert!(parse(Length::parse, "calc( 1px + 2px )").is_ok());
    assert!(parse(Length::parse, "calc(1px + 2px )").is_ok());
    assert!(parse(Length::parse, "calc( 1px + 2px)").is_ok());
}
<|endoftext|>


<commit_before>extern crate game_state;

use game_state::state::State;
use game_state::Renderer;

use std::sync::Arc;

#[no_mangle]
pub extern "C" fn mod_input_load( state: &mut State ) {
    state.input_state.clear();
}

#[no_mangle]
pub extern "C" fn mod_input_tick( state: &mut State ) {
    // Renderers own the input event loop associated with their
    // internals: i.e. the window manager window
    // - get input events and convert them to our internal format
    // and push them into the input events queue

    // we want to clear that queue each tick, regardless of if we dealt with the events
    state.input_state.pending_input_events.clear();

    // Now we want to
    for i in 0 .. state.renderers.len() {
        let mut events = state.renderers[i].get_input_events();
        state.input_state.pending_input_events.append(&mut events);
    }
}

#[no_mangle]
pub extern "C" fn mod_input_unload( state: &mut State ) {
    state.input_state.clear();
}
<commit_msg>adding mod_input<commit_after>extern crate game_state;

use game_state::state::State;
use game_state::Renderer;

use std::sync::Arc;

#[no_mangle]
pub extern "C" fn mod_input_load( state: &mut State ) {
    state.input_state.clear();
}

#[no_mangle]
pub extern "C" fn mod_input_tick( state: &mut State ) {
    // Renderers own the input event loop associated with their
    // internals: i.e. the window manager window
    // - get input events and convert them to our internal format
    // and push them into the input events queue

    // we want to clear that queue each tick, regardless of if we dealt with the events
    state.input_state.pending_input_events.clear();

    // Now we want to
    for i in 0 .. state.renderers.len() {
        let mut events = state.renderers[i].get_input_events();
        println!("{}", events);
        state.input_state.pending_input_events.append(&mut events);
    }
}

#[no_mangle]
pub extern "C" fn mod_input_unload( state: &mut State ) {
    state.input_state.clear();
}
<|endoftext|>