<commit_before>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

use capnp::Error;
use capnp::primitive_list;

use capnp_rpc::{RpcSystem, twoparty, rpc_twoparty_capnp};

use calculator_capnp::calculator;
use capnp::capability::Promise;

use futures::{Future, Stream};
use tokio_core::io::Io;

struct ValueImpl {
    value: f64
}

impl ValueImpl {
    fn new(value: f64) -> ValueImpl {
        ValueImpl { value: value }
    }
}

impl calculator::value::Server for ValueImpl {
    fn read(&mut self,
            _params: calculator::value::ReadParams,
            mut results: calculator::value::ReadResults)
            -> Promise<(), Error>
    {
        results.get().set_value(self.value);
        Promise::ok(())
    }
}

fn evaluate_impl(expression: calculator::expression::Reader,
                 params: Option<primitive_list::Reader<f64>>)
                 -> Promise<f64, Error>
{
    match pry!(expression.which()) {
        calculator::expression::Literal(v) => {
            Promise::ok(v)
        },
        calculator::expression::PreviousResult(p) => {
            Promise::from_future(pry!(p).read_request().send().promise.and_then(|v| {
                Ok(try!(v.get()).get_value())
            }))
        }
        calculator::expression::Parameter(p) => {
            match params {
                Some(params) if p < params.len() => {
                    Promise::ok(params.get(p))
                }
                _ => {
                    Promise::err(Error::failed(format!("bad parameter: {}", p)))
                }
            }
        }
        calculator::expression::Call(call) => {
            let func = pry!(call.get_function());
            let param_promises: Vec<Promise<f64, Error>> =
                pry!(call.get_params()).iter().map(|p| evaluate_impl(p, params)).collect();
            // XXX shouldn't need to collect()

            Promise::from_future(::futures::future::join_all(param_promises).and_then(move |param_values| {
                let mut request = func.call_request();
                {
                    let mut params = request.get().init_params(param_values.len() as u32);
                    for ii in 0..param_values.len() {
                        params.set(ii as u32, param_values[ii]);
                    }
                }
                request.send().promise.and_then(|result| {
                    Ok(try!(result.get()).get_value())
                })
            }))
        }
    }
}

struct FunctionImpl {
    param_count: u32,
    body: ::capnp::message::Builder<::capnp::message::HeapAllocator>,
}

impl FunctionImpl {
    fn new(param_count: u32, body: calculator::expression::Reader) -> ::capnp::Result<FunctionImpl> {
        let mut result = FunctionImpl {
            param_count: param_count,
            body: ::capnp::message::Builder::new_default(),
        };
        try!(result.body.set_root(body));
        Ok(result)
    }
}

impl calculator::function::Server for FunctionImpl {
    fn call(&mut self,
            params: calculator::function::CallParams,
            mut results: calculator::function::CallResults)
        -> Promise<(), Error>
    {
        let params = pry!(pry!(params.get()).get_params());
        if params.len() != self.param_count {
            Promise::err(Error::failed(
                format!("Expect {} parameters but got {}.", self.param_count, params.len())))
        } else {
            Promise::from_future(evaluate_impl(
                pry!(self.body.get_root::<calculator::expression::Builder>()).as_reader(),
                Some(params)).map(move |v| {
                    results.get().set_value(v);
                }))
        }
    }
}

#[derive(Clone, Copy)]
pub struct OperatorImpl {
    op: calculator::Operator,
}

impl calculator::function::Server for OperatorImpl {
    fn call(&mut self,
            params: calculator::function::CallParams,
            mut results: calculator::function::CallResults)
            -> Promise<(), Error>
    {
        let params = pry!(pry!(params.get()).get_params());
        if params.len() != 2 {
            Promise::err(Error::failed("Wrong number of paramters.".to_string()))
        } else {
            let v = match self.op {
                calculator::Operator::Add =>       params.get(0) + params.get(1),
                calculator::Operator::Subtract =>  params.get(0) - params.get(1),
                calculator::Operator::Multiply =>  params.get(0) * params.get(1),
                calculator::Operator::Divide =>    params.get(0) / params.get(1),
            };
            results.get().set_value(v);
            Promise::ok(())
        }
    }
}

struct CalculatorImpl;

impl calculator::Server for CalculatorImpl {
    fn evaluate(&mut self,
                params: calculator::EvaluateParams,
                mut results: calculator::EvaluateResults)
                -> Promise<(), Error>
    {
        Promise::from_future(evaluate_impl(pry!(pry!(params.get()).get_expression()), None).map(move |v| {
            results.get().set_value(
                calculator::value::ToClient::new(ValueImpl::new(v)).from_server::<::capnp_rpc::Server>());
        }))
    }
    fn def_function(&mut self,
                    params: calculator::DefFunctionParams,
                    mut results: calculator::DefFunctionResults)
                    -> Promise<(), Error>
    {
        results.get().set_func(
            calculator::function::ToClient::new(
                pry!(FunctionImpl::new(pry!(params.get()).get_param_count() as u32,
                                       pry!(pry!(params.get()).get_body()))))
                .from_server::<::capnp_rpc::Server>());
        Promise::ok(())
    }
    fn get_operator(&mut self,
                    params: calculator::GetOperatorParams,
                    mut results: calculator::GetOperatorResults)
                    -> Promise<(), Error>
    {
        let op = pry!(pry!(params.get()).get_op());
        results.get().set_func(
            calculator::function::ToClient::new(OperatorImpl {op : op}).from_server::<::capnp_rpc::Server>());
        Promise::ok(())
    }
}

pub fn main() {
    use std::net::ToSocketAddrs;
    let args: Vec<String> = ::std::env::args().collect();
    if args.len() != 3 {
        println!("usage: {} server ADDRESS[:PORT]", args[0]);
        return;
    }

    let mut core = ::tokio_core::reactor::Core::new().unwrap();
    let handle = core.handle();

    let addr = args[2].to_socket_addrs().unwrap().next().expect("could not parse address");
    let socket = ::tokio_core::net::TcpListener::bind(&addr, &handle).unwrap();

    let calc =
        calculator::ToClient::new(CalculatorImpl).from_server::<::capnp_rpc::Server>();

    let done = socket.incoming().for_each(move |(socket, _addr)| {
        let (reader, writer) = socket.split();
        let handle = handle.clone();

        let mut network =
            twoparty::VatNetwork::new(reader, writer, &handle,
                                      rpc_twoparty_capnp::Side::Server, Default::default());
        let disconnect_promise = network.on_disconnect();

        let rpc_system = RpcSystem::new(Box::new(network), Some(calc.clone().client), handle.clone());

        handle.spawn(disconnect_promise.and_then(move |()| { drop(rpc_system); Ok(()) }).map_err(|_| ()));

        Ok(())
    });

    core.run(done).unwrap();
}
<commit_msg>add note about iterator issue<commit_after>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

use capnp::Error;
use capnp::primitive_list;

use capnp_rpc::{RpcSystem, twoparty, rpc_twoparty_capnp};

use calculator_capnp::calculator;
use capnp::capability::Promise;

use futures::{Future, Stream};
use tokio_core::io::Io;

struct ValueImpl {
    value: f64
}

impl ValueImpl {
    fn new(value: f64) -> ValueImpl {
        ValueImpl { value: value }
    }
}

impl calculator::value::Server for ValueImpl {
    fn read(&mut self,
            _params: calculator::value::ReadParams,
            mut results: calculator::value::ReadResults)
            -> Promise<(), Error>
    {
        results.get().set_value(self.value);
        Promise::ok(())
    }
}

fn evaluate_impl(expression: calculator::expression::Reader,
                 params: Option<primitive_list::Reader<f64>>)
                 -> Promise<f64, Error>
{
    match pry!(expression.which()) {
        calculator::expression::Literal(v) => {
            Promise::ok(v)
        },
        calculator::expression::PreviousResult(p) => {
            Promise::from_future(pry!(p).read_request().send().promise.and_then(|v| {
                Ok(try!(v.get()).get_value())
            }))
        }
        calculator::expression::Parameter(p) => {
            match params {
                Some(params) if p < params.len() => {
                    Promise::ok(params.get(p))
                }
                _ => {
                    Promise::err(Error::failed(format!("bad parameter: {}", p)))
                }
            }
        }
        calculator::expression::Call(call) => {
            let func = pry!(call.get_function());
            let param_promises: Vec<Promise<f64, Error>> =
                pry!(call.get_params()).iter().map(|p| evaluate_impl(p, params)).collect();
            // XXX shouldn't need to collect()
            // see https://github.com/alexcrichton/futures-rs/issues/285

            Promise::from_future(::futures::future::join_all(param_promises).and_then(move |param_values| {
                let mut request = func.call_request();
                {
                    let mut params = request.get().init_params(param_values.len() as u32);
                    for ii in 0..param_values.len() {
                        params.set(ii as u32, param_values[ii]);
                    }
                }
                request.send().promise.and_then(|result| {
                    Ok(try!(result.get()).get_value())
                })
            }))
        }
    }
}

struct FunctionImpl {
    param_count: u32,
    body: ::capnp::message::Builder<::capnp::message::HeapAllocator>,
}

impl FunctionImpl {
    fn new(param_count: u32, body: calculator::expression::Reader) -> ::capnp::Result<FunctionImpl> {
        let mut result = FunctionImpl {
            param_count: param_count,
            body: ::capnp::message::Builder::new_default(),
        };
        try!(result.body.set_root(body));
        Ok(result)
    }
}

impl calculator::function::Server for FunctionImpl {
    fn call(&mut self,
            params: calculator::function::CallParams,
            mut results: calculator::function::CallResults)
        -> Promise<(), Error>
    {
        let params = pry!(pry!(params.get()).get_params());
        if params.len() != self.param_count {
            Promise::err(Error::failed(
                format!("Expect {} parameters but got {}.", self.param_count, params.len())))
        } else {
            Promise::from_future(evaluate_impl(
                pry!(self.body.get_root::<calculator::expression::Builder>()).as_reader(),
                Some(params)).map(move |v| {
                    results.get().set_value(v);
                }))
        }
    }
}

#[derive(Clone, Copy)]
pub struct OperatorImpl {
    op: calculator::Operator,
}

impl calculator::function::Server for OperatorImpl {
    fn call(&mut self,
            params: calculator::function::CallParams,
            mut results: calculator::function::CallResults)
            -> Promise<(), Error>
    {
        let params = pry!(pry!(params.get()).get_params());
        if params.len() != 2 {
            Promise::err(Error::failed("Wrong number of paramters.".to_string()))
        } else {
            let v = match self.op {
                calculator::Operator::Add =>       params.get(0) + params.get(1),
                calculator::Operator::Subtract =>  params.get(0) - params.get(1),
                calculator::Operator::Multiply =>  params.get(0) * params.get(1),
                calculator::Operator::Divide =>    params.get(0) / params.get(1),
            };
            results.get().set_value(v);
            Promise::ok(())
        }
    }
}

struct CalculatorImpl;

impl calculator::Server for CalculatorImpl {
    fn evaluate(&mut self,
                params: calculator::EvaluateParams,
                mut results: calculator::EvaluateResults)
                -> Promise<(), Error>
    {
        Promise::from_future(evaluate_impl(pry!(pry!(params.get()).get_expression()), None).map(move |v| {
            results.get().set_value(
                calculator::value::ToClient::new(ValueImpl::new(v)).from_server::<::capnp_rpc::Server>());
        }))
    }
    fn def_function(&mut self,
                    params: calculator::DefFunctionParams,
                    mut results: calculator::DefFunctionResults)
                    -> Promise<(), Error>
    {
        results.get().set_func(
            calculator::function::ToClient::new(
                pry!(FunctionImpl::new(pry!(params.get()).get_param_count() as u32,
                                       pry!(pry!(params.get()).get_body()))))
                .from_server::<::capnp_rpc::Server>());
        Promise::ok(())
    }
    fn get_operator(&mut self,
                    params: calculator::GetOperatorParams,
                    mut results: calculator::GetOperatorResults)
                    -> Promise<(), Error>
    {
        let op = pry!(pry!(params.get()).get_op());
        results.get().set_func(
            calculator::function::ToClient::new(OperatorImpl {op : op}).from_server::<::capnp_rpc::Server>());
        Promise::ok(())
    }
}

pub fn main() {
    use std::net::ToSocketAddrs;
    let args: Vec<String> = ::std::env::args().collect();
    if args.len() != 3 {
        println!("usage: {} server ADDRESS[:PORT]", args[0]);
        return;
    }

    let mut core = ::tokio_core::reactor::Core::new().unwrap();
    let handle = core.handle();

    let addr = args[2].to_socket_addrs().unwrap().next().expect("could not parse address");
    let socket = ::tokio_core::net::TcpListener::bind(&addr, &handle).unwrap();

    let calc =
        calculator::ToClient::new(CalculatorImpl).from_server::<::capnp_rpc::Server>();

    let done = socket.incoming().for_each(move |(socket, _addr)| {
        let (reader, writer) = socket.split();
        let handle = handle.clone();

        let mut network =
            twoparty::VatNetwork::new(reader, writer, &handle,
                                      rpc_twoparty_capnp::Side::Server, Default::default());
        let disconnect_promise = network.on_disconnect();

        let rpc_system = RpcSystem::new(Box::new(network), Some(calc.clone().client), handle.clone());

        handle.spawn(disconnect_promise.and_then(move |()| { drop(rpc_system); Ok(()) }).map_err(|_| ()));

        Ok(())
    });

    core.run(done).unwrap();
}
<|endoftext|>


<commit_before>//! Provides an interface to output sound to the user's speakers.
//!
//! It consists of two main types: `SoundData` is just raw sound data,
//! and a `Source` is a `SoundData` connected to a particular sound
//! channel.

use std::fmt;
use std::io;
use std::io::Read;
use std::path;

use std::sync::Arc;

use rodio;

use context::Context;
use GameError;
use GameResult;

/// A struct that contains all information for tracking sound info.
///
/// You generally don't have to create this yourself, it will be part
/// of your `Context` object.
pub struct AudioContext {
    endpoint: rodio::Endpoint,
}

impl AudioContext {
    /// Create new AudioContext.
    pub fn new() -> GameResult<AudioContext> {
        let error = GameError::AudioError(String::from(
            "Could not initialize sound system (for \
             some reason)",
        ));
        // This is deprecated in cpal but not in the most recent rodio (0.5.2) yet.
        #[allow(deprecated)]
        let e = rodio::get_default_endpoint().ok_or(error)?;
        Ok(AudioContext { endpoint: e })
    }
}

impl fmt::Debug for AudioContext {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<AudioContext: {:p}>", self)
    }
}

/// Static sound data stored in memory.
/// It is Arc'ed, so cheap to clone.
#[derive(Clone, Debug)]
pub struct SoundData(Arc<Box<[u8]>>);

impl SoundData {
    /// Copies the data in the given slice into a new SoundData object.
    pub fn from_bytes(data: &[u8]) -> Self {
        SoundData::from(Vec::from(data))
    }

    /// Creates a SoundData from any Read object; this involves
    /// copying it into a buffer.
    pub fn from_read<R>(reader: &mut R) -> GameResult<Self>
    where
        R: Read,
    {
        let mut buffer = Vec::new();
        reader.read_to_end(&mut buffer)?;

        Ok(SoundData::from(buffer))
    }
}

impl From<Vec<u8>> for SoundData {
    fn from(v: Vec<u8>) -> Self {
        v.into_boxed_slice().into()
    }
}

impl From<Box<[u8]>> for SoundData {
    fn from(b: Box<[u8]>) -> Self {
        SoundData(Arc::new(b))
    }
}

impl AsRef<[u8]> for SoundData {
    fn as_ref(&self) -> &[u8] {
        self.0.as_ref().as_ref()
    }
}

/// A source of audio data connected to a particular `Channel`.
/// Will stop playing when dropped.
// TODO: Check and see if this matches Love2d's semantics!
// Eventually it might read from a streaming decoder of some kind,
// but for now it is just an in-memory SoundData structure.
// The source of a rodio decoder must be Send, which something
// that contains a reference to a ZipFile is not, so we are going
// to just slurp all the data into memory for now.
// There's really a lot of work that needs to be done here, since
// rodio has gotten better (if still somewhat arcane) and our filesystem
// code has done the data-slurping-from-zip's for us
// but for now it works.
pub struct Source {
    data: io::Cursor<SoundData>,
    sink: rodio::Sink,
}

impl Source {
    /// Create a new Source from the given file.
    pub fn new<P: AsRef<path::Path>>(context: &mut Context, path: P) -> GameResult<Self> {
        let path = path.as_ref();
        let data = {
            let file = &mut context.filesystem.open(path)?;
            SoundData::from_read(file)?
        };
        Source::from_data(context, data)
    }

    /// Creates a new Source using the given SoundData object.
    pub fn from_data(context: &mut Context, data: SoundData) -> GameResult<Self> {
        let sink = rodio::Sink::new(&context.audio_context.endpoint);
        let cursor = io::Cursor::new(data);
        Ok(Source {
            data: cursor,
            sink: sink,
        })
    }

    /// Plays the Source.
    pub fn play(&self) -> GameResult<()> {
        // Creating a new Decoder each time seems a little messy,
        // since it may do checking and data-type detection that is
        // redundant, but it's not super expensive.
        // See https://github.com/ggez/ggez/issues/98 for discussion
        let cursor = self.data.clone();
        let decoder = rodio::Decoder::new(cursor)?;
        self.sink.append(decoder);
        Ok(())
    }

    /// Pauses playback
    pub fn pause(&self) {
        self.sink.pause()
    }

    /// Resumes playback
    pub fn resume(&self) {
        self.sink.play()
    }

    /// Stops playback
    pub fn stop(&self) {
        self.sink.stop()
    }

    /// Gets the current volume
    pub fn volume(&self) -> f32 {
        self.sink.volume()
    }

    /// Sets the current volume
    pub fn set_volume(&mut self, value: f32) {
        self.sink.set_volume(value)
    }

    /// Get whether or not the source is paused
    pub fn paused(&self) -> bool {
        self.sink.is_paused()
    }

    /// Get whether or not the source is playing
    pub fn playing(&self) -> bool {
        !self.paused() // && !self.stopped()
    }
}

impl fmt::Debug for Source {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<Audio source: {:p}>", self)
    }
}

// TODO: global start, stop, volume?
<commit_msg>Change SoundData inner data to Arc<[u8]><commit_after>//! Provides an interface to output sound to the user's speakers.
//!
//! It consists of two main types: `SoundData` is just raw sound data,
//! and a `Source` is a `SoundData` connected to a particular sound
//! channel.

use std::fmt;
use std::io;
use std::io::Read;
use std::path;

use std::sync::Arc;

use rodio;

use context::Context;
use GameError;
use GameResult;

/// A struct that contains all information for tracking sound info.
///
/// You generally don't have to create this yourself, it will be part
/// of your `Context` object.
pub struct AudioContext {
    endpoint: rodio::Endpoint,
}

impl AudioContext {
    /// Create new AudioContext.
    pub fn new() -> GameResult<AudioContext> {
        let error = GameError::AudioError(String::from(
            "Could not initialize sound system (for \
             some reason)",
        ));
        // This is deprecated in cpal but not in the most recent rodio (0.5.2) yet.
        #[allow(deprecated)]
        let e = rodio::get_default_endpoint().ok_or(error)?;
        Ok(AudioContext { endpoint: e })
    }
}

impl fmt::Debug for AudioContext {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<AudioContext: {:p}>", self)
    }
}

/// Static sound data stored in memory.
/// It is Arc'ed, so cheap to clone.
#[derive(Clone, Debug)]
pub struct SoundData(Arc<[u8]>);

impl SoundData {
    /// Copies the data in the given slice into a new SoundData object.
    pub fn from_bytes(data: &[u8]) -> Self {
        SoundData(Arc::from(data))
    }

    /// Creates a SoundData from any Read object; this involves
    /// copying it into a buffer.
    pub fn from_read<R>(reader: &mut R) -> GameResult<Self>
    where
        R: Read,
    {
        let mut buffer = Vec::new();
        reader.read_to_end(&mut buffer)?;

        Ok(SoundData::from(buffer))
    }
}

impl From<Vec<u8>> for SoundData {
    fn from(v: Vec<u8>) -> Self {
        SoundData(Arc::from(v))
    }
}

impl From<Box<[u8]>> for SoundData {
    fn from(b: Box<[u8]>) -> Self {
        SoundData(Arc::from(b))
    }
}

impl AsRef<[u8]> for SoundData {
    fn as_ref(&self) -> &[u8] {
        self.0.as_ref().as_ref()
    }
}

/// A source of audio data connected to a particular `Channel`.
/// Will stop playing when dropped.
// TODO: Check and see if this matches Love2d's semantics!
// Eventually it might read from a streaming decoder of some kind,
// but for now it is just an in-memory SoundData structure.
// The source of a rodio decoder must be Send, which something
// that contains a reference to a ZipFile is not, so we are going
// to just slurp all the data into memory for now.
// There's really a lot of work that needs to be done here, since
// rodio has gotten better (if still somewhat arcane) and our filesystem
// code has done the data-slurping-from-zip's for us
// but for now it works.
pub struct Source {
    data: io::Cursor<SoundData>,
    sink: rodio::Sink,
}

impl Source {
    /// Create a new Source from the given file.
    pub fn new<P: AsRef<path::Path>>(context: &mut Context, path: P) -> GameResult<Self> {
        let path = path.as_ref();
        let data = {
            let file = &mut context.filesystem.open(path)?;
            SoundData::from_read(file)?
        };
        Source::from_data(context, data)
    }

    /// Creates a new Source using the given SoundData object.
    pub fn from_data(context: &mut Context, data: SoundData) -> GameResult<Self> {
        let sink = rodio::Sink::new(&context.audio_context.endpoint);
        let cursor = io::Cursor::new(data);
        Ok(Source {
            data: cursor,
            sink: sink,
        })
    }

    /// Plays the Source.
    pub fn play(&self) -> GameResult<()> {
        // Creating a new Decoder each time seems a little messy,
        // since it may do checking and data-type detection that is
        // redundant, but it's not super expensive.
        // See https://github.com/ggez/ggez/issues/98 for discussion
        let cursor = self.data.clone();
        let decoder = rodio::Decoder::new(cursor)?;
        self.sink.append(decoder);
        Ok(())
    }

    /// Pauses playback
    pub fn pause(&self) {
        self.sink.pause()
    }

    /// Resumes playback
    pub fn resume(&self) {
        self.sink.play()
    }

    /// Stops playback
    pub fn stop(&self) {
        self.sink.stop()
    }

    /// Gets the current volume
    pub fn volume(&self) -> f32 {
        self.sink.volume()
    }

    /// Sets the current volume
    pub fn set_volume(&mut self, value: f32) {
        self.sink.set_volume(value)
    }

    /// Get whether or not the source is paused
    pub fn paused(&self) -> bool {
        self.sink.is_paused()
    }

    /// Get whether or not the source is playing
    pub fn playing(&self) -> bool {
        !self.paused() // && !self.stopped()
    }
}

impl fmt::Debug for Source {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<Audio source: {:p}>", self)
    }
}

// TODO: global start, stop, volume?
<|endoftext|>


<commit_before>use std::fs::File;
use std::path::Path;

use futures::prelude::*;
use futures::sync::mpsc::{self, Sender, Receiver};
use futures_cpupool::CpuPool;
use memmap::{Mmap, Protection};

use ::BATCH_FUTURE_BUFFER_SIZE;
use arc_slice::{self, ArcSlice};
use errors::*;
use marshal::{Hashed, Hasher, ObjectHash, SmallRecord, Tree};
use split::{self, Chunked};
use trace::BatchTrace;

/// A batch of files being marshalled.
pub struct Batch<T: BatchTrace = ()> {
    trace: T,

    marshal_tx: Sender<Hashed>,
    marshal_rx: Receiver<Hashed>,

    marshal_pool: CpuPool,

    size_chunks: usize,
}

impl<T: BatchTrace> Batch<T> {
    pub fn with_trace(marshal_pool: CpuPool, trace: T) -> Self {
        let (marshal_tx, marshal_rx) = mpsc::channel(BATCH_FUTURE_BUFFER_SIZE);

        Batch {
            trace,

            marshal_tx,
            marshal_rx,

            marshal_pool,

            size_chunks: 0,
        }
    }

    /// Read a file as a byte slice. This will memory-map the underlying file.
    ///
    /// * `file` - the file to read. *Must be opened with read permissions!*
    fn read(&mut self, file: &File) -> Result<ArcSlice> {
        Ok(
            arc_slice::mapped(Mmap::open(file, Protection::Read)?),
        )
    }

    /// Read a file as a byte slice. This will memory-map the underlying file.
    fn read_path<P: AsRef<Path>>(&mut self, path: P) -> Result<ArcSlice> {
        self.read(&File::open(path)?)
    }

    /// Chunk the file at the given path.
    pub fn chunk_file<P: AsRef<Path>>(&mut self, path: P) -> Result<Chunked> {
        let slice = self.read_path(path)?;
        let mut trace = self.trace.on_split(slice.len() as u64);
        let chunked = split::chunk_with_trace(slice, &mut trace);

        Ok(chunked)
    }

    /// Marshal a chunked file into a tree of objects, returning the marshalled objects along with
    /// the hash of the root object.
    pub fn marshal_file(
        &mut self,
        chunked: Chunked,
    ) -> Box<Future<Item = ObjectHash, Error = Error> + Send> {
        let tree = Tree::load(chunked.to_vec().into_iter().map(SmallRecord::from));
        let tree_len = tree.len();

        self.size_chunks += tree_len;

        let marshal = tree.marshal(Hasher::with_trace(
            self.marshal_tx.clone(),
            self.trace.on_marshal(tree_len),
        ));

        Box::new(self.marshal_pool.spawn(marshal))
    }

    pub fn len(&self) -> usize {
        self.size_chunks
    }

    /// Convert this `Batch` into a stream of `Hashed` objects.
    pub fn into_stream(self) -> Box<Stream<Item = Hashed, Error = Error> + Send> {
        Box::new(self.marshal_rx.map_err(|()| "Upstream error!".into()))
    }
}
<commit_msg>Refactor: size_chunks => len<commit_after>use std::fs::File;
use std::path::Path;

use futures::prelude::*;
use futures::sync::mpsc::{self, Sender, Receiver};
use futures_cpupool::CpuPool;
use memmap::{Mmap, Protection};

use ::BATCH_FUTURE_BUFFER_SIZE;
use arc_slice::{self, ArcSlice};
use errors::*;
use marshal::{Hashed, Hasher, ObjectHash, SmallRecord, Tree};
use split::{self, Chunked};
use trace::BatchTrace;

/// A batch of files being marshalled.
pub struct Batch<T: BatchTrace = ()> {
    trace: T,

    marshal_tx: Sender<Hashed>,
    marshal_rx: Receiver<Hashed>,

    marshal_pool: CpuPool,

    len: usize,
}

impl<T: BatchTrace> Batch<T> {
    pub fn with_trace(marshal_pool: CpuPool, trace: T) -> Self {
        let (marshal_tx, marshal_rx) = mpsc::channel(BATCH_FUTURE_BUFFER_SIZE);

        Batch {
            trace,

            marshal_tx,
            marshal_rx,

            marshal_pool,

            len: 0,
        }
    }

    /// Read a file as a byte slice. This will memory-map the underlying file.
    ///
    /// * `file` - the file to read. *Must be opened with read permissions!*
    fn read(&mut self, file: &File) -> Result<ArcSlice> {
        Ok(
            arc_slice::mapped(Mmap::open(file, Protection::Read)?),
        )
    }

    /// Read a file as a byte slice. This will memory-map the underlying file.
    fn read_path<P: AsRef<Path>>(&mut self, path: P) -> Result<ArcSlice> {
        self.read(&File::open(path)?)
    }

    /// Chunk the file at the given path.
    pub fn chunk_file<P: AsRef<Path>>(&mut self, path: P) -> Result<Chunked> {
        let slice = self.read_path(path)?;
        let mut trace = self.trace.on_split(slice.len() as u64);
        let chunked = split::chunk_with_trace(slice, &mut trace);

        Ok(chunked)
    }

    /// Marshal a chunked file into a tree of objects, returning the marshalled objects along with
    /// the hash of the root object.
    pub fn marshal_file(
        &mut self,
        chunked: Chunked,
    ) -> Box<Future<Item = ObjectHash, Error = Error> + Send> {
        let tree = Tree::load(chunked.to_vec().into_iter().map(SmallRecord::from));
        let tree_len = tree.len();

        self.len += tree_len;

        let marshal = tree.marshal(Hasher::with_trace(
            self.marshal_tx.clone(),
            self.trace.on_marshal(tree_len),
        ));

        Box::new(self.marshal_pool.spawn(marshal))
    }

    /// The total size of the batch, in chunks.
    pub fn len(&self) -> usize {
        self.len
    }

    /// Convert this `Batch` into a stream of `Hashed` objects.
    pub fn into_stream(self) -> Box<Stream<Item = Hashed, Error = Error> + Send> {
        Box::new(self.marshal_rx.map_err(|()| "Upstream error!".into()))
    }
}
<|endoftext|>


<commit_before>extern crate lz4;

use std::env;
use std::fmt::{Display, Formatter};
use std::fs;
use std::fs::{File, PathExt, OpenOptions};
use std::io::{Error, ErrorKind, Read, Write, Seek, SeekFrom};
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::hash::{Hasher, SipHasher};
use std::path::{Path, PathBuf};

use super::compiler::OutputInfo;
use super::utils::hash_write_stream;
use super::utils::DEFAULT_BUF_SIZE;
use super::io::binary::*;
use super::version;

const HEADER: &'static [u8] = b"OBCF\x00\x02";
const FOOTER: &'static [u8] = b"END\x00";
const SUFFIX: &'static str = ".lz4";

#[derive(Debug)]
pub enum CacheError {
	InvalidHeader(PathBuf),
	InvalidFooter(PathBuf),
	PackedFilesMismatch(PathBuf),
	MutexError(String),
}

impl Display for CacheError {
	fn fmt(&self, f: &mut Formatter) -> Result<(), ::std::fmt::Error> {
		match self {
			&CacheError::InvalidHeader(ref path) => write!(f, "invalid cache file header: {}", path.display()),
			&CacheError::InvalidFooter(ref path) => write!(f, "invalid cache file footer: {}", path.display()),
			&CacheError::PackedFilesMismatch(ref path) => write!(f, "unexpected count of packed cached files: {}", path.display()),
			&CacheError::MutexError(ref message) => write!(f, "mutex error: {}", message),
		}
	}
}

impl ::std::error::Error for CacheError {
	fn description(&self) -> &str {
		match self {
			&CacheError::InvalidHeader(_) => "invalid cache file header",
			&CacheError::InvalidFooter(_) => "invalid cache file footer",
			&CacheError::PackedFilesMismatch(_) => "unexpected count of packed cached files",
			&CacheError::MutexError(_) => "mutex error",
		}
	}

	fn cause(&self) -> Option<&::std::error::Error> {
		None
	}
}

struct FileHash {
	hash: String,
	size: u64,
	modified: u64,
}

#[derive(Clone)]
pub struct Cache {
	file_hash: Arc<Mutex<HashMap<PathBuf, Arc<Mutex<Option<FileHash>>>>>>,
	cache_dir: PathBuf
}

struct CacheFile {
	path: PathBuf,
	size: u64,
	accessed: u64,
}

impl Cache {
	pub fn new() -> Self {
		let cache_dir = match env::var("OCTOBUILD_CACHE") {
			Ok(value) => Path::new(&value).to_path_buf(),
			Err(_) => env::home_dir().unwrap().join(".octobuild").join("cache")
		};
		Cache {
			file_hash: Arc::new(Mutex::new(HashMap::new())),
			cache_dir: cache_dir
		}
	}

	pub fn run_cached<F: Fn()->Result<OutputInfo, Error>>(&self, params: &str, inputs: &Vec<PathBuf>, outputs: &Vec<PathBuf>, worker: F) -> Result<OutputInfo, Error> {
		let hash = try! (self.generate_hash(params, inputs));
		let path = self.cache_dir.join(&hash[0..2]).join(&(hash[2..].to_string() + SUFFIX));
		// Try to read data from cache.
		match read_cache(&path, outputs) {
			Ok(output) => {return Ok(output)}
			Err(_) => {}
		}
		// Run task and save result to cache.
		let output = try !(worker());
		try !(write_cache(&path, outputs, &output));
		Ok(output)
	}

	pub fn cleanup(&self, max_cache_size: u64) -> Result<(), Error> {
		let mut files: Vec<CacheFile> = Vec::new();
		for item in try! (fs::walk_dir(&self.cache_dir)) {
			let path = try! (item).path();
			if path.to_str().map_or(false, |v| v.ends_with(SUFFIX)) {
				let attr = try! (fs::metadata(&path));
				if attr.is_file() {
					files.push(CacheFile {
						path: path,
						size: attr.len(),
						accessed: attr.modified(),
					});
				}
			}
		}
		files.sort_by(|a, b| b.accessed.cmp(&a.accessed));

		let mut cache_size: u64 = 0;
		for item in files.into_iter() {
			cache_size += item.size;
			if cache_size > max_cache_size {
				let _ = try!(fs::remove_file(&item.path));
			}
		}
		Ok(())
	}

	fn generate_hash(&self, params: &str, inputs: &Vec<PathBuf>) -> Result<String, Error> {
		let mut sip_hash = SipHasher::new();
		let hash: &mut Hasher = &mut sip_hash;
		// str
		hash.write(params.as_bytes());
		hash.write_u8(0);
		// inputs
		for input in inputs.iter() {
			let file_hash = try! (self.get_file_hash(input));
			hash.write(file_hash.as_bytes());
		}
		Ok(format!("{:016x}", hash.finish()))
	}

	pub fn get_file_hash(&self, path: &Path) -> Result<String, Error> {
		// Get/create lock for file entry.
		let hash_lock = match self.file_hash.lock() {
			Ok(mut map) => {
				match map.entry(path.to_path_buf()) {
					Entry::Occupied(entry) => entry.get().clone(),
					Entry::Vacant(entry) => entry.insert(Arc::new(Mutex::new(None))).clone()
				}
			}
			Err(e) => {
				return Err(Error::new(ErrorKind::Other, CacheError::MutexError(e.to_string())));
			}
		};
		// Get file hash.
		let result = match hash_lock.lock() { // rust: #22722
			Ok(mut hash_entry) => {
				// Validate entry, if exists.
				match *hash_entry {
					Some(ref value) => {
						let stat = try! (fs::metadata(path));
						if value.size == stat.len() && value.modified == stat.modified() {
							return Ok(value.hash.clone());
						}
					}
					None => {}
				}
				// Calculate hash value.
				let stat = try! (fs::metadata(path));
				let hash = try! (generate_file_hash(path));
				*hash_entry = Some(FileHash {
					hash: hash.clone(),
					size: stat.len(),
					modified: stat.modified(),
				});
				Ok(hash)
			}
			Err(e) => Err(Error::new(ErrorKind::Other, CacheError::MutexError(e.to_string())))
		};
		result
	}
}

fn generate_file_hash(path: &Path) -> Result<String, Error> {
	let mut hash = SipHasher::new();
	let mut file = try! (File::open(path));
	try! (hash_write_stream(&mut hash, &mut file));
	Ok(format!("{:016x}", hash.finish()))
}

fn write_cache(path: &Path, paths: &Vec<PathBuf>, output: &OutputInfo) -> Result<(), Error> {
	if !output.success() {
		return Ok(());
	}
	match path.parent() {
		Some(parent) => try! (fs::create_dir_all(&parent)),
		None => ()
	}

	fn write_cache_compressed<W: Write>(write: W, paths: &Vec<PathBuf>, output: &OutputInfo) -> Result<(), Error> {
		let mut stream = try! (lz4::Encoder::new(write, 1));
		try! (write_cache_inner(&mut stream, paths, output));
		match stream.finish() {
			(_, result) => result
		}
	}

	fn write_cache_inner(stream: &mut Write, paths: &Vec<PathBuf>, output: &OutputInfo) -> Result<(), Error> {
		try! (stream.write_all(HEADER));
		try! (write_usize(stream, paths.len()));
		let mut buf: [u8; DEFAULT_BUF_SIZE] = [0; DEFAULT_BUF_SIZE];
		for path in paths.iter() {
			let mut file = try! (File::open(path));
			loop {
				let size = try! (file.read(&mut buf));
				if size <= 0 {
					break;
				}
				try! (write_usize(stream, size));
				try! (stream.write_all(&buf[0..size]));
			}
			try! (write_usize(stream, 0));
		}
		try! (write_output(stream, output));
		try! (stream.write_all(FOOTER));
		Ok(())
	}

	match write_cache_compressed(try! (File::create(path)), paths, output) {
		Err(e) => {
			let raw_path = path.with_extension("blk");
			let mut stream = BlockWrite(try! (File::create(&raw_path)));
			let _ = stream.write(&version::full_version().into_bytes());
			let _ = write_cache_inner(&mut stream, paths, output);
			println!("COMPRESSION FAILED. PLEASE SEND FILE TO DEVELOPER: {:?}", raw_path);
			return Err(e);
		}
		Ok(v) => Ok(v),
	}
}

struct BlockWrite<W: Write> (W);

impl<W: Write> Write for BlockWrite<W> {
	fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
		try! (write_usize(&mut self.0, buf.len()));
		self.0.write(buf)
	}

	fn flush(&mut self) -> Result<(), Error> {
		self.0.flush()
	}
}

fn read_cache(path: &Path, paths: &Vec<PathBuf>) -> Result<OutputInfo, Error> {
	let mut file = try! (OpenOptions::new().read(true).write(true).open(Path::new(path)));
	try! (file.write(&[4]));
	try! (file.seek(SeekFrom::Start(0)));
	let mut stream = try! (lz4::Decoder::new (file));
	if try! (read_exact(&mut stream, HEADER.len())) != HEADER {
		return Err(Error::new(ErrorKind::InvalidInput, CacheError::InvalidHeader(path.to_path_buf())));
	}
	if try! (read_usize(&mut stream)) != paths.len() {
		return Err(Error::new(ErrorKind::InvalidInput, CacheError::PackedFilesMismatch(path.to_path_buf())));
	}
	for path in paths.iter() {
		let mut file = try! (File::create(path));
		loop {
			let size = try! (read_usize(&mut stream));
			if size == 0 {break;}
			let block = try! (read_exact(&mut stream, size));
			try! (file.write_all(&block));
		}
	}
	let output = try! (read_output(&mut stream));
	if try! (read_exact(&mut stream, FOOTER.len())) != FOOTER {
		return Err(Error::new(ErrorKind::InvalidInput, CacheError::InvalidFooter(path.to_path_buf())));
	}
	Ok(output)
}

fn write_blob(stream: &mut Write, blob: &[u8]) -> Result<(), Error> {
	try! (write_usize(stream, blob.len()));
	try! (stream.write_all(blob));
	Ok(())
}

fn read_blob(stream: &mut Read) -> Result<Vec<u8>, Error> {
	let size = try! (read_usize(stream));
	read_exact(stream, size)
}

fn write_output(stream: &mut Write, output: &OutputInfo) -> Result<(), Error> {
	try! (write_blob(stream, &output.stdout));
	try! (write_blob(stream, &output.stderr));
	Ok(())
}

fn read_output(stream: &mut Read) -> Result<OutputInfo, Error> {
	let stdout = try! (read_blob(stream));
	let stderr = try! (read_blob(stream));
	Ok(OutputInfo {
		status: Some(0),
		stdout: stdout,
		stderr: stderr,
	})
}
<commit_msg>lz4 update: compilation fix<commit_after>extern crate lz4;

use std::env;
use std::fmt::{Display, Formatter};
use std::fs;
use std::fs::{File, PathExt, OpenOptions};
use std::io::{Error, ErrorKind, Read, Write, Seek, SeekFrom};
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::hash::{Hasher, SipHasher};
use std::path::{Path, PathBuf};

use super::compiler::OutputInfo;
use super::utils::hash_write_stream;
use super::utils::DEFAULT_BUF_SIZE;
use super::io::binary::*;
use super::version;

const HEADER: &'static [u8] = b"OBCF\x00\x02";
const FOOTER: &'static [u8] = b"END\x00";
const SUFFIX: &'static str = ".lz4";

#[derive(Debug)]
pub enum CacheError {
	InvalidHeader(PathBuf),
	InvalidFooter(PathBuf),
	PackedFilesMismatch(PathBuf),
	MutexError(String),
}

impl Display for CacheError {
	fn fmt(&self, f: &mut Formatter) -> Result<(), ::std::fmt::Error> {
		match self {
			&CacheError::InvalidHeader(ref path) => write!(f, "invalid cache file header: {}", path.display()),
			&CacheError::InvalidFooter(ref path) => write!(f, "invalid cache file footer: {}", path.display()),
			&CacheError::PackedFilesMismatch(ref path) => write!(f, "unexpected count of packed cached files: {}", path.display()),
			&CacheError::MutexError(ref message) => write!(f, "mutex error: {}", message),
		}
	}
}

impl ::std::error::Error for CacheError {
	fn description(&self) -> &str {
		match self {
			&CacheError::InvalidHeader(_) => "invalid cache file header",
			&CacheError::InvalidFooter(_) => "invalid cache file footer",
			&CacheError::PackedFilesMismatch(_) => "unexpected count of packed cached files",
			&CacheError::MutexError(_) => "mutex error",
		}
	}

	fn cause(&self) -> Option<&::std::error::Error> {
		None
	}
}

struct FileHash {
	hash: String,
	size: u64,
	modified: u64,
}

#[derive(Clone)]
pub struct Cache {
	file_hash: Arc<Mutex<HashMap<PathBuf, Arc<Mutex<Option<FileHash>>>>>>,
	cache_dir: PathBuf
}

struct CacheFile {
	path: PathBuf,
	size: u64,
	accessed: u64,
}

impl Cache {
	pub fn new() -> Self {
		let cache_dir = match env::var("OCTOBUILD_CACHE") {
			Ok(value) => Path::new(&value).to_path_buf(),
			Err(_) => env::home_dir().unwrap().join(".octobuild").join("cache")
		};
		Cache {
			file_hash: Arc::new(Mutex::new(HashMap::new())),
			cache_dir: cache_dir
		}
	}

	pub fn run_cached<F: Fn()->Result<OutputInfo, Error>>(&self, params: &str, inputs: &Vec<PathBuf>, outputs: &Vec<PathBuf>, worker: F) -> Result<OutputInfo, Error> {
		let hash = try! (self.generate_hash(params, inputs));
		let path = self.cache_dir.join(&hash[0..2]).join(&(hash[2..].to_string() + SUFFIX));
		// Try to read data from cache.
		match read_cache(&path, outputs) {
			Ok(output) => {return Ok(output)}
			Err(_) => {}
		}
		// Run task and save result to cache.
		let output = try !(worker());
		try !(write_cache(&path, outputs, &output));
		Ok(output)
	}

	pub fn cleanup(&self, max_cache_size: u64) -> Result<(), Error> {
		let mut files: Vec<CacheFile> = Vec::new();
		for item in try! (fs::walk_dir(&self.cache_dir)) {
			let path = try! (item).path();
			if path.to_str().map_or(false, |v| v.ends_with(SUFFIX)) {
				let attr = try! (fs::metadata(&path));
				if attr.is_file() {
					files.push(CacheFile {
						path: path,
						size: attr.len(),
						accessed: attr.modified(),
					});
				}
			}
		}
		files.sort_by(|a, b| b.accessed.cmp(&a.accessed));

		let mut cache_size: u64 = 0;
		for item in files.into_iter() {
			cache_size += item.size;
			if cache_size > max_cache_size {
				let _ = try!(fs::remove_file(&item.path));
			}
		}
		Ok(())
	}

	fn generate_hash(&self, params: &str, inputs: &Vec<PathBuf>) -> Result<String, Error> {
		let mut sip_hash = SipHasher::new();
		let hash: &mut Hasher = &mut sip_hash;
		// str
		hash.write(params.as_bytes());
		hash.write_u8(0);
		// inputs
		for input in inputs.iter() {
			let file_hash = try! (self.get_file_hash(input));
			hash.write(file_hash.as_bytes());
		}
		Ok(format!("{:016x}", hash.finish()))
	}

	pub fn get_file_hash(&self, path: &Path) -> Result<String, Error> {
		// Get/create lock for file entry.
		let hash_lock = match self.file_hash.lock() {
			Ok(mut map) => {
				match map.entry(path.to_path_buf()) {
					Entry::Occupied(entry) => entry.get().clone(),
					Entry::Vacant(entry) => entry.insert(Arc::new(Mutex::new(None))).clone()
				}
			}
			Err(e) => {
				return Err(Error::new(ErrorKind::Other, CacheError::MutexError(e.to_string())));
			}
		};
		// Get file hash.
		let result = match hash_lock.lock() { // rust: #22722
			Ok(mut hash_entry) => {
				// Validate entry, if exists.
				match *hash_entry {
					Some(ref value) => {
						let stat = try! (fs::metadata(path));
						if value.size == stat.len() && value.modified == stat.modified() {
							return Ok(value.hash.clone());
						}
					}
					None => {}
				}
				// Calculate hash value.
				let stat = try! (fs::metadata(path));
				let hash = try! (generate_file_hash(path));
				*hash_entry = Some(FileHash {
					hash: hash.clone(),
					size: stat.len(),
					modified: stat.modified(),
				});
				Ok(hash)
			}
			Err(e) => Err(Error::new(ErrorKind::Other, CacheError::MutexError(e.to_string())))
		};
		result
	}
}

fn generate_file_hash(path: &Path) -> Result<String, Error> {
	let mut hash = SipHasher::new();
	let mut file = try! (File::open(path));
	try! (hash_write_stream(&mut hash, &mut file));
	Ok(format!("{:016x}", hash.finish()))
}

fn write_cache(path: &Path, paths: &Vec<PathBuf>, output: &OutputInfo) -> Result<(), Error> {
	if !output.success() {
		return Ok(());
	}
	match path.parent() {
		Some(parent) => try! (fs::create_dir_all(&parent)),
		None => ()
	}

	fn write_cache_compressed<W: Write>(write: W, paths: &Vec<PathBuf>, output: &OutputInfo) -> Result<(), Error> {
		let mut stream = try! (lz4::EncoderParams::new().level(1).build(write));
		try! (write_cache_inner(&mut stream, paths, output));
		match stream.finish() {
			(_, result) => result
		}
	}

	fn write_cache_inner(stream: &mut Write, paths: &Vec<PathBuf>, output: &OutputInfo) -> Result<(), Error> {
		try! (stream.write_all(HEADER));
		try! (write_usize(stream, paths.len()));
		let mut buf: [u8; DEFAULT_BUF_SIZE] = [0; DEFAULT_BUF_SIZE];
		for path in paths.iter() {
			let mut file = try! (File::open(path));
			loop {
				let size = try! (file.read(&mut buf));
				if size <= 0 {
					break;
				}
				try! (write_usize(stream, size));
				try! (stream.write_all(&buf[0..size]));
			}
			try! (write_usize(stream, 0));
		}
		try! (write_output(stream, output));
		try! (stream.write_all(FOOTER));
		Ok(())
	}

	match write_cache_compressed(try! (File::create(path)), paths, output) {
		Err(e) => {
			let raw_path = path.with_extension("blk");
			let mut stream = BlockWrite(try! (File::create(&raw_path)));
			let _ = stream.write(&version::full_version().into_bytes());
			let _ = write_cache_inner(&mut stream, paths, output);
			println!("COMPRESSION FAILED. PLEASE SEND FILE TO DEVELOPER: {:?}", raw_path);
			return Err(e);
		}
		Ok(v) => Ok(v),
	}
}

struct BlockWrite<W: Write> (W);

impl<W: Write> Write for BlockWrite<W> {
	fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
		try! (write_usize(&mut self.0, buf.len()));
		self.0.write(buf)
	}

	fn flush(&mut self) -> Result<(), Error> {
		self.0.flush()
	}
}

fn read_cache(path: &Path, paths: &Vec<PathBuf>) -> Result<OutputInfo, Error> {
	let mut file = try! (OpenOptions::new().read(true).write(true).open(Path::new(path)));
	try! (file.write(&[4]));
	try! (file.seek(SeekFrom::Start(0)));
	let mut stream = try! (lz4::Decoder::new (file));
	if try! (read_exact(&mut stream, HEADER.len())) != HEADER {
		return Err(Error::new(ErrorKind::InvalidInput, CacheError::InvalidHeader(path.to_path_buf())));
	}
	if try! (read_usize(&mut stream)) != paths.len() {
		return Err(Error::new(ErrorKind::InvalidInput, CacheError::PackedFilesMismatch(path.to_path_buf())));
	}
	for path in paths.iter() {
		let mut file = try! (File::create(path));
		loop {
			let size = try! (read_usize(&mut stream));
			if size == 0 {break;}
			let block = try! (read_exact(&mut stream, size));
			try! (file.write_all(&block));
		}
	}
	let output = try! (read_output(&mut stream));
	if try! (read_exact(&mut stream, FOOTER.len())) != FOOTER {
		return Err(Error::new(ErrorKind::InvalidInput, CacheError::InvalidFooter(path.to_path_buf())));
	}
	Ok(output)
}

fn write_blob(stream: &mut Write, blob: &[u8]) -> Result<(), Error> {
	try! (write_usize(stream, blob.len()));
	try! (stream.write_all(blob));
	Ok(())
}

fn read_blob(stream: &mut Read) -> Result<Vec<u8>, Error> {
	let size = try! (read_usize(stream));
	read_exact(stream, size)
}

fn write_output(stream: &mut Write, output: &OutputInfo) -> Result<(), Error> {
	try! (write_blob(stream, &output.stdout));
	try! (write_blob(stream, &output.stderr));
	Ok(())
}

fn read_output(stream: &mut Read) -> Result<OutputInfo, Error> {
	let stdout = try! (read_blob(stream));
	let stderr = try! (read_blob(stream));
	Ok(OutputInfo {
		status: Some(0),
		stdout: stdout,
		stderr: stderr,
	})
}
<|endoftext|>


<commit_before>use std::os;
use std::io::FileNotFound;
use std::io::stdio::{stdout, stderr};
use std::io::fs::{rmdir_recursive};
use std::io::fs::{readdir, readlink};
use std::io::process::Process;
use std::from_str::from_str;
use std::str::from_utf8;

use argparse::{ArgumentParser, StoreConst, List};
use collections::treemap::{TreeMap, TreeSet};

use super::env::Environ;
use super::monitor::Monitor;
use super::options::env_options;
use super::linux::{CPipe, run_newuser};
use super::uidmap::write_max_map;

enum CleanMode {
    Help,
    Container,
    TmpFolders,
    OldContainers,
    Transient,
    AllTransient,
    Everything,
}

pub fn run_do_rm(_env: &mut Environ, args: Vec<String>) -> Result<int, String>
{
    let mut dirs: Vec<String> = Vec::new();
    {
        let mut ap = ArgumentParser::new();
        ap.refer(&mut dirs)
            .add_argument("dirs", box List::<String>,
                "Names of directories to remove");
        match ap.parse(args, &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(0) => return Ok(0),
            Err(_) => return Ok(122),
        }
    }
    for d in dirs.iter() {
        try!(rmdir_recursive(&Path::new(d.as_slice()))
            .map_err(|e| format!("Error removing {}: {}", d, e)));
    }
    return Ok(0);
}

pub fn run_rmdirs(exe: &Path, dirs: Vec<Path>) -> Result<(), String> {
    let pipe = try!(CPipe::new());
    let mut monitor = Monitor::new(true);

    let mut args = vec!("__rm".to_string());
    args.extend(dirs.iter().map(|p| p.as_str().unwrap().to_string()));
    let pid = try!(run_newuser(&pipe,
        &exe.as_str().unwrap().to_string(),
        args.as_slice(),
        &TreeMap::new()));

    try!(write_max_map(pid));

    try!(pipe.wakeup());

    monitor.add("child".to_string(), pid);
    monitor.wait_all();
    if monitor.get_status() == 0 {
        return Ok(());
    }
    return Err(format!("Error removing dirs"));
}

pub fn run_clean(env: &mut Environ, args: Vec<String>) -> Result<int, String>
{
    let mut mode: CleanMode = Help;
    let mut names: Vec<String> = Vec::new();
    {
        let mut ap = ArgumentParser::new();
        ap.refer(&mut names)
            .add_argument("names", box List::<String>,
                "Names of containers to delete");
        ap.refer(&mut mode)
            .add_option(["--container"], box StoreConst(Container),
                "Delete specified container(s)")
            .add_option(["--tmp", "--tmp-folders"],
                box StoreConst(TmpFolders),
                "Delete .tmp folders. They are just garbage from previous
                 unsuccessful commands and safe to delete if you don't run any
                 other vagga processes in parallel")
            .add_option(["--everything"], box StoreConst(Everything),
                "Remove `.vagga` directory. Useful mostly before removing
                 project itself. Needed when subuid/subgid used, so some files
                 are not owned by the same user.")
            .add_option(["--old", "--old-containers"],
                box StoreConst(OldContainers),
                "Delete old containers. Currently it deletes all containers
                 not linked in .vagga/xxx directly. Basically it means keep
                 single container (of each name/variant) with version last used
                 for this specific contianer.")
            .add_option(["--tmp", "--transient"],
                box StoreConst(Transient),
                "Remove all transient containers, which do not have running
                 processes. Note running processes are matched by pid, so may
                 not be robust enough in some cases")
            .add_option(["--all-transient"],
                box StoreConst(AllTransient),
                "Remove all transient containers without liveness checking")
            .required();
        env_options(env, &mut ap);
        match ap.parse(args, &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(0) => return Ok(0),
            Err(_) => return Ok(122),
        }
    }

    let mut runenv = TreeMap::new();
    for &(ref k, ref v) in os::env().iter() {
        runenv.insert(k.clone(), v.clone());
    }
    env.populate_environ(&mut runenv);

    match mode {
        Help => return Err(format!("Use one of the cleanup options")),
        Container => {
            let roots = env.local_vagga.join(".roots");
            try!(run_rmdirs(&env.vagga_exe,
                names.iter().map(|n| roots.join(n.as_slice())).collect()));
        }
        Everything => {
            try!(run_rmdirs(&env.vagga_exe, vec!(env.local_vagga.clone())));
        }
        TmpFolders => {
            let roots = env.local_vagga.join(".roots");
            let arts = env.local_vagga.join(".artifacts");
            let mut to_delete = Vec::new();
            for d in [roots, arts].iter() {
                match readdir(d) {
                    Ok(items) => {
                        for path in items.iter() {
                            match path.extension_str() {
                                Some(x) if x.as_slice() == "tmp" => {
                                    to_delete.push(path.clone());
                                }
                                _ => continue,
                            }
                        }
                    }
                    Err(ref e) if e.kind == FileNotFound => {}
                    Err(ref e) => {
                        return Err(format!("Can't read dir {}: {}",
                            d.display(), e));
                    }
                }
            }
            try!(run_rmdirs(&env.vagga_exe, to_delete));
        }
        OldContainers => {
            let links = try!(readdir(&env.local_vagga)
                .map_err(|e| format!("Can't read dir {}: {}",
                    env.local_vagga.display(), e)));
            let mut roots = TreeSet::new();
            for item in links.iter() {
                if item.filename_str().unwrap().starts_with(".") { continue; }
                let lnk = try!(readlink(item)
                    .map_err(|e| format!("Can't read link {}: {}",
                        item.display(), e)));
                if lnk.dir_path() == Path::new(".roots") {
                    roots.insert(lnk.filename_str().unwrap().to_string());
                }
            }
            debug!("Roots alive: {}", roots);
            let items = try!(readdir(&env.local_vagga.join(".roots"))
                .map_err(|e| format!("Can't read dir {}/.roots: {}",
                    env.local_vagga.display(), e)));
            let mut to_delete = Vec::new();
            for path in items.iter() {
                match path.filename_str() {
                    Some(n) if roots.contains(&n.to_string()) => {
                        continue;
                    }
                    _ => { to_delete.push(path.clone()); }
                }
            }
            try!(run_rmdirs(&env.vagga_exe, to_delete));
        }
        AllTransient => {
            let transient = env.local_vagga.join(".transients");
            let mut to_delete = Vec::new();
            match readdir(&transient) {
                Ok(items) => {
                    for path in items.iter() {
                        to_delete.push(path.clone());
                    }
                }
                Err(ref e) if e.kind == FileNotFound => {}
                Err(ref e) => {
                    return Err(format!("Can't read dir {}: {}",
                        transient.display(), e));
                }
            }
            try!(run_rmdirs(&env.vagga_exe, to_delete));
        }
        Transient => {
            let transient = env.local_vagga.join(".transients");
            let mut to_delete = Vec::new();
            match readdir(&transient) {
                Ok(items) => {
                    for path in items.iter() {
                        let pid = match path.extension()
                            .and_then(from_utf8)
                            .and_then(from_str) {
                            None => continue,  // Not our dir, skip
                            Some(pid) => pid,
                        };
                        if Process::kill(pid, 0).ok().is_none() {
                            to_delete.push(path.clone());
                        }
                    }
                }
                Err(ref e) if e.kind == FileNotFound => {}
                Err(ref e) => {
                    return Err(format!("Can't read dir {}: {}",
                        transient.display(), e));
                }
            }
            try!(run_rmdirs(&env.vagga_exe, to_delete));
        }
    }

    return Ok(0);
}
<commit_msg>Remove duplicate `--tmp` flag<commit_after>use std::os;
use std::io::FileNotFound;
use std::io::stdio::{stdout, stderr};
use std::io::fs::{rmdir_recursive};
use std::io::fs::{readdir, readlink};
use std::io::process::Process;
use std::from_str::from_str;
use std::str::from_utf8;

use argparse::{ArgumentParser, StoreConst, List};
use collections::treemap::{TreeMap, TreeSet};

use super::env::Environ;
use super::monitor::Monitor;
use super::options::env_options;
use super::linux::{CPipe, run_newuser};
use super::uidmap::write_max_map;

enum CleanMode {
    Help,
    Container,
    TmpFolders,
    OldContainers,
    Transient,
    AllTransient,
    Everything,
}

pub fn run_do_rm(_env: &mut Environ, args: Vec<String>) -> Result<int, String>
{
    let mut dirs: Vec<String> = Vec::new();
    {
        let mut ap = ArgumentParser::new();
        ap.refer(&mut dirs)
            .add_argument("dirs", box List::<String>,
                "Names of directories to remove");
        match ap.parse(args, &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(0) => return Ok(0),
            Err(_) => return Ok(122),
        }
    }
    for d in dirs.iter() {
        try!(rmdir_recursive(&Path::new(d.as_slice()))
            .map_err(|e| format!("Error removing {}: {}", d, e)));
    }
    return Ok(0);
}

pub fn run_rmdirs(exe: &Path, dirs: Vec<Path>) -> Result<(), String> {
    let pipe = try!(CPipe::new());
    let mut monitor = Monitor::new(true);

    let mut args = vec!("__rm".to_string());
    args.extend(dirs.iter().map(|p| p.as_str().unwrap().to_string()));
    let pid = try!(run_newuser(&pipe,
        &exe.as_str().unwrap().to_string(),
        args.as_slice(),
        &TreeMap::new()));

    try!(write_max_map(pid));

    try!(pipe.wakeup());

    monitor.add("child".to_string(), pid);
    monitor.wait_all();
    if monitor.get_status() == 0 {
        return Ok(());
    }
    return Err(format!("Error removing dirs"));
}

pub fn run_clean(env: &mut Environ, args: Vec<String>) -> Result<int, String>
{
    let mut mode: CleanMode = Help;
    let mut names: Vec<String> = Vec::new();
    {
        let mut ap = ArgumentParser::new();
        ap.refer(&mut names)
            .add_argument("names", box List::<String>,
                "Names of containers to delete");
        ap.refer(&mut mode)
            .add_option(["--container"], box StoreConst(Container),
                "Delete specified container(s)")
            .add_option(["--tmp", "--tmp-folders"],
                box StoreConst(TmpFolders),
                "Delete .tmp folders. They are just garbage from previous
                 unsuccessful commands and safe to delete if you don't run any
                 other vagga processes in parallel")
            .add_option(["--everything"], box StoreConst(Everything),
                "Remove `.vagga` directory. Useful mostly before removing
                 project itself. Needed when subuid/subgid used, so some files
                 are not owned by the same user.")
            .add_option(["--old", "--old-containers"],
                box StoreConst(OldContainers),
                "Delete old containers. Currently it deletes all containers
                 not linked in .vagga/xxx directly. Basically it means keep
                 single container (of each name/variant) with version last used
                 for this specific contianer.")
            .add_option(["--transient"], box StoreConst(Transient),
                "Remove all transient containers, which do not have running
                 processes. Note running processes are matched by pid, so may
                 not be robust enough in some cases")
            .add_option(["--all-transient"],
                box StoreConst(AllTransient),
                "Remove all transient containers without liveness checking")
            .required();
        env_options(env, &mut ap);
        match ap.parse(args, &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(0) => return Ok(0),
            Err(_) => return Ok(122),
        }
    }

    let mut runenv = TreeMap::new();
    for &(ref k, ref v) in os::env().iter() {
        runenv.insert(k.clone(), v.clone());
    }
    env.populate_environ(&mut runenv);

    match mode {
        Help => return Err(format!("Use one of the cleanup options")),
        Container => {
            let roots = env.local_vagga.join(".roots");
            try!(run_rmdirs(&env.vagga_exe,
                names.iter().map(|n| roots.join(n.as_slice())).collect()));
        }
        Everything => {
            try!(run_rmdirs(&env.vagga_exe, vec!(env.local_vagga.clone())));
        }
        TmpFolders => {
            let roots = env.local_vagga.join(".roots");
            let arts = env.local_vagga.join(".artifacts");
            let mut to_delete = Vec::new();
            for d in [roots, arts].iter() {
                match readdir(d) {
                    Ok(items) => {
                        for path in items.iter() {
                            match path.extension_str() {
                                Some(x) if x.as_slice() == "tmp" => {
                                    to_delete.push(path.clone());
                                }
                                _ => continue,
                            }
                        }
                    }
                    Err(ref e) if e.kind == FileNotFound => {}
                    Err(ref e) => {
                        return Err(format!("Can't read dir {}: {}",
                            d.display(), e));
                    }
                }
            }
            try!(run_rmdirs(&env.vagga_exe, to_delete));
        }
        OldContainers => {
            let links = try!(readdir(&env.local_vagga)
                .map_err(|e| format!("Can't read dir {}: {}",
                    env.local_vagga.display(), e)));
            let mut roots = TreeSet::new();
            for item in links.iter() {
                if item.filename_str().unwrap().starts_with(".") { continue; }
                let lnk = try!(readlink(item)
                    .map_err(|e| format!("Can't read link {}: {}",
                        item.display(), e)));
                if lnk.dir_path() == Path::new(".roots") {
                    roots.insert(lnk.filename_str().unwrap().to_string());
                }
            }
            debug!("Roots alive: {}", roots);
            let items = try!(readdir(&env.local_vagga.join(".roots"))
                .map_err(|e| format!("Can't read dir {}/.roots: {}",
                    env.local_vagga.display(), e)));
            let mut to_delete = Vec::new();
            for path in items.iter() {
                match path.filename_str() {
                    Some(n) if roots.contains(&n.to_string()) => {
                        continue;
                    }
                    _ => { to_delete.push(path.clone()); }
                }
            }
            try!(run_rmdirs(&env.vagga_exe, to_delete));
        }
        AllTransient => {
            let transient = env.local_vagga.join(".transients");
            let mut to_delete = Vec::new();
            match readdir(&transient) {
                Ok(items) => {
                    for path in items.iter() {
                        to_delete.push(path.clone());
                    }
                }
                Err(ref e) if e.kind == FileNotFound => {}
                Err(ref e) => {
                    return Err(format!("Can't read dir {}: {}",
                        transient.display(), e));
                }
            }
            try!(run_rmdirs(&env.vagga_exe, to_delete));
        }
        Transient => {
            let transient = env.local_vagga.join(".transients");
            let mut to_delete = Vec::new();
            match readdir(&transient) {
                Ok(items) => {
                    for path in items.iter() {
                        let pid = match path.extension()
                            .and_then(from_utf8)
                            .and_then(from_str) {
                            None => continue,  // Not our dir, skip
                            Some(pid) => pid,
                        };
                        if Process::kill(pid, 0).ok().is_none() {
                            to_delete.push(path.clone());
                        }
                    }
                }
                Err(ref e) if e.kind == FileNotFound => {}
                Err(ref e) => {
                    return Err(format!("Can't read dir {}: {}",
                        transient.display(), e));
                }
            }
            try!(run_rmdirs(&env.vagga_exe, to_delete));
        }
    }

    return Ok(0);
}
<|endoftext|>


<commit_before>#![feature(plugin_registrar)]
#![feature(quote)]
#![feature(phase)]

#[phase(plugin)]
extern crate regex_macros;

extern crate regex;
extern crate rustc;
extern crate syntax;

use std::io::Reader;
use std::io::process::Command;
use std::path::Path;
use syntax::parse::token;
use syntax::ast::{ TokenTree };
use syntax::ext::base::{DummyResult, ExtCtxt, MacResult, MacItem};
use syntax::codemap::Span;

#[plugin_registrar]
#[doc(hidden)]
pub fn plugin_registrar(reg: &mut ::rustc::plugin::Registry) {
    reg.register_macro("cmake", macro_handler);
}

// main handler for the macro
fn macro_handler(ecx: &mut ExtCtxt, span: Span, token_tree: &[TokenTree]) -> Box<MacResult> {
    // getting the arguments from the macro
    let (srcPath, libname) = match parse_macro_arguments(ecx, token_tree) {
        Ok(t) => t,
        Err(msg) => {
            ecx.span_err(span, msg.as_slice());
            return DummyResult::any(span)
        }
    };

    // absolute path
    let srcPath = ::std::os::make_absolute(&srcPath);

    // creating the build directory
    let buildDirectory = srcPath.join("cmake-build");

    // creating the directory if it doesn't exist
    if !buildDirectory.exists() {
        match ::std::io::fs::mkdir_recursive(&buildDirectory, ::std::io::UserRWX) {
            Ok(_) => (),
            Err(err) => {
                ecx.span_err(span, format!("{}", err).as_slice());
                return DummyResult::any(span)
            }
        }
    }

    // getting the libraries output directory
    let outputDir = srcPath.join("cmake-build-result");
    if !outputDir.exists() {
        match ::std::io::fs::mkdir_recursive(&outputDir, ::std::io::UserRWX) {
            Ok(_) => (),
            Err(err) => {
                ecx.span_err(span, format!("{}", err).as_slice());
                return DummyResult::any(span)
            }
        }
    }

    // getting the generator
    let (generatorCmake, generatorExec) = if cfg!(windows) {
        ("MinGW Makefiles", "mingw32-make")
    } else {
        ("Makefiles", "make")
    };

    // invoking CMake
    let cmakeProcess = match Command::new("cmake")
                                .cwd(&buildDirectory)
                                .args(&["-G", generatorCmake])
                                .arg(format!("-DCMAKE_LIBRARY_OUTPUT_DIRECTORY:dir={}", outputDir.display()))
                                .arg(format!("-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY:dir={}", outputDir.display()))
                                .arg(format!("{}", srcPath.display()))
                                .spawn()
    {
        Ok(p) => p,
        Err(err) => {
            ecx.span_err(span, format!("{}", err).as_slice());
            return DummyResult::any(span)
        }
    };

    // the content of stderr is converted into compilation errors
    match cmakeProcess.stderr.clone() {
        Some(mut stderr) => match stderr.read_to_str() {
            Ok(s) => if s.len() >= 1 {
                ecx.span_err(span, s.as_slice());
                return DummyResult::any(span)
            },
            Err(err) => ecx.span_err(span, format!("{}", err).as_slice())
        },
        None => ecx.span_warn(span, "could not open stderr pipe to cmake")
    }

    // invoking make
    let mut makeCommand = Command::new(generatorExec);
    makeCommand.cwd(&buildDirectory);
    match &libname { &Some(ref l) => { makeCommand.arg(l.as_slice()); }, _ => () };
    let makeProcess = match makeCommand.spawn() {
        Ok(p) => p,
        Err(err) => {
            ecx.span_err(span, format!("{}", err).as_slice());
            return DummyResult::any(span)
        }
    };

    // the content of stderr is converted into compilation errors
    match makeProcess.stderr.clone() {
        Some(mut stderr) => match stderr.read_to_str() {
            Ok(s) => if s.len() >= 1 {
                ecx.span_err(span, s.as_slice());
                return DummyResult::any(span)
            },
            Err(err) => ecx.span_err(span, format!("{}", err).as_slice())
        },
        None => ecx.span_warn(span, "could not open stderr pipe to cmake")
    }

    // outputting the library name
    {
        let link = match &libname {
            &Some(ref l) => format!("#[link_args = \"-L {}\"]\n#[link(name=\"{}\")]\nextern {{}}", outputDir.display().as_maybe_owned().escape_default().as_slice(), l),
            &None => format!("#[link_args = \"-L {}\"]\nextern {{}}", outputDir.display().as_maybe_owned().escape_default().as_slice())
        };
        str_to_item(ecx, link.as_slice())
    }
}

fn str_to_item(ecx: &mut ExtCtxt, content: &str) -> Box<MacResult> {
    let mut parser = ::syntax::parse::new_parser_from_source_str(ecx.parse_sess(), ecx.cfg(), "".to_string(), content.to_string());

    match parser.parse_item_with_outer_attributes() {
        None => fail!("unable to parse library output into items"),
        Some(i) => MacItem::new(i)
    }
}

// converts an Expr into a String if possible
fn expr_to_literal(expr: ::std::gc::Gc<::syntax::ast::Expr>)
    -> Result<String, String>
{
    Ok(match expr.node {
        syntax::ast::ExprLit(lit) => {
            match lit.node {
                syntax::ast::LitStr(ref s, _) => s.to_str(),
                _ => return Err(format!("expected string literal but got `{}`", syntax::print::pprust::lit_to_str(lit)))
            }
        }
        _ => return Err(format!("expected string literal but got `{}`", syntax::print::pprust::expr_to_str(expr)))
    })
}

// tries to obtain the macro arguments from the token tree
fn parse_macro_arguments(cx: &mut ExtCtxt, tts: &[syntax::ast::TokenTree])
    -> Result<(Path, Option<String>), String>
{
    let mut parser = ::syntax::parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), Vec::from_slice(tts));

    let path = match Path::new_opt(try!(expr_to_literal(cx.expand_expr(parser.parse_expr())))) {
        Some(p) => p,
        None => return Err(format!("the path is not utf-8!"))
    };

    let libname = if parser.eat(&token::COMMA) {
        Some(try!(expr_to_literal(cx.expand_expr(parser.parse_expr()))))
    } else { None };

    if !parser.eat(&token::EOF) {
        return Err("too many arguments".to_string());
    }

    Ok((path, libname))
}
<commit_msg>Fix makefiles for unix<commit_after>#![feature(plugin_registrar)]
#![feature(quote)]
#![feature(phase)]

#[phase(plugin)]
extern crate regex_macros;

extern crate regex;
extern crate rustc;
extern crate syntax;

use std::io::Reader;
use std::io::process::Command;
use std::path::Path;
use syntax::parse::token;
use syntax::ast::{ TokenTree };
use syntax::ext::base::{DummyResult, ExtCtxt, MacResult, MacItem};
use syntax::codemap::Span;

#[plugin_registrar]
#[doc(hidden)]
pub fn plugin_registrar(reg: &mut ::rustc::plugin::Registry) {
    reg.register_macro("cmake", macro_handler);
}

// main handler for the macro
fn macro_handler(ecx: &mut ExtCtxt, span: Span, token_tree: &[TokenTree]) -> Box<MacResult> {
    // getting the arguments from the macro
    let (srcPath, libname) = match parse_macro_arguments(ecx, token_tree) {
        Ok(t) => t,
        Err(msg) => {
            ecx.span_err(span, msg.as_slice());
            return DummyResult::any(span)
        }
    };

    // absolute path
    let srcPath = ::std::os::make_absolute(&srcPath);

    // creating the build directory
    let buildDirectory = srcPath.join("cmake-build");

    // creating the directory if it doesn't exist
    if !buildDirectory.exists() {
        match ::std::io::fs::mkdir_recursive(&buildDirectory, ::std::io::UserRWX) {
            Ok(_) => (),
            Err(err) => {
                ecx.span_err(span, format!("{}", err).as_slice());
                return DummyResult::any(span)
            }
        }
    }

    // getting the libraries output directory
    let outputDir = srcPath.join("cmake-build-result");
    if !outputDir.exists() {
        match ::std::io::fs::mkdir_recursive(&outputDir, ::std::io::UserRWX) {
            Ok(_) => (),
            Err(err) => {
                ecx.span_err(span, format!("{}", err).as_slice());
                return DummyResult::any(span)
            }
        }
    }

    // getting the generator
    let (generatorCmake, generatorExec) = if cfg!(windows) {
        ("MinGW Makefiles", "mingw32-make")
    } else {
        ("Unix Makefiles", "make")
    };

    // invoking CMake
    let cmakeProcess = match Command::new("cmake")
                                .cwd(&buildDirectory)
                                .args(&["-G", generatorCmake])
                                .arg(format!("-DCMAKE_LIBRARY_OUTPUT_DIRECTORY:dir={}", outputDir.display()))
                                .arg(format!("-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY:dir={}", outputDir.display()))
                                .arg(format!("{}", srcPath.display()))
                                .spawn()
    {
        Ok(p) => p,
        Err(err) => {
            ecx.span_err(span, format!("{}", err).as_slice());
            return DummyResult::any(span)
        }
    };

    // the content of stderr is converted into compilation errors
    match cmakeProcess.stderr.clone() {
        Some(mut stderr) => match stderr.read_to_str() {
            Ok(s) => if s.len() >= 1 {
                ecx.span_err(span, s.as_slice());
                return DummyResult::any(span)
            },
            Err(err) => ecx.span_err(span, format!("{}", err).as_slice())
        },
        None => ecx.span_warn(span, "could not open stderr pipe to cmake")
    }

    // invoking make
    let mut makeCommand = Command::new(generatorExec);
    makeCommand.cwd(&buildDirectory);
    match &libname { &Some(ref l) => { makeCommand.arg(l.as_slice()); }, _ => () };
    let makeProcess = match makeCommand.spawn() {
        Ok(p) => p,
        Err(err) => {
            ecx.span_err(span, format!("{}", err).as_slice());
            return DummyResult::any(span)
        }
    };

    // the content of stderr is converted into compilation errors
    match makeProcess.stderr.clone() {
        Some(mut stderr) => match stderr.read_to_str() {
            Ok(s) => if s.len() >= 1 {
                ecx.span_err(span, s.as_slice());
                return DummyResult::any(span)
            },
            Err(err) => ecx.span_err(span, format!("{}", err).as_slice())
        },
        None => ecx.span_warn(span, "could not open stderr pipe to cmake")
    }

    // outputting the library name
    {
        let link = match &libname {
            &Some(ref l) => format!("#[link_args = \"-L {}\"]\n#[link(name=\"{}\")]\nextern {{}}", outputDir.display().as_maybe_owned().escape_default().as_slice(), l),
            &None => format!("#[link_args = \"-L {}\"]\nextern {{}}", outputDir.display().as_maybe_owned().escape_default().as_slice())
        };
        str_to_item(ecx, link.as_slice())
    }
}

fn str_to_item(ecx: &mut ExtCtxt, content: &str) -> Box<MacResult> {
    let mut parser = ::syntax::parse::new_parser_from_source_str(ecx.parse_sess(), ecx.cfg(), "".to_string(), content.to_string());

    match parser.parse_item_with_outer_attributes() {
        None => fail!("unable to parse library output into items"),
        Some(i) => MacItem::new(i)
    }
}

// converts an Expr into a String if possible
fn expr_to_literal(expr: ::std::gc::Gc<::syntax::ast::Expr>)
    -> Result<String, String>
{
    Ok(match expr.node {
        syntax::ast::ExprLit(lit) => {
            match lit.node {
                syntax::ast::LitStr(ref s, _) => s.to_str(),
                _ => return Err(format!("expected string literal but got `{}`", syntax::print::pprust::lit_to_str(lit)))
            }
        }
        _ => return Err(format!("expected string literal but got `{}`", syntax::print::pprust::expr_to_str(expr)))
    })
}

// tries to obtain the macro arguments from the token tree
fn parse_macro_arguments(cx: &mut ExtCtxt, tts: &[syntax::ast::TokenTree])
    -> Result<(Path, Option<String>), String>
{
    let mut parser = ::syntax::parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), Vec::from_slice(tts));

    let path = match Path::new_opt(try!(expr_to_literal(cx.expand_expr(parser.parse_expr())))) {
        Some(p) => p,
        None => return Err(format!("the path is not utf-8!"))
    };

    let libname = if parser.eat(&token::COMMA) {
        Some(try!(expr_to_literal(cx.expand_expr(parser.parse_expr()))))
    } else { None };

    if !parser.eat(&token::EOF) {
        return Err("too many arguments".to_string());
    }

    Ok((path, libname))
}
<|endoftext|>


<commit_before>#![feature(phase)]

extern crate fuse;
#[phase(plugin)]
extern crate json_macros;
extern crate time;
extern crate libc;
extern crate serialize;

use std::collections::TreeMap;
use std::io::{FileType, USER_FILE, USER_DIR};
use std::os;
use libc::{ENOENT};
use time::Timespec;
use fuse::{FileAttr, Filesystem, Request, ReplyAttr, ReplyData, ReplyEntry, ReplyDirectory};
use serialize::json;

struct JsonFilesystem {
    tree: json::Object,
    attrs: TreeMap<u64, FileAttr>,
    inodes: TreeMap<String, u64>,
}

impl JsonFilesystem {
    fn new(tree: &json::Object) -> JsonFilesystem {
        let mut attrs = TreeMap::new();
        let mut inodes = TreeMap::new();
        let ts = time::now().to_timespec();
        let attr = FileAttr {
            ino: 1,
            size: 0,
            blocks: 0,
            atime: ts,
            mtime: ts,
            ctime: ts,
            crtime: ts,
            kind: FileType::Directory,
            perm: USER_DIR,
            nlink: 0,
            uid: 0,
            gid: 0,
            rdev: 0,
            flags: 0,
        };
        attrs.insert(1, attr);
        inodes.insert("/".to_string(), 1);
        for (i, (key, value)) in tree.iter().enumerate() {
            let attr = FileAttr {
                ino: i as u64 + 2,
                size: value.to_pretty_str().len() as u64,
                blocks: 0,
                atime: ts,
                mtime: ts,
                ctime: ts,
                crtime: ts,
                kind: FileType::RegularFile,
                perm: USER_FILE,
                nlink: 0,
                uid: 0,
                gid: 0,
                rdev: 0,
                flags: 0,
            };
            attrs.insert(attr.ino, attr);
            inodes.insert(key.clone(), attr.ino);
        }
        JsonFilesystem { tree: tree.clone(), attrs: attrs, inodes: inodes }
    }
}

impl Filesystem for JsonFilesystem {
    fn getattr(&mut self, _req: &Request, ino: u64, reply: ReplyAttr) {
        println!("getattr(ino={})", ino);
        match self.attrs.get(&ino) {
            Some(attr) => {
                let ttl = Timespec::new(1, 0);
                reply.attr(&ttl, attr);
            },
            None => reply.error(ENOENT),
        };
    }

    fn lookup(&mut self, _req: &Request, parent: u64, name: &PosixPath, reply: ReplyEntry) {
        println!("lookup(parent={}, name={})", parent, name.display());
        let inode = match self.inodes.get(name.as_str().unwrap()) {
            Some(inode) => inode,
            None => {
                reply.error(ENOENT);
                return;
            },
        };
        match self.attrs.get(inode) {
            Some(attr) => {
                let ttl = Timespec::new(1, 0);
                reply.entry(&ttl, attr, 0);
            },
            None => reply.error(ENOENT),
        };

    }

    fn read(&mut self, _req: &Request, ino: u64, fh: u64, offset: u64, size: uint, reply: ReplyData) {
        println!("read(ino={}, fh={}, offset={}, size={})", ino, fh, offset, size);
        let bytes = "Hello world!\n".as_bytes();
        reply.data(bytes);
    }

    fn readdir(&mut self, _req: &Request, ino: u64, fh: u64, offset: u64, mut reply: ReplyDirectory) {
        println!("readdir(ino={}, fh={}, offset={})", ino, fh, offset);
        if ino == 1 {
            if offset == 0 {
                reply.add(1, 0, FileType::Directory, &PosixPath::new("."));
                reply.add(1, 1, FileType::Directory, &PosixPath::new(".."));
                for (key, &inode) in self.inodes.iter() {
                    if inode == 1 { continue; }
                    let offset = inode; // hack
                    println!("\tkey={}, inode={}, offset={}", key, inode, offset);
                    reply.add(inode, offset, FileType::RegularFile, &PosixPath::new(key));
                }
            }
            reply.ok();
        } else {
            reply.error(ENOENT);
        }
    }
}

fn main() {
    println!("24 days of Rust - fuse (day 15&16)");
    let data = json!({
        "foo": "bar",
        "answer": 42,
    });
    let tree = data.as_object().unwrap();
    let fs = JsonFilesystem::new(tree);
    let mountpoint = match os::args().as_slice() {
        [_, ref path] => Path::new(path),
        _ => {
            println!("Usage: {} <MOUNTPOINT>", os::args()[0]);
            return;
        }
    };
    fuse::mount(fs, &mountpoint, &[]);
}
<commit_msg>Reading for real.<commit_after>#![feature(phase)]

extern crate fuse;
#[phase(plugin)]
extern crate json_macros;
extern crate time;
extern crate libc;
extern crate serialize;

use std::collections::TreeMap;
use std::io::{FileType, USER_FILE, USER_DIR};
use std::os;
use libc::{ENOENT};
use time::Timespec;
use fuse::{FileAttr, Filesystem, Request, ReplyAttr, ReplyData, ReplyEntry, ReplyDirectory};
use serialize::json;

struct JsonFilesystem {
    tree: json::Object,
    attrs: TreeMap<u64, FileAttr>,
    inodes: TreeMap<String, u64>,
}

impl JsonFilesystem {
    fn new(tree: &json::Object) -> JsonFilesystem {
        let mut attrs = TreeMap::new();
        let mut inodes = TreeMap::new();
        let ts = time::now().to_timespec();
        let attr = FileAttr {
            ino: 1,
            size: 0,
            blocks: 0,
            atime: ts,
            mtime: ts,
            ctime: ts,
            crtime: ts,
            kind: FileType::Directory,
            perm: USER_DIR,
            nlink: 0,
            uid: 0,
            gid: 0,
            rdev: 0,
            flags: 0,
        };
        attrs.insert(1, attr);
        inodes.insert("/".to_string(), 1);
        for (i, (key, value)) in tree.iter().enumerate() {
            let attr = FileAttr {
                ino: i as u64 + 2,
                size: value.to_pretty_str().len() as u64,
                blocks: 0,
                atime: ts,
                mtime: ts,
                ctime: ts,
                crtime: ts,
                kind: FileType::RegularFile,
                perm: USER_FILE,
                nlink: 0,
                uid: 0,
                gid: 0,
                rdev: 0,
                flags: 0,
            };
            attrs.insert(attr.ino, attr);
            inodes.insert(key.clone(), attr.ino);
        }
        JsonFilesystem { tree: tree.clone(), attrs: attrs, inodes: inodes }
    }
}

impl Filesystem for JsonFilesystem {
    fn getattr(&mut self, _req: &Request, ino: u64, reply: ReplyAttr) {
        println!("getattr(ino={})", ino);
        match self.attrs.get(&ino) {
            Some(attr) => {
                let ttl = Timespec::new(1, 0);
                reply.attr(&ttl, attr);
            },
            None => reply.error(ENOENT),
        };
    }

    fn lookup(&mut self, _req: &Request, parent: u64, name: &PosixPath, reply: ReplyEntry) {
        println!("lookup(parent={}, name={})", parent, name.display());
        let inode = match self.inodes.get(name.as_str().unwrap()) {
            Some(inode) => inode,
            None => {
                reply.error(ENOENT);
                return;
            },
        };
        match self.attrs.get(inode) {
            Some(attr) => {
                let ttl = Timespec::new(1, 0);
                reply.entry(&ttl, attr, 0);
            },
            None => reply.error(ENOENT),
        };

    }

    fn read(&mut self, _req: &Request, ino: u64, fh: u64, offset: u64, size: uint, reply: ReplyData) {
        println!("read(ino={}, fh={}, offset={}, size={})", ino, fh, offset, size);
        for (key, &inode) in self.inodes.iter() {
            if inode == ino {
                let value = self.tree.get(key).unwrap();
                reply.data(value.to_pretty_str().as_bytes());
                return;
            }
        }
        reply.error(ENOENT);
    }

    fn readdir(&mut self, _req: &Request, ino: u64, fh: u64, offset: u64, mut reply: ReplyDirectory) {
        println!("readdir(ino={}, fh={}, offset={})", ino, fh, offset);
        if ino == 1 {
            if offset == 0 {
                reply.add(1, 0, FileType::Directory, &PosixPath::new("."));
                reply.add(1, 1, FileType::Directory, &PosixPath::new(".."));
                for (key, &inode) in self.inodes.iter() {
                    if inode == 1 { continue; }
                    let offset = inode; // hack
                    println!("\tkey={}, inode={}, offset={}", key, inode, offset);
                    reply.add(inode, offset, FileType::RegularFile, &PosixPath::new(key));
                }
            }
            reply.ok();
        } else {
            reply.error(ENOENT);
        }
    }
}

fn main() {
    println!("24 days of Rust - fuse (day 15&16)");
    let data = json!({
        "foo": "bar",
        "answer": 42,
    });
    let tree = data.as_object().unwrap();
    let fs = JsonFilesystem::new(tree);
    let mountpoint = match os::args().as_slice() {
        [_, ref path] => Path::new(path),
        _ => {
            println!("Usage: {} <MOUNTPOINT>", os::args()[0]);
            return;
        }
    };
    fuse::mount(fs, &mountpoint, &[]);
}
<|endoftext|>


<commit_before>extern crate byteorder;
extern crate gimli;

use gimli::{AttributeValue, DebugAbbrev, DebugAranges, DebugInfo, DebugLine, DebugPubNames,
            DebugPubTypes, DW_AT_stmt_list, LineNumberProgramHeader, LittleEndian, StateMachine};
use std::env;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;

fn read_section(section: &str) -> Vec<u8> {
    let mut path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
    path.push("./fixtures/self/");
    path.push(section);

    println!("Reading section \"{}\" at path {:?}", section, path);
    assert!(path.is_file());
    let mut file = File::open(path).unwrap();

    let mut buf = Vec::new();
    file.read_to_end(&mut buf).unwrap();
    buf
}

#[test]
fn test_parse_self_debug_info() {
    let debug_info = read_section("debug_info");
    let debug_info = DebugInfo::<LittleEndian>::new(&debug_info);

    let debug_abbrev = read_section("debug_abbrev");
    let debug_abbrev = DebugAbbrev::<LittleEndian>::new(&debug_abbrev);

    let mut iter = debug_info.units();
    while let Some(unit) = iter.next().expect("Should parse compilation unit") {
        let abbrevs = unit.abbreviations(debug_abbrev)
            .expect("Should parse abbreviations");

        let mut cursor = unit.entries(&abbrevs);

        while cursor.next_dfs().expect("Should parse next dfs").is_some() {
            let entry = cursor.current().expect("Should have a current entry");

            let mut attrs = entry.attrs();
            while let Some(_) = attrs.next().expect("Should parse entry's attribute") {
            }
        }
    }
}

#[test]
fn test_parse_self_debug_line() {
    let debug_info = read_section("debug_info");
    let debug_info = DebugInfo::<LittleEndian>::new(&debug_info);

    let debug_abbrev = read_section("debug_abbrev");
    let debug_abbrev = DebugAbbrev::<LittleEndian>::new(&debug_abbrev);

    let debug_line = read_section("debug_line");
    let debug_line = DebugLine::<LittleEndian>::new(&debug_line);

    let mut iter = debug_info.units();
    while let Some(unit) = iter.next().expect("Should parse compilation unit") {
        let abbrevs = unit.abbreviations(debug_abbrev)
            .expect("Should parse abbreviations");

        let mut cursor = unit.entries(&abbrevs);
        cursor.next_dfs().expect("Should parse next dfs");

        let unit_entry = cursor.current()
            .expect("Should have a root entry");

        if let Some(AttributeValue::DebugLineRef(offset)) = unit_entry.attr_value(DW_AT_stmt_list) {
            let header = LineNumberProgramHeader::new(debug_line, offset, unit.address_size())
                .expect("should parse line number program header");

            let mut state_machine = StateMachine::new(header);
            while let Some(_) = state_machine.next_row()
                .expect("Should parse and execute all rows in the line number program") {
            }
        }
    }
}

#[test]
fn test_parse_self_debug_aranges() {
    let debug_aranges = read_section("debug_aranges");
    let debug_aranges = DebugAranges::<LittleEndian>::new(&debug_aranges);

    let mut aranges = debug_aranges.items();
    while let Some(_) = aranges.next().expect("Should parse arange OK") {
        // Not really anything else we can check right now.
    }
}

#[test]
fn test_parse_self_debug_pubnames() {
    let debug_pubnames = read_section("debug_pubnames");
    let debug_pubnames = DebugPubNames::<LittleEndian>::new(&debug_pubnames);

    let mut pubnames = debug_pubnames.items();
    while let Some(_) = pubnames.next().expect("Should parse pubname OK") {
        // Not really anything else we can check right now.
    }
}

#[test]
fn test_parse_self_debug_pubtypes() {
    let debug_pubtypes = read_section("debug_pubtypes");
    let debug_pubtypes = DebugPubTypes::<LittleEndian>::new(&debug_pubtypes);

    let mut pubtypes = debug_pubtypes.items();
    while let Some(_) = pubtypes.next().expect("Should parse pubtype OK") {
        // Not really anything else we can check right now.
    }
}
<commit_msg>Delete unneeded `extern crate byteorder`<commit_after>extern crate gimli;

use gimli::{AttributeValue, DebugAbbrev, DebugAranges, DebugInfo, DebugLine, DebugPubNames,
            DebugPubTypes, DW_AT_stmt_list, LineNumberProgramHeader, LittleEndian, StateMachine};
use std::env;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;

fn read_section(section: &str) -> Vec<u8> {
    let mut path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
    path.push("./fixtures/self/");
    path.push(section);

    println!("Reading section \"{}\" at path {:?}", section, path);
    assert!(path.is_file());
    let mut file = File::open(path).unwrap();

    let mut buf = Vec::new();
    file.read_to_end(&mut buf).unwrap();
    buf
}

#[test]
fn test_parse_self_debug_info() {
    let debug_info = read_section("debug_info");
    let debug_info = DebugInfo::<LittleEndian>::new(&debug_info);

    let debug_abbrev = read_section("debug_abbrev");
    let debug_abbrev = DebugAbbrev::<LittleEndian>::new(&debug_abbrev);

    let mut iter = debug_info.units();
    while let Some(unit) = iter.next().expect("Should parse compilation unit") {
        let abbrevs = unit.abbreviations(debug_abbrev)
            .expect("Should parse abbreviations");

        let mut cursor = unit.entries(&abbrevs);

        while cursor.next_dfs().expect("Should parse next dfs").is_some() {
            let entry = cursor.current().expect("Should have a current entry");

            let mut attrs = entry.attrs();
            while let Some(_) = attrs.next().expect("Should parse entry's attribute") {
            }
        }
    }
}

#[test]
fn test_parse_self_debug_line() {
    let debug_info = read_section("debug_info");
    let debug_info = DebugInfo::<LittleEndian>::new(&debug_info);

    let debug_abbrev = read_section("debug_abbrev");
    let debug_abbrev = DebugAbbrev::<LittleEndian>::new(&debug_abbrev);

    let debug_line = read_section("debug_line");
    let debug_line = DebugLine::<LittleEndian>::new(&debug_line);

    let mut iter = debug_info.units();
    while let Some(unit) = iter.next().expect("Should parse compilation unit") {
        let abbrevs = unit.abbreviations(debug_abbrev)
            .expect("Should parse abbreviations");

        let mut cursor = unit.entries(&abbrevs);
        cursor.next_dfs().expect("Should parse next dfs");

        let unit_entry = cursor.current()
            .expect("Should have a root entry");

        if let Some(AttributeValue::DebugLineRef(offset)) = unit_entry.attr_value(DW_AT_stmt_list) {
            let header = LineNumberProgramHeader::new(debug_line, offset, unit.address_size())
                .expect("should parse line number program header");

            let mut state_machine = StateMachine::new(header);
            while let Some(_) = state_machine.next_row()
                .expect("Should parse and execute all rows in the line number program") {
            }
        }
    }
}

#[test]
fn test_parse_self_debug_aranges() {
    let debug_aranges = read_section("debug_aranges");
    let debug_aranges = DebugAranges::<LittleEndian>::new(&debug_aranges);

    let mut aranges = debug_aranges.items();
    while let Some(_) = aranges.next().expect("Should parse arange OK") {
        // Not really anything else we can check right now.
    }
}

#[test]
fn test_parse_self_debug_pubnames() {
    let debug_pubnames = read_section("debug_pubnames");
    let debug_pubnames = DebugPubNames::<LittleEndian>::new(&debug_pubnames);

    let mut pubnames = debug_pubnames.items();
    while let Some(_) = pubnames.next().expect("Should parse pubname OK") {
        // Not really anything else we can check right now.
    }
}

#[test]
fn test_parse_self_debug_pubtypes() {
    let debug_pubtypes = read_section("debug_pubtypes");
    let debug_pubtypes = DebugPubTypes::<LittleEndian>::new(&debug_pubtypes);

    let mut pubtypes = debug_pubtypes.items();
    while let Some(_) = pubtypes.next().expect("Should parse pubtype OK") {
        // Not really anything else we can check right now.
    }
}
<|endoftext|>