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

use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::BlobBinding;
use dom::bindings::codegen::Bindings::BlobBinding::BlobMethods;
use dom::bindings::codegen::UnionTypes::BlobOrString;
use dom::bindings::error::{Error, Fallible};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, Root};
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::str::DOMString;
use encoding::all::UTF_8;
use encoding::types::{EncoderTrap, Encoding};
use ipc_channel::ipc;
use net_traits::blob_url_store::{BlobBuf, get_blob_origin};
use net_traits::filemanager_thread::{FileManagerThreadMsg, SelectedFileId, RelativePos, ReadFileProgress};
use net_traits::{CoreResourceMsg, IpcSend};
use std::cell::Cell;
use std::mem;
use std::ops::Index;
use std::path::PathBuf;
use uuid::Uuid;

/// File-based blob
#[derive(JSTraceable)]
pub struct FileBlob {
    id: SelectedFileId,
    name: Option<PathBuf>,
    cache: DOMRefCell<Option<Vec<u8>>>,
    size: u64,
}

/// Blob backend implementation
#[must_root]
#[derive(JSTraceable)]
pub enum BlobImpl {
    /// File-based blob
    File(FileBlob),
    /// Memory-based blob
    Memory(Vec<u8>),
    /// Sliced blob, including parent blob and
    /// relative positions representing current slicing range,
    /// it is leaf of a two-layer fat tree
    Sliced(JS<Blob>, RelativePos),
}

impl BlobImpl {
    /// Construct memory-backed BlobImpl
    #[allow(unrooted_must_root)]
    pub fn new_from_bytes(bytes: Vec<u8>) -> BlobImpl {
        BlobImpl::Memory(bytes)
    }

    /// Construct file-backed BlobImpl from File ID
    pub fn new_from_file(file_id: SelectedFileId, name: PathBuf, size: u64) -> BlobImpl {
        BlobImpl::File(FileBlob {
            id: file_id,
            name: Some(name),
            cache: DOMRefCell::new(None),
            size: size,
        })
    }
}

// https://w3c.github.io/FileAPI/#blob
#[dom_struct]
pub struct Blob {
    reflector_: Reflector,
    #[ignore_heap_size_of = "No clear owner"]
    blob_impl: DOMRefCell<BlobImpl>,
    typeString: String,
    isClosed_: Cell<bool>,
}

impl Blob {
    #[allow(unrooted_must_root)]
    pub fn new(global: GlobalRef, blob_impl: BlobImpl, typeString: String) -> Root<Blob> {
        let boxed_blob = box Blob::new_inherited(blob_impl, typeString);
        reflect_dom_object(boxed_blob, global, BlobBinding::Wrap)
    }

    #[allow(unrooted_must_root)]
    pub fn new_inherited(blob_impl: BlobImpl, typeString: String) -> Blob {
        Blob {
            reflector_: Reflector::new(),
            blob_impl: DOMRefCell::new(blob_impl),
            // NOTE: Guarding the format correctness here,
            // https://w3c.github.io/FileAPI/#dfn-type
            typeString: normalize_type_string(&typeString),
            isClosed_: Cell::new(false),
        }
    }

    #[allow(unrooted_must_root)]
    fn new_sliced(parent: &Blob, rel_pos: RelativePos,
                  relativeContentType: DOMString) -> Root<Blob> {
        let global = parent.global();
        let blob_impl = match *parent.blob_impl.borrow() {
            BlobImpl::File(_) => {
                // Create new parent node
                BlobImpl::Sliced(JS::from_ref(parent), rel_pos)
            }
            BlobImpl::Memory(_) => {
                // Create new parent node
                BlobImpl::Sliced(JS::from_ref(parent), rel_pos)
            }
            BlobImpl::Sliced(ref grandparent, ref old_rel_pos) => {
                // Adjust the slicing position, using same parent
                BlobImpl::Sliced(grandparent.clone(), old_rel_pos.slice_inner(&rel_pos))
            }
        };

        Blob::new(global.r(), blob_impl, relativeContentType.into())
    }

    // https://w3c.github.io/FileAPI/#constructorBlob
    pub fn Constructor(global: GlobalRef,
                       blobParts: Option<Vec<BlobOrString>>,
                       blobPropertyBag: &BlobBinding::BlobPropertyBag)
                       -> Fallible<Root<Blob>> {
        // TODO: accept other blobParts types - ArrayBuffer or ArrayBufferView
        let bytes: Vec<u8> = match blobParts {
            None => Vec::new(),
            Some(blobparts) => match blob_parts_to_bytes(blobparts) {
                Ok(bytes) => bytes,
                Err(_) => return Err(Error::InvalidCharacter),
            }
        };

        Ok(Blob::new(global, BlobImpl::new_from_bytes(bytes), blobPropertyBag.type_.to_string()))
    }

    /// Get a slice to inner data, this might incur synchronous read and caching
    pub fn get_bytes(&self) -> Result<Vec<u8>, ()> {
        match *self.blob_impl.borrow() {
            BlobImpl::File(ref f) => {
                let (buffer, is_new_buffer) = match *f.cache.borrow() {
                    Some(ref bytes) => (bytes.clone(), false),
                    None => {
                        let global = self.global();
                        let bytes = read_file(global.r(), f.id.clone())?;
                        (bytes, true)
                    }
                };

                // Cache
                if is_new_buffer {
                    *f.cache.borrow_mut() = Some(buffer.clone());
                }

                Ok(buffer)
            }
            BlobImpl::Memory(ref s) => Ok(s.clone()),
            BlobImpl::Sliced(ref parent, ref rel_pos) => {
                parent.get_bytes().map(|v| {
                    let range = rel_pos.to_abs_range(v.len());
                    v.index(range).to_vec()
                })
            }
        }
    }

    /// Get a FileID representing the Blob content,
    /// used by URL.createObjectURL
    pub fn get_blob_url_id(&self) -> SelectedFileId {
        let opt_sliced_parent = match *self.blob_impl.borrow() {
            BlobImpl::Sliced(ref parent, ref rel_pos) => {
                Some((parent.promote(/* set_valid is */ false), rel_pos.clone(), parent.Size()))
            }
            _ => None
        };

        match opt_sliced_parent {
            Some((parent_id, rel_pos, size)) => self.create_sliced_url_id(&parent_id, &rel_pos, size),
            None => self.promote(/* set_valid is */ true),
        }
    }

    /// Promote non-Slice blob:
    /// 1. Memory-based: The bytes in data slice will be transferred to file manager thread.
    /// 2. File-based: Activation
    /// Depending on set_valid, the returned FileID can be part of
    /// valid or invalid Blob URL.
    fn promote(&self, set_valid: bool) -> SelectedFileId {
        let mut bytes = vec![];

        match *self.blob_impl.borrow_mut() {
            BlobImpl::Sliced(_, _) => {
                debug!("Sliced can't have a sliced parent");
                // Return dummy id
                return SelectedFileId(Uuid::new_v4().simple().to_string());
            }
            BlobImpl::File(ref f) => {
                if set_valid {
                    let global = self.global();
                    let origin = get_blob_origin(&global.r().get_url());
                    let (tx, rx) = ipc::channel().unwrap();

                    let msg = FileManagerThreadMsg::ActivateBlobURL(f.id.clone(), tx, origin.clone());
                    self.send_to_file_manager(msg);

                    match rx.recv().unwrap() {
                        Ok(_) => return f.id.clone(),
                        // Return a dummy id on error
                        Err(_) => return SelectedFileId(Uuid::new_v4().simple().to_string())
                    }
                } else {
                    // no need to activate
                    return f.id.clone();
                }
            }
            BlobImpl::Memory(ref mut bytes_in) => mem::swap(bytes_in, &mut bytes),
        };

        let global = self.global();
        let origin = get_blob_origin(&global.r().get_url());

        let blob_buf = BlobBuf {
            filename: None,
            type_string: self.typeString.clone(),
            size: bytes.len() as u64,
            bytes: bytes.to_vec(),
        };

        let (tx, rx) = ipc::channel().unwrap();
        let msg = FileManagerThreadMsg::PromoteMemory(blob_buf, set_valid, tx, origin.clone());
        self.send_to_file_manager(msg);

        match rx.recv().unwrap() {
            Ok(id) => {
                let id = SelectedFileId(id.0);
                *self.blob_impl.borrow_mut() = BlobImpl::File(FileBlob {
                    id: id.clone(),
                    name: None,
                    cache: DOMRefCell::new(Some(bytes.to_vec())),
                    size: bytes.len() as u64,
                });
                id
            }
            // Dummy id
            Err(_) => SelectedFileId(Uuid::new_v4().simple().to_string()),
        }
    }

    /// Get a FileID representing sliced parent-blob content
    fn create_sliced_url_id(&self, parent_id: &SelectedFileId,
                            rel_pos: &RelativePos, parent_len: u64) -> SelectedFileId {
        let global = self.global();

        let origin = get_blob_origin(&global.r().get_url());

        let (tx, rx) = ipc::channel().unwrap();
        let msg = FileManagerThreadMsg::AddSlicedURLEntry(parent_id.clone(),
                                                          rel_pos.clone(),
                                                          tx, origin.clone());
        self.send_to_file_manager(msg);
        match rx.recv().expect("File manager thread is down") {
            Ok(new_id) => {
                let new_id = SelectedFileId(new_id.0);

                *self.blob_impl.borrow_mut() = BlobImpl::File(FileBlob {
                    id: new_id.clone(),
                    name: None,
                    cache: DOMRefCell::new(None),
                    size: rel_pos.to_abs_range(parent_len as usize).len() as u64,
                });

                // Return the indirect id reference
                new_id
            }
            Err(_) => {
                // Return dummy id
                SelectedFileId(Uuid::new_v4().simple().to_string())
            }
        }
    }

    /// Cleanups at the time of destruction/closing
    fn clean_up_file_resource(&self) {
        if let BlobImpl::File(ref f) = *self.blob_impl.borrow() {
            let global = self.global();
            let origin = get_blob_origin(&global.r().get_url());

            let (tx, rx) = ipc::channel().unwrap();

            let msg = FileManagerThreadMsg::DecRef(f.id.clone(), origin, tx);
            self.send_to_file_manager(msg);
            let _ = rx.recv().unwrap();
        }
    }

    fn send_to_file_manager(&self, msg: FileManagerThreadMsg) {
        let global = self.global();
        let resource_threads = global.r().resource_threads();
        let _ = resource_threads.send(CoreResourceMsg::ToFileManager(msg));
    }
}

impl Drop for Blob {
    fn drop(&mut self) {
        if !self.IsClosed() {
            self.clean_up_file_resource();
        }
    }
}

fn read_file(global: GlobalRef, id: SelectedFileId) -> Result<Vec<u8>, ()> {
    let resource_threads = global.resource_threads();
    let (chan, recv) = ipc::channel().map_err(|_|())?;
    let origin = get_blob_origin(&global.get_url());
    let check_url_validity = false;
    let msg = FileManagerThreadMsg::ReadFile(chan, id, check_url_validity, origin);
    let _ = resource_threads.send(CoreResourceMsg::ToFileManager(msg));

    let mut bytes = vec![];

    loop {
        match recv.recv().unwrap() {
            Ok(ReadFileProgress::Meta(mut blob_buf)) => {
                bytes.append(&mut blob_buf.bytes);
            }
            Ok(ReadFileProgress::Partial(mut bytes_in)) => {
                bytes.append(&mut bytes_in);
            }
            Ok(ReadFileProgress::EOF) => {
                return Ok(bytes);
            }
            Err(_) => return Err(()),
        }
    }
}

/// Extract bytes from BlobParts, used by Blob and File constructor
/// https://w3c.github.io/FileAPI/#constructorBlob
pub fn blob_parts_to_bytes(blobparts: Vec<BlobOrString>) -> Result<Vec<u8>, ()> {
    let mut ret = vec![];

    for blobpart in &blobparts {
        match blobpart {
            &BlobOrString::String(ref s) => {
                let mut bytes = UTF_8.encode(s, EncoderTrap::Replace).map_err(|_|())?;
                ret.append(&mut bytes);
            },
            &BlobOrString::Blob(ref b) => {
                let mut bytes = b.get_bytes().unwrap_or(vec![]);
                ret.append(&mut bytes);
            },
        }
    }

    Ok(ret)
}

impl BlobMethods for Blob {
    // https://w3c.github.io/FileAPI/#dfn-size
    fn Size(&self) -> u64 {
        // XXX: This will incur reading if file-based
        match self.get_bytes() {
            Ok(s) => s.len() as u64,
            _ => 0,
        }
    }

    // https://w3c.github.io/FileAPI/#dfn-type
    fn Type(&self) -> DOMString {
        DOMString::from(self.typeString.clone())
    }

    // https://w3c.github.io/FileAPI/#slice-method-algo
    fn Slice(&self,
             start: Option<i64>,
             end: Option<i64>,
             contentType: Option<DOMString>)
             -> Root<Blob> {
        let rel_pos = RelativePos::from_opts(start, end);
        Blob::new_sliced(self, rel_pos, contentType.unwrap_or(DOMString::from("")))
    }

    // https://w3c.github.io/FileAPI/#dfn-isClosed
    fn IsClosed(&self) -> bool {
        self.isClosed_.get()
    }

    // https://w3c.github.io/FileAPI/#dfn-close
    fn Close(&self) {
        // Step 1
        if self.isClosed_.get() {
            return;
        }

        // Step 2
        self.isClosed_.set(true);

        // Step 3
        self.clean_up_file_resource();
    }
}

/// Get the normalized, MIME-parsable type string
/// https://w3c.github.io/FileAPI/#dfn-type
/// XXX: We will relax the restriction here,
/// since the spec has some problem over this part.
/// see https://github.com/w3c/FileAPI/issues/43
fn normalize_type_string(s: &str) -> String {
    if is_ascii_printable(s) {
        let s_lower = s.to_lowercase();
        // match s_lower.parse() as Result<Mime, ()> {
            // Ok(_) => s_lower,
            // Err(_) => "".to_string()
        s_lower
    } else {
        "".to_string()
    }
}

fn is_ascii_printable(string: &str) -> bool {
    // Step 5.1 in Sec 5.1 of File API spec
    // https://w3c.github.io/FileAPI/#constructorBlob
    string.chars().all(|c| c >= '\x20' && c <= '\x7E')
}
<commit_msg>Improve Blob.Size() speed by avoid reading content<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::BlobBinding;
use dom::bindings::codegen::Bindings::BlobBinding::BlobMethods;
use dom::bindings::codegen::UnionTypes::BlobOrString;
use dom::bindings::error::{Error, Fallible};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, Root};
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::str::DOMString;
use encoding::all::UTF_8;
use encoding::types::{EncoderTrap, Encoding};
use ipc_channel::ipc;
use net_traits::blob_url_store::{BlobBuf, get_blob_origin};
use net_traits::filemanager_thread::{FileManagerThreadMsg, SelectedFileId, RelativePos, ReadFileProgress};
use net_traits::{CoreResourceMsg, IpcSend};
use std::cell::Cell;
use std::mem;
use std::ops::Index;
use std::path::PathBuf;
use uuid::Uuid;

/// File-based blob
#[derive(JSTraceable)]
pub struct FileBlob {
    id: SelectedFileId,
    name: Option<PathBuf>,
    cache: DOMRefCell<Option<Vec<u8>>>,
    size: u64,
}

/// Blob backend implementation
#[must_root]
#[derive(JSTraceable)]
pub enum BlobImpl {
    /// File-based blob
    File(FileBlob),
    /// Memory-based blob
    Memory(Vec<u8>),
    /// Sliced blob, including parent blob and
    /// relative positions representing current slicing range,
    /// it is leaf of a two-layer fat tree
    Sliced(JS<Blob>, RelativePos),
}

impl BlobImpl {
    /// Construct memory-backed BlobImpl
    #[allow(unrooted_must_root)]
    pub fn new_from_bytes(bytes: Vec<u8>) -> BlobImpl {
        BlobImpl::Memory(bytes)
    }

    /// Construct file-backed BlobImpl from File ID
    pub fn new_from_file(file_id: SelectedFileId, name: PathBuf, size: u64) -> BlobImpl {
        BlobImpl::File(FileBlob {
            id: file_id,
            name: Some(name),
            cache: DOMRefCell::new(None),
            size: size,
        })
    }
}

// https://w3c.github.io/FileAPI/#blob
#[dom_struct]
pub struct Blob {
    reflector_: Reflector,
    #[ignore_heap_size_of = "No clear owner"]
    blob_impl: DOMRefCell<BlobImpl>,
    typeString: String,
    isClosed_: Cell<bool>,
}

impl Blob {
    #[allow(unrooted_must_root)]
    pub fn new(global: GlobalRef, blob_impl: BlobImpl, typeString: String) -> Root<Blob> {
        let boxed_blob = box Blob::new_inherited(blob_impl, typeString);
        reflect_dom_object(boxed_blob, global, BlobBinding::Wrap)
    }

    #[allow(unrooted_must_root)]
    pub fn new_inherited(blob_impl: BlobImpl, typeString: String) -> Blob {
        Blob {
            reflector_: Reflector::new(),
            blob_impl: DOMRefCell::new(blob_impl),
            // NOTE: Guarding the format correctness here,
            // https://w3c.github.io/FileAPI/#dfn-type
            typeString: normalize_type_string(&typeString),
            isClosed_: Cell::new(false),
        }
    }

    #[allow(unrooted_must_root)]
    fn new_sliced(parent: &Blob, rel_pos: RelativePos,
                  relativeContentType: DOMString) -> Root<Blob> {
        let global = parent.global();
        let blob_impl = match *parent.blob_impl.borrow() {
            BlobImpl::File(_) => {
                // Create new parent node
                BlobImpl::Sliced(JS::from_ref(parent), rel_pos)
            }
            BlobImpl::Memory(_) => {
                // Create new parent node
                BlobImpl::Sliced(JS::from_ref(parent), rel_pos)
            }
            BlobImpl::Sliced(ref grandparent, ref old_rel_pos) => {
                // Adjust the slicing position, using same parent
                BlobImpl::Sliced(grandparent.clone(), old_rel_pos.slice_inner(&rel_pos))
            }
        };

        Blob::new(global.r(), blob_impl, relativeContentType.into())
    }

    // https://w3c.github.io/FileAPI/#constructorBlob
    pub fn Constructor(global: GlobalRef,
                       blobParts: Option<Vec<BlobOrString>>,
                       blobPropertyBag: &BlobBinding::BlobPropertyBag)
                       -> Fallible<Root<Blob>> {
        // TODO: accept other blobParts types - ArrayBuffer or ArrayBufferView
        let bytes: Vec<u8> = match blobParts {
            None => Vec::new(),
            Some(blobparts) => match blob_parts_to_bytes(blobparts) {
                Ok(bytes) => bytes,
                Err(_) => return Err(Error::InvalidCharacter),
            }
        };

        Ok(Blob::new(global, BlobImpl::new_from_bytes(bytes), blobPropertyBag.type_.to_string()))
    }

    /// Get a slice to inner data, this might incur synchronous read and caching
    pub fn get_bytes(&self) -> Result<Vec<u8>, ()> {
        match *self.blob_impl.borrow() {
            BlobImpl::File(ref f) => {
                let (buffer, is_new_buffer) = match *f.cache.borrow() {
                    Some(ref bytes) => (bytes.clone(), false),
                    None => {
                        let global = self.global();
                        let bytes = read_file(global.r(), f.id.clone())?;
                        (bytes, true)
                    }
                };

                // Cache
                if is_new_buffer {
                    *f.cache.borrow_mut() = Some(buffer.clone());
                }

                Ok(buffer)
            }
            BlobImpl::Memory(ref s) => Ok(s.clone()),
            BlobImpl::Sliced(ref parent, ref rel_pos) => {
                parent.get_bytes().map(|v| {
                    let range = rel_pos.to_abs_range(v.len());
                    v.index(range).to_vec()
                })
            }
        }
    }

    /// Get a FileID representing the Blob content,
    /// used by URL.createObjectURL
    pub fn get_blob_url_id(&self) -> SelectedFileId {
        let opt_sliced_parent = match *self.blob_impl.borrow() {
            BlobImpl::Sliced(ref parent, ref rel_pos) => {
                Some((parent.promote(/* set_valid is */ false), rel_pos.clone(), parent.Size()))
            }
            _ => None
        };

        match opt_sliced_parent {
            Some((parent_id, rel_pos, size)) => self.create_sliced_url_id(&parent_id, &rel_pos, size),
            None => self.promote(/* set_valid is */ true),
        }
    }

    /// Promote non-Slice blob:
    /// 1. Memory-based: The bytes in data slice will be transferred to file manager thread.
    /// 2. File-based: Activation
    /// Depending on set_valid, the returned FileID can be part of
    /// valid or invalid Blob URL.
    fn promote(&self, set_valid: bool) -> SelectedFileId {
        let mut bytes = vec![];

        match *self.blob_impl.borrow_mut() {
            BlobImpl::Sliced(_, _) => {
                debug!("Sliced can't have a sliced parent");
                // Return dummy id
                return SelectedFileId(Uuid::new_v4().simple().to_string());
            }
            BlobImpl::File(ref f) => {
                if set_valid {
                    let global = self.global();
                    let origin = get_blob_origin(&global.r().get_url());
                    let (tx, rx) = ipc::channel().unwrap();

                    let msg = FileManagerThreadMsg::ActivateBlobURL(f.id.clone(), tx, origin.clone());
                    self.send_to_file_manager(msg);

                    match rx.recv().unwrap() {
                        Ok(_) => return f.id.clone(),
                        // Return a dummy id on error
                        Err(_) => return SelectedFileId(Uuid::new_v4().simple().to_string())
                    }
                } else {
                    // no need to activate
                    return f.id.clone();
                }
            }
            BlobImpl::Memory(ref mut bytes_in) => mem::swap(bytes_in, &mut bytes),
        };

        let global = self.global();
        let origin = get_blob_origin(&global.r().get_url());

        let blob_buf = BlobBuf {
            filename: None,
            type_string: self.typeString.clone(),
            size: bytes.len() as u64,
            bytes: bytes.to_vec(),
        };

        let (tx, rx) = ipc::channel().unwrap();
        let msg = FileManagerThreadMsg::PromoteMemory(blob_buf, set_valid, tx, origin.clone());
        self.send_to_file_manager(msg);

        match rx.recv().unwrap() {
            Ok(id) => {
                let id = SelectedFileId(id.0);
                *self.blob_impl.borrow_mut() = BlobImpl::File(FileBlob {
                    id: id.clone(),
                    name: None,
                    cache: DOMRefCell::new(Some(bytes.to_vec())),
                    size: bytes.len() as u64,
                });
                id
            }
            // Dummy id
            Err(_) => SelectedFileId(Uuid::new_v4().simple().to_string()),
        }
    }

    /// Get a FileID representing sliced parent-blob content
    fn create_sliced_url_id(&self, parent_id: &SelectedFileId,
                            rel_pos: &RelativePos, parent_len: u64) -> SelectedFileId {
        let global = self.global();

        let origin = get_blob_origin(&global.r().get_url());

        let (tx, rx) = ipc::channel().unwrap();
        let msg = FileManagerThreadMsg::AddSlicedURLEntry(parent_id.clone(),
                                                          rel_pos.clone(),
                                                          tx, origin.clone());
        self.send_to_file_manager(msg);
        match rx.recv().expect("File manager thread is down") {
            Ok(new_id) => {
                let new_id = SelectedFileId(new_id.0);

                *self.blob_impl.borrow_mut() = BlobImpl::File(FileBlob {
                    id: new_id.clone(),
                    name: None,
                    cache: DOMRefCell::new(None),
                    size: rel_pos.to_abs_range(parent_len as usize).len() as u64,
                });

                // Return the indirect id reference
                new_id
            }
            Err(_) => {
                // Return dummy id
                SelectedFileId(Uuid::new_v4().simple().to_string())
            }
        }
    }

    /// Cleanups at the time of destruction/closing
    fn clean_up_file_resource(&self) {
        if let BlobImpl::File(ref f) = *self.blob_impl.borrow() {
            let global = self.global();
            let origin = get_blob_origin(&global.r().get_url());

            let (tx, rx) = ipc::channel().unwrap();

            let msg = FileManagerThreadMsg::DecRef(f.id.clone(), origin, tx);
            self.send_to_file_manager(msg);
            let _ = rx.recv().unwrap();
        }
    }

    fn send_to_file_manager(&self, msg: FileManagerThreadMsg) {
        let global = self.global();
        let resource_threads = global.r().resource_threads();
        let _ = resource_threads.send(CoreResourceMsg::ToFileManager(msg));
    }
}

impl Drop for Blob {
    fn drop(&mut self) {
        if !self.IsClosed() {
            self.clean_up_file_resource();
        }
    }
}

fn read_file(global: GlobalRef, id: SelectedFileId) -> Result<Vec<u8>, ()> {
    let resource_threads = global.resource_threads();
    let (chan, recv) = ipc::channel().map_err(|_|())?;
    let origin = get_blob_origin(&global.get_url());
    let check_url_validity = false;
    let msg = FileManagerThreadMsg::ReadFile(chan, id, check_url_validity, origin);
    let _ = resource_threads.send(CoreResourceMsg::ToFileManager(msg));

    let mut bytes = vec![];

    loop {
        match recv.recv().unwrap() {
            Ok(ReadFileProgress::Meta(mut blob_buf)) => {
                bytes.append(&mut blob_buf.bytes);
            }
            Ok(ReadFileProgress::Partial(mut bytes_in)) => {
                bytes.append(&mut bytes_in);
            }
            Ok(ReadFileProgress::EOF) => {
                return Ok(bytes);
            }
            Err(_) => return Err(()),
        }
    }
}

/// Extract bytes from BlobParts, used by Blob and File constructor
/// https://w3c.github.io/FileAPI/#constructorBlob
pub fn blob_parts_to_bytes(blobparts: Vec<BlobOrString>) -> Result<Vec<u8>, ()> {
    let mut ret = vec![];

    for blobpart in &blobparts {
        match blobpart {
            &BlobOrString::String(ref s) => {
                let mut bytes = UTF_8.encode(s, EncoderTrap::Replace).map_err(|_|())?;
                ret.append(&mut bytes);
            },
            &BlobOrString::Blob(ref b) => {
                let mut bytes = b.get_bytes().unwrap_or(vec![]);
                ret.append(&mut bytes);
            },
        }
    }

    Ok(ret)
}

impl BlobMethods for Blob {
    // https://w3c.github.io/FileAPI/#dfn-size
    fn Size(&self) -> u64 {
         match *self.blob_impl.borrow() {
            BlobImpl::File(ref f) => f.size,
            BlobImpl::Memory(ref v) => v.len() as u64,
            BlobImpl::Sliced(ref parent, ref rel_pos) =>
                rel_pos.to_abs_range(parent.Size() as usize).len() as u64,
         }
    }

    // https://w3c.github.io/FileAPI/#dfn-type
    fn Type(&self) -> DOMString {
        DOMString::from(self.typeString.clone())
    }

    // https://w3c.github.io/FileAPI/#slice-method-algo
    fn Slice(&self,
             start: Option<i64>,
             end: Option<i64>,
             contentType: Option<DOMString>)
             -> Root<Blob> {
        let rel_pos = RelativePos::from_opts(start, end);
        Blob::new_sliced(self, rel_pos, contentType.unwrap_or(DOMString::from("")))
    }

    // https://w3c.github.io/FileAPI/#dfn-isClosed
    fn IsClosed(&self) -> bool {
        self.isClosed_.get()
    }

    // https://w3c.github.io/FileAPI/#dfn-close
    fn Close(&self) {
        // Step 1
        if self.isClosed_.get() {
            return;
        }

        // Step 2
        self.isClosed_.set(true);

        // Step 3
        self.clean_up_file_resource();
    }
}

/// Get the normalized, MIME-parsable type string
/// https://w3c.github.io/FileAPI/#dfn-type
/// XXX: We will relax the restriction here,
/// since the spec has some problem over this part.
/// see https://github.com/w3c/FileAPI/issues/43
fn normalize_type_string(s: &str) -> String {
    if is_ascii_printable(s) {
        let s_lower = s.to_lowercase();
        // match s_lower.parse() as Result<Mime, ()> {
            // Ok(_) => s_lower,
            // Err(_) => "".to_string()
        s_lower
    } else {
        "".to_string()
    }
}

fn is_ascii_printable(string: &str) -> bool {
    // Step 5.1 in Sec 5.1 of File API spec
    // https://w3c.github.io/FileAPI/#constructorBlob
    string.chars().all(|c| c >= '\x20' && c <= '\x7E')
}
<|endoftext|>


<commit_before>use time;
use error::{ToResult, ToError};
use std::path::PathBuf;
use std::collections::BTreeMap;
use std::collections::btree_map::Iter;
use std::fs::{File, OpenOptions};
use std::io::{BufReader, BufWriter, ErrorKind};
use bincode::SizeLimit::Infinite;
use bincode::rustc_serialize::{encode_into, decode_from};

pub type Bookmarks = BTreeMap<String, Bookmark>;

#[derive(Debug)]
pub struct Database {
    location: PathBuf,
    bookmarks: Bookmarks,
}

impl Database {
    fn new(location: PathBuf, bookmarks: Bookmarks) -> Database {
        Database {
            location: location,
            bookmarks: bookmarks,
        }
    }

    pub fn open(directory: PathBuf) -> ToResult<Database> {
        let bookmarks = match File::open(&directory) {
            Ok(file) => {
                let mut reader = BufReader::new(file);
                let bookmarks: Bookmarks = match decode_from(&mut reader, Infinite) {
                    Ok(value) => value,
                    Err(err) => panic!("ERROR DECODING: {:?}", err),
                };

                bookmarks
            },
            Err(ref err) if err.kind() == ErrorKind::NotFound => Bookmarks::new(),
            Err(err) => return Err(ToError::Io(err)),
        };

        return Ok(Database::new(directory, bookmarks));
    }

    // TODO: add a check to verify the db is open.
    // TODO: add check to verify value is a valid directory.
    pub fn put(&mut self, key: String, value: PathBuf) -> ToResult<()> {
        if self.bookmarks.contains_key(&key) {
            if let Some(bookmark) = self.bookmarks.get_mut(&key) {
                bookmark.directory = value;
                bookmark.updated_at = now();
            }
        } else {
            let bookmark = Bookmark::new(key.clone(), value);
            self.bookmarks.insert(key, bookmark);
        }

        match self.close() {
            Ok(value) => return Ok(value),
            Err(err) => panic!("Failed to close: {:?}", err),
        };
    }

    pub fn get(&self, key: &String) -> Option<&Bookmark> {
        return self.bookmarks.get(key);
    }

    pub fn delete(&mut self, key: String) -> ToResult<()> {
        match self.bookmarks.remove(&key) {
            None => panic!("nothing to delete."),
            _ => {},
        }

        match self.close() {
            Ok(value) => return Ok(value),
            Err(err) => panic!("Failed to close: {:?}", err),
        };
    }

    pub fn all<'a>(&'a self) -> Iter<'a, String, Bookmark> {
        return self.bookmarks.iter();
    }

    fn close(&self) -> ToResult<()> {
        let mut options = OpenOptions::new();
                options.write(true);

        let file = match options.open(&self.location) {
            Ok(file) => file,
            Err(_) => {
                options.create(true);
                try!(options.open(&self.location))
            }
        };

        let mut writer = BufWriter::new(file);
        match encode_into(&self.bookmarks, &mut writer, Infinite) {
            Ok(_) => println!("successful encode"),
            Err(err) => panic!("ERROR ECODING: {:?}", err),
        }

        return Ok(());
    }
}

#[derive(Debug, RustcEncodable, RustcDecodable, PartialEq)]
pub struct Bookmark {
    pub name: String,
    pub directory: PathBuf,
    created_at: i64,
    pub updated_at: i64,
    accessed_at: Option<i64>,
}

impl Bookmark {
    pub fn new(name: String, directory: PathBuf) -> Bookmark {
        return Bookmark {
            name: name,
            directory: directory,
            created_at: now(),
            updated_at: now(),
            accessed_at: None,
        };
    }
}

fn now() -> i64 {
    return time::now_utc().to_timespec().sec;
}
<commit_msg>Removes debug output.<commit_after>use time;
use error::{ToResult, ToError};
use std::path::PathBuf;
use std::collections::BTreeMap;
use std::collections::btree_map::Iter;
use std::fs::{File, OpenOptions};
use std::io::{BufReader, BufWriter, ErrorKind};
use bincode::SizeLimit::Infinite;
use bincode::rustc_serialize::{encode_into, decode_from};

pub type Bookmarks = BTreeMap<String, Bookmark>;

#[derive(Debug)]
pub struct Database {
    location: PathBuf,
    bookmarks: Bookmarks,
}

impl Database {
    fn new(location: PathBuf, bookmarks: Bookmarks) -> Database {
        Database {
            location: location,
            bookmarks: bookmarks,
        }
    }

    pub fn open(directory: PathBuf) -> ToResult<Database> {
        let bookmarks = match File::open(&directory) {
            Ok(file) => {
                let mut reader = BufReader::new(file);
                let bookmarks: Bookmarks = match decode_from(&mut reader, Infinite) {
                    Ok(value) => value,
                    Err(err) => panic!("ERROR DECODING: {:?}", err),
                };

                bookmarks
            },
            Err(ref err) if err.kind() == ErrorKind::NotFound => Bookmarks::new(),
            Err(err) => return Err(ToError::Io(err)),
        };

        return Ok(Database::new(directory, bookmarks));
    }

    // TODO: add a check to verify the db is open.
    // TODO: add check to verify value is a valid directory.
    pub fn put(&mut self, key: String, value: PathBuf) -> ToResult<()> {
        if self.bookmarks.contains_key(&key) {
            if let Some(bookmark) = self.bookmarks.get_mut(&key) {
                bookmark.directory = value;
                bookmark.updated_at = now();
            }
        } else {
            let bookmark = Bookmark::new(key.clone(), value);
            self.bookmarks.insert(key, bookmark);
        }

        match self.close() {
            Ok(value) => return Ok(value),
            Err(err) => panic!("Failed to close: {:?}", err),
        };
    }

    pub fn get(&self, key: &String) -> Option<&Bookmark> {
        return self.bookmarks.get(key);
    }

    pub fn delete(&mut self, key: String) -> ToResult<()> {
        match self.bookmarks.remove(&key) {
            None => panic!("nothing to delete."),
            _ => {},
        }

        match self.close() {
            Ok(value) => return Ok(value),
            Err(err) => panic!("Failed to close: {:?}", err),
        };
    }

    pub fn all<'a>(&'a self) -> Iter<'a, String, Bookmark> {
        return self.bookmarks.iter();
    }

    fn close(&self) -> ToResult<()> {
        let mut options = OpenOptions::new();
                options.write(true);

        let file = match options.open(&self.location) {
            Ok(file) => file,
            Err(_) => {
                options.create(true);
                try!(options.open(&self.location))
            }
        };

        let mut writer = BufWriter::new(file);
        match encode_into(&self.bookmarks, &mut writer, Infinite) {
            Ok(_) => {},
            Err(err) => panic!("ERROR ECODING: {:?}", err),
        }

        return Ok(());
    }
}

#[derive(Debug, RustcEncodable, RustcDecodable, PartialEq)]
pub struct Bookmark {
    pub name: String,
    pub directory: PathBuf,
    created_at: i64,
    pub updated_at: i64,
    accessed_at: Option<i64>,
}

impl Bookmark {
    pub fn new(name: String, directory: PathBuf) -> Bookmark {
        return Bookmark {
            name: name,
            directory: directory,
            created_at: now(),
            updated_at: now(),
            accessed_at: None,
        };
    }
}

fn now() -> i64 {
    return time::now_utc().to_timespec().sec;
}
<|endoftext|>


<commit_before>use std::fmt;

use serde::de::Visitor;
use serde::{self, Deserialize};

use super::CellErrorType;

/// An enum to represent all different data types that can appear as
/// a value in a worksheet cell
#[derive(Debug, Clone, PartialEq)]
pub enum DataType {
    /// Unsigned integer
    Int(i64),
    /// Float
    Float(f64),
    /// String
    String(String),
    /// Boolean
    Bool(bool),
    /// Error
    Error(CellErrorType),
    /// Empty cell
    Empty,
}

impl Default for DataType {
    fn default() -> DataType {
        DataType::Empty
    }
}

impl DataType {
    /// Assess if datatype is empty
    pub fn is_empty(&self) -> bool {
        *self == DataType::Empty
    }
    /// Assess if datatype is a int
    pub fn is_int(&self) -> bool {
        if let DataType::Int(_) = *self {
            true
        } else {
            false
        }
    }
    /// Assess if datatype is a float
    pub fn is_float(&self) -> bool {
        if let DataType::Float(_) = *self {
            true
        } else {
            false
        }
    }
    /// Assess if datatype is a bool
    pub fn is_bool(&self) -> bool {
        if let DataType::Bool(_) = *self {
            true
        } else {
            false
        }
    }
    /// Assess if datatype is a string
    pub fn is_string(&self) -> bool {
        if let DataType::String(_) = *self {
            true
        } else {
            false
        }
    }
}

impl PartialEq<str> for DataType {
    fn eq(&self, other: &str) -> bool {
        match *self {
            DataType::String(ref s) if s == other => true,
            _ => false,
        }
    }
}

impl PartialEq<f64> for DataType {
    fn eq(&self, other: &f64) -> bool {
        match *self {
            DataType::Float(ref s) if *s == *other => true,
            _ => false,
        }
    }
}

impl PartialEq<bool> for DataType {
    fn eq(&self, other: &bool) -> bool {
        match *self {
            DataType::Bool(ref s) if *s == *other => true,
            _ => false,
        }
    }
}

impl PartialEq<i64> for DataType {
    fn eq(&self, other: &i64) -> bool {
        match *self {
            DataType::Int(ref s) if *s == *other => true,
            _ => false,
        }
    }
}

impl fmt::Display for DataType {
    fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
        match *self {
            DataType::Int(ref e) => write!(f, "{}", e),
            DataType::Float(ref e) => write!(f, "{}", e),
            DataType::String(ref e) => write!(f, "{}", e),
            DataType::Bool(ref e) => write!(f, "{}", e),
            DataType::Error(ref e) => write!(f, "{}", e),
            DataType::Empty => Ok(()),
        }
    }
}

impl<'de> Deserialize<'de> for DataType {
    #[inline]
    fn deserialize<D>(deserializer: D) -> Result<DataType, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        struct DataTypeVisitor;

        impl<'de> Visitor<'de> for DataTypeVisitor {
            type Value = DataType;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("any valid JSON value")
            }

            #[inline]
            fn visit_bool<E>(self, value: bool) -> Result<DataType, E> {
                Ok(DataType::Bool(value))
            }

            #[inline]
            fn visit_i64<E>(self, value: i64) -> Result<DataType, E> {
                Ok(DataType::Int(value))
            }

            #[inline]
            fn visit_u64<E>(self, value: u64) -> Result<DataType, E> {
                Ok(DataType::Int(value as i64))
            }

            #[inline]
            fn visit_f64<E>(self, value: f64) -> Result<DataType, E> {
                Ok(DataType::Float(value))
            }

            #[inline]
            fn visit_str<E>(self, value: &str) -> Result<DataType, E>
            where
                E: serde::de::Error,
            {
                self.visit_string(String::from(value))
            }

            #[inline]
            fn visit_string<E>(self, value: String) -> Result<DataType, E> {
                Ok(DataType::String(value))
            }

            #[inline]
            fn visit_none<E>(self) -> Result<DataType, E> {
                Ok(DataType::Empty)
            }

            #[inline]
            fn visit_some<D>(self, deserializer: D) -> Result<DataType, D::Error>
            where
                D: serde::Deserializer<'de>,
            {
                Deserialize::deserialize(deserializer)
            }

            #[inline]
            fn visit_unit<E>(self) -> Result<DataType, E> {
                Ok(DataType::Empty)
            }
        }

        deserializer.deserialize_any(DataTypeVisitor)
    }
}

macro_rules! define_from {
    ($variant:path, $ty:ty) => {
        impl From<$ty> for DataType {
            fn from(v: $ty) -> Self {
                $variant(v)
            }
        }
    };
}

define_from!(DataType::Int, i64);
define_from!(DataType::Float, f64);
define_from!(DataType::String, String);
define_from!(DataType::Bool, bool);
define_from!(DataType::Error, CellErrorType);

impl<'a> From<&'a str> for DataType {
    fn from(v: &'a str) -> Self {
        DataType::String(String::from(v))
    }
}

impl From<()> for DataType {
    fn from(_: ()) -> Self {
        DataType::Empty
    }
}

impl<T> From<Option<T>> for DataType
where
    DataType: From<T>,
{
    fn from(v: Option<T>) -> Self {
        match v {
            Some(v) => From::from(v),
            None => DataType::Empty,
        }
    }
}
<commit_msg>add get_datatype functions<commit_after>use std::fmt;

use serde::de::Visitor;
use serde::{self, Deserialize};

use super::CellErrorType;

/// An enum to represent all different data types that can appear as
/// a value in a worksheet cell
#[derive(Debug, Clone, PartialEq)]
pub enum DataType {
    /// Unsigned integer
    Int(i64),
    /// Float
    Float(f64),
    /// String
    String(String),
    /// Boolean
    Bool(bool),
    /// Error
    Error(CellErrorType),
    /// Empty cell
    Empty,
}

impl Default for DataType {
    fn default() -> DataType {
        DataType::Empty
    }
}

impl DataType {
    /// Assess if datatype is empty
    pub fn is_empty(&self) -> bool {
        *self == DataType::Empty
    }
    /// Assess if datatype is a int
    pub fn is_int(&self) -> bool {
        if let DataType::Int(_) = *self {
            true
        } else {
            false
        }
    }
    /// Assess if datatype is a float
    pub fn is_float(&self) -> bool {
        if let DataType::Float(_) = *self {
            true
        } else {
            false
        }
    }
    /// Assess if datatype is a bool
    pub fn is_bool(&self) -> bool {
        if let DataType::Bool(_) = *self {
            true
        } else {
            false
        }
    }
    /// Assess if datatype is a string
    pub fn is_string(&self) -> bool {
        if let DataType::String(_) = *self {
            true
        } else {
            false
        }
    }

    /// Try getting int value
    pub fn get_int(&self) -> Option<i64> {
        if let DataType::Int(v) = self {
            Some(*v)
        } else {
            None
        }
    }
    /// Try getting float value
    pub fn get_float(&self) -> Option<f64> {
        if let DataType::Float(v) = self {
            Some(*v)
        } else {
            None
        }
    }
    /// Try getting bool value
    pub fn get_bool(&self) -> Option<bool> {
        if let DataType::Bool(v) = self {
            Some(*v)
        } else {
            None
        }
    }
    /// Try getting string value
    pub fn get_string(&self) -> Option<&str> {
        if let DataType::String(v) = self {
            Some(&**v)
        } else {
            None
        }
    }
}

impl PartialEq<str> for DataType {
    fn eq(&self, other: &str) -> bool {
        match *self {
            DataType::String(ref s) if s == other => true,
            _ => false,
        }
    }
}

impl PartialEq<f64> for DataType {
    fn eq(&self, other: &f64) -> bool {
        match *self {
            DataType::Float(ref s) if *s == *other => true,
            _ => false,
        }
    }
}

impl PartialEq<bool> for DataType {
    fn eq(&self, other: &bool) -> bool {
        match *self {
            DataType::Bool(ref s) if *s == *other => true,
            _ => false,
        }
    }
}

impl PartialEq<i64> for DataType {
    fn eq(&self, other: &i64) -> bool {
        match *self {
            DataType::Int(ref s) if *s == *other => true,
            _ => false,
        }
    }
}

impl fmt::Display for DataType {
    fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
        match *self {
            DataType::Int(ref e) => write!(f, "{}", e),
            DataType::Float(ref e) => write!(f, "{}", e),
            DataType::String(ref e) => write!(f, "{}", e),
            DataType::Bool(ref e) => write!(f, "{}", e),
            DataType::Error(ref e) => write!(f, "{}", e),
            DataType::Empty => Ok(()),
        }
    }
}

impl<'de> Deserialize<'de> for DataType {
    #[inline]
    fn deserialize<D>(deserializer: D) -> Result<DataType, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        struct DataTypeVisitor;

        impl<'de> Visitor<'de> for DataTypeVisitor {
            type Value = DataType;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                formatter.write_str("any valid JSON value")
            }

            #[inline]
            fn visit_bool<E>(self, value: bool) -> Result<DataType, E> {
                Ok(DataType::Bool(value))
            }

            #[inline]
            fn visit_i64<E>(self, value: i64) -> Result<DataType, E> {
                Ok(DataType::Int(value))
            }

            #[inline]
            fn visit_u64<E>(self, value: u64) -> Result<DataType, E> {
                Ok(DataType::Int(value as i64))
            }

            #[inline]
            fn visit_f64<E>(self, value: f64) -> Result<DataType, E> {
                Ok(DataType::Float(value))
            }

            #[inline]
            fn visit_str<E>(self, value: &str) -> Result<DataType, E>
            where
                E: serde::de::Error,
            {
                self.visit_string(String::from(value))
            }

            #[inline]
            fn visit_string<E>(self, value: String) -> Result<DataType, E> {
                Ok(DataType::String(value))
            }

            #[inline]
            fn visit_none<E>(self) -> Result<DataType, E> {
                Ok(DataType::Empty)
            }

            #[inline]
            fn visit_some<D>(self, deserializer: D) -> Result<DataType, D::Error>
            where
                D: serde::Deserializer<'de>,
            {
                Deserialize::deserialize(deserializer)
            }

            #[inline]
            fn visit_unit<E>(self) -> Result<DataType, E> {
                Ok(DataType::Empty)
            }
        }

        deserializer.deserialize_any(DataTypeVisitor)
    }
}

macro_rules! define_from {
    ($variant:path, $ty:ty) => {
        impl From<$ty> for DataType {
            fn from(v: $ty) -> Self {
                $variant(v)
            }
        }
    };
}

define_from!(DataType::Int, i64);
define_from!(DataType::Float, f64);
define_from!(DataType::String, String);
define_from!(DataType::Bool, bool);
define_from!(DataType::Error, CellErrorType);

impl<'a> From<&'a str> for DataType {
    fn from(v: &'a str) -> Self {
        DataType::String(String::from(v))
    }
}

impl From<()> for DataType {
    fn from(_: ()) -> Self {
        DataType::Empty
    }
}

impl<T> From<Option<T>> for DataType
where
    DataType: From<T>,
{
    fn from(v: Option<T>) -> Self {
        match v {
            Some(v) => From::from(v),
            None => DataType::Empty,
        }
    }
}
<|endoftext|>


<commit_before>use piston_window;
use gfx_device_gl::{Resources, Output, CommandBuffer};
use gfx_graphics::GfxGraphics;

use piston_window::Transformed;
use piston_window::ImageSize;

#[derive(Debug)]
pub struct Object {
    x: f64,
    y: f64,
    sprite: Option<piston_window::Texture<Resources>>,
}

impl Object {
    pub fn new() -> Object {
        Object { x: 0., y: 0., sprite: None }
    }

    pub fn mov(&mut self, x: f64, y: f64) {
        self.x += x;
        self.y += y;
    }

    pub fn mov_to(&mut self, x: f64, y: f64) {
        self.x = x;
        self.y = y;
    }

    pub fn set_sprite(&mut self, sprite: piston_window::Texture<Resources>) {
        self.sprite = Some(sprite);
    }

    pub fn render(&self, graphic: &mut GfxGraphics<Resources, CommandBuffer<Resources>, Output>, view: &piston_window::math::Matrix2d) {
        match self.sprite {
            None => {
                let red = [1.0, 0.0, 0.0, 1.0];
                let square = piston_window::rectangle::square(0.0, 0.0, 100.0);
                piston_window::rectangle(red, square, view.trans(self.x, self.y).trans(-50.0, -50.0), graphic);
            }
            Some(ref sprite) => {
                let (x, y) = sprite.get_size();
                let (x, y) = (x / 2, y / 2);
                piston_window::image(sprite, view.trans(self.x, self.y).trans(-(x as f64), -(y as f64)), graphic);
            }
        }
    }
}

#[test]
fn object_new() {
    let object = Object::new();
    assert_eq!(object.x, 0.0);
    assert_eq!(object.y, 0.0);
}

#[test]
fn object_mov() {
    let mut object = Object::new();
    assert_eq!(object.x, 0.);
    assert_eq!(object.y, 0.);

    object.mov(0., 0.);
    assert_eq!(object.x, 0.);
    assert_eq!(object.y, 0.);

    object.mov(1., 1.);
    assert_eq!(object.x, 1.);
    assert_eq!(object.y, 1.);

    object.mov(-3., -3.);
    assert_eq!(object.x, -2.);
    assert_eq!(object.y, -2.);
}

#[test]
fn object_mov_to() {
    let mut object = Object::new();
    assert_eq!(object.x, 0.);
    assert_eq!(object.y, 0.);

    object.mov_to(0., 0.);
    assert_eq!(object.x, 0.);
    assert_eq!(object.y, 0.);

    object.mov_to(10., 10.);
    assert_eq!(object.x, 10.);
    assert_eq!(object.y, 10.);

    object.mov_to(-30., -30.);
    assert_eq!(object.x, -30.);
    assert_eq!(object.y, -30.);
}
<commit_msg>Nonopt sprite<commit_after>use piston_window;
use gfx_device_gl::{Resources, Output, CommandBuffer};
use gfx_graphics::GfxGraphics;

use piston_window::Transformed;
use piston_window::ImageSize;

#[derive(Debug)]
pub struct Object {
    x: f64,
    y: f64,
    sprite: piston_window::Texture<Resources>,
}

impl Object {
    pub fn new() -> Object {
        Object { x: 0., y: 0., sprite: None }
    }

    pub fn mov(&mut self, x: f64, y: f64) {
        self.x += x;
        self.y += y;
    }

    pub fn mov_to(&mut self, x: f64, y: f64) {
        self.x = x;
        self.y = y;
    }

    pub fn set_sprite(&mut self, sprite: piston_window::Texture<Resources>) {
        self.sprite = Some(sprite);
    }

    pub fn render(&self, graphic: &mut GfxGraphics<Resources, CommandBuffer<Resources>, Output>, view: &piston_window::math::Matrix2d) {
        let (x, y) = sprite.get_size();
        let (x, y) = (x / 2, y / 2);
        piston_window::image(sprite, view.trans(self.x, self.y).trans(-(x as f64), -(y as f64)), graphic);
    }
}

#[test]
fn object_new() {
    let object = Object::new();
    assert_eq!(object.x, 0.0);
    assert_eq!(object.y, 0.0);
}

#[test]
fn object_mov() {
    let mut object = Object::new();
    assert_eq!(object.x, 0.);
    assert_eq!(object.y, 0.);

    object.mov(0., 0.);
    assert_eq!(object.x, 0.);
    assert_eq!(object.y, 0.);

    object.mov(1., 1.);
    assert_eq!(object.x, 1.);
    assert_eq!(object.y, 1.);

    object.mov(-3., -3.);
    assert_eq!(object.x, -2.);
    assert_eq!(object.y, -2.);
}

#[test]
fn object_mov_to() {
    let mut object = Object::new();
    assert_eq!(object.x, 0.);
    assert_eq!(object.y, 0.);

    object.mov_to(0., 0.);
    assert_eq!(object.x, 0.);
    assert_eq!(object.y, 0.);

    object.mov_to(10., 10.);
    assert_eq!(object.x, 10.);
    assert_eq!(object.y, 10.);

    object.mov_to(-30., -30.);
    assert_eq!(object.x, -30.);
    assert_eq!(object.y, -30.);
}
<|endoftext|>


<commit_before>#![feature(slice_patterns,convert,iter_cmp,box_patterns)]

use std::fmt;
use std::collections::HashSet;
use Route::*;

extern crate adventutils;

#[derive(Clone)]
pub enum Route {
    NextLeg(Box<Route>, Leg),
    Start
}

impl Route {
    pub fn distance(&self) -> usize {
        match self {
            &NextLeg(ref prev, ref leg) => prev.distance() + leg.distance,
            &Start => 0
        }
    }

    pub fn already_visited(&self, location: &str) -> bool {
        match self {
            &NextLeg(ref prev, ref leg) => leg.start == location || leg.end == location || prev.already_visited(location),
            &Start => false
        }
    }
}

impl fmt::Display for Route {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self {
            &NextLeg(box Start, ref leg) => write!(fmt, "{} -> {}", leg.start, leg.end),
            &NextLeg(ref prev, ref leg) => write!(fmt, "{} -> {}", prev, leg.end),
            &Start => Ok(())
        }
    }
}

#[derive(Clone)]
pub struct Leg {
    start: String,
    end: String,
    distance: usize
}

impl Leg {
    pub fn new(start: String, end: String, distance: usize) -> Leg {
        Leg {
            start: start,
            end: end,
            distance: distance
        }
    }
}

fn main() {
    let input = adventutils::read_input_file();
    let legs : Vec<_> = input.lines().flat_map(&parse_line).collect();

    // Collect locations
    let start_points : HashSet<_> = legs.iter().map(|l| l.start.clone()).collect();
    let end_points : HashSet<_> = legs.iter().map(|l| l.end.clone()).collect();
    let all_locations : HashSet<_> = start_points.union(&end_points).collect();

    let best_route = start_points.iter()
        .map(|s| find_best_route(s, &legs, &all_locations, Route::Start))
        .filter_map(|r| r)
        .min_by(|r| r.distance());

    match best_route {
        Some(r) => println!("Found best route: {} ({})", r, r.distance()),
        None => println!("Failed to find a route!")
    }
}

// Find the best possible route from the specified start to all remaining locations
fn find_best_route(start: &str, legs: &Vec<Leg>, locations: &HashSet<&String>, current_route: Route) -> Option<Route> {
    // Have we already visited everything?
    if locations.iter().all(|l| current_route.already_visited(l)) {
        println!("Candidate Route: {} = {}", current_route, current_route.distance());
        Some(current_route)
    } else {
        println!("Probing: {} -> ?", current_route);
        legs.iter()
            .filter(|l| l.start == start && !current_route.already_visited(&l.end))
            .map(|l| find_best_route(&l.end, legs, locations, Route::NextLeg(Box::new(current_route.clone()), l.clone())))
            .filter_map(|r| r)
            .min_by(|r| r.distance())
    }
}

fn parse_line(input: &str) -> Vec<Leg> {
    let tokens : Vec<_> = input.split_whitespace().collect();
    if let [ start, "to", end, "=", distance ] = tokens.as_slice() {
        vec![
            Leg::new(start.to_string(), end.to_string(), usize::from_str_radix(distance, 10).unwrap()),
            Leg::new(end.to_string(), start.to_string(), usize::from_str_radix(distance, 10).unwrap())
        ]
    } else {
        panic!("Unrecognized string: {}", input);
    }
}
<commit_msg>day9 part2<commit_after>#![feature(slice_patterns,convert,iter_cmp,box_patterns)]

use std::fmt;
use std::collections::HashSet;
use Route::*;

extern crate adventutils;

#[derive(Clone)]
pub enum Route {
    NextLeg(Box<Route>, Leg),
    Start
}

impl Route {
    pub fn distance(&self) -> usize {
        match self {
            &NextLeg(ref prev, ref leg) => prev.distance() + leg.distance,
            &Start => 0
        }
    }

    pub fn already_visited(&self, location: &str) -> bool {
        match self {
            &NextLeg(ref prev, ref leg) => leg.start == location || leg.end == location || prev.already_visited(location),
            &Start => false
        }
    }
}

impl fmt::Display for Route {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match self {
            &NextLeg(box Start, ref leg) => write!(fmt, "{} -> {}", leg.start, leg.end),
            &NextLeg(ref prev, ref leg) => write!(fmt, "{} -> {}", prev, leg.end),
            &Start => Ok(())
        }
    }
}

#[derive(Clone)]
pub struct Leg {
    start: String,
    end: String,
    distance: usize
}

impl Leg {
    pub fn new(start: String, end: String, distance: usize) -> Leg {
        Leg {
            start: start,
            end: end,
            distance: distance
        }
    }
}

fn main() {
    let input = adventutils::read_input_file();
    let legs : Vec<_> = input.lines().flat_map(&parse_line).collect();

    // Collect locations
    let start_points : HashSet<_> = legs.iter().map(|l| l.start.clone()).collect();
    let end_points : HashSet<_> = legs.iter().map(|l| l.end.clone()).collect();
    let all_locations : HashSet<_> = start_points.union(&end_points).collect();

    let shortest_route = start_points.iter()
        .map(|s| find_shortest_route(s, &legs, &all_locations, Route::Start))
        .filter_map(|r| r)
        .min_by(|r| r.distance());

    match shortest_route {
        Some(r) => println!("Found shortest route: {} ({})", r, r.distance()),
        None => println!("Failed to find a shortest route!")
    }

    let longest_route = start_points.iter()
        .map(|s| find_longest_route(s, &legs, &all_locations, Route::Start))
        .filter_map(|r| r)
        .max_by(|r| r.distance());

    match longest_route {
        Some(r) => println!("Found longest route: {} ({})", r, r.distance()),
        None => println!("Failed to find a route!")
    }
}

// Find the shortest possible route from the specified start to all remaining locations
fn find_shortest_route(start: &str, legs: &Vec<Leg>, locations: &HashSet<&String>, current_route: Route) -> Option<Route> {
    // Have we already visited everything?
    if locations.iter().all(|l| current_route.already_visited(l)) {
        Some(current_route)
    } else {
        legs.iter()
            .filter(|l| l.start == start && !current_route.already_visited(&l.end))
            .map(|l| find_shortest_route(&l.end, legs, locations, Route::NextLeg(Box::new(current_route.clone()), l.clone())))
            .filter_map(|r| r)
            .min_by(|r| r.distance())
    }
}

// Find the shortest possible route from the specified start to all remaining locations
fn find_longest_route(start: &str, legs: &Vec<Leg>, locations: &HashSet<&String>, current_route: Route) -> Option<Route> {
    // Have we already visited everything?
    if locations.iter().all(|l| current_route.already_visited(l)) {
        Some(current_route)
    } else {
        legs.iter()
            .filter(|l| l.start == start && !current_route.already_visited(&l.end))
            .map(|l| find_longest_route(&l.end, legs, locations, Route::NextLeg(Box::new(current_route.clone()), l.clone())))
            .filter_map(|r| r)
            .max_by(|r| r.distance())
    }
}

fn parse_line(input: &str) -> Vec<Leg> {
    let tokens : Vec<_> = input.split_whitespace().collect();
    if let [ start, "to", end, "=", distance ] = tokens.as_slice() {
        vec![
            Leg::new(start.to_string(), end.to_string(), usize::from_str_radix(distance, 10).unwrap()),
            Leg::new(end.to_string(), start.to_string(), usize::from_str_radix(distance, 10).unwrap())
        ]
    } else {
        panic!("Unrecognized string: {}", input);
    }
}
<|endoftext|>


<commit_before>use syntax::{ast, codemap, parse};
use syntax::ptr::P;
use syntax::ext::base;

use parse::Parse;
use generate::Generate;
use test::Test;
use bench::Bench;

/// Defines the state of a `describe!` macro as it is parsing.
pub struct DescribeState {
    pub name: Option<ast::Ident>,
    pub before: Option<P<ast::Block>>,
    pub after: Option<P<ast::Block>>,
    pub before_each: Option<P<ast::Block>>,
    pub after_each: Option<P<ast::Block>>,
    pub subblocks: Vec<SubBlock>
}

/// Any supported subblock.
#[deriving(Clone)]
pub enum SubBlock {
    TestBlock(Test),
    BenchBlock(Bench),
    DescribeBlock(P<ast::Item>)
}

/// Defines the overarching `describe!` syntax extension.
///
/// All other macros in stainless are actually "fake" in the sense
/// that they are detected and expanded inside of the implementation
/// of `describe!`.
pub fn describe<'a>(cx: &'a mut base::ExtCtxt, sp: codemap::Span,
                name: ast::Ident, tokens: Vec<ast::TokenTree>) -> Box<base::MacResult + 'a> {
    // Parse a full DescribeState from the input, emitting errors if used incorrectly.
    let state: DescribeState = Parse::parse(&mut parse::tts_to_parser(cx.parse_sess(), tokens, cx.cfg()), (sp, &mut*cx, Some(name)));

    // Export the new module.
    base::MacItems::new(Some(state.generate(sp, cx, ())).into_iter())
}

<commit_msg>(doc) Added explanation of internal code.<commit_after>/// ## Internal Code Guide
///
/// This crate is centered around two traits, Parse and Generate,
/// which define how to parse a struct from a Parser over Rust code
/// and how to generate Rust code from a struct respectively.
///
/// All the parsing and generation of code is done through these traits.
///
/// `describe` is responsible for expanding the `describe!` macro
/// as a whole, and delegates to the Parse and Generate implementations
/// for `DescribeState`, which holds all the information necessary to
/// generate the AST for an expanded `describe!`.
///
/// The Parse implementation of DescribeState delegates to the Parse
/// implementation of Test, defined in the test module, and Bench,
/// defined in the bench module. The Generate implementation does the
/// same.
///
/// Most of the code can be understood by just walking through the
/// implementations of Parse and Generate for all the types inside
/// this crate, which can be found in the parse and generate
/// modules respectively.
///

use syntax::{ast, codemap, parse};
use syntax::ptr::P;
use syntax::ext::base;

use parse::Parse;
use generate::Generate;
use test::Test;
use bench::Bench;

/// Defines the state of a `describe!` macro as it is parsing.
#[deriving(Clone)]
pub struct DescribeState {
    pub name: Option<ast::Ident>,
    pub before: Option<P<ast::Block>>,
    pub after: Option<P<ast::Block>>,
    pub before_each: Option<P<ast::Block>>,
    pub after_each: Option<P<ast::Block>>,
    pub subblocks: Vec<SubBlock>
}

/// Any supported subblock.
#[deriving(Clone)]
pub enum SubBlock {
    TestBlock(Test),
    BenchBlock(Bench),
    DescribeBlock(DescribeState)
}

/// Defines the overarching `describe!` syntax extension.
///
/// All other macros in stainless are actually "fake" in the sense
/// that they are detected and expanded inside of the implementation
/// of `describe!`.
pub fn describe<'a>(cx: &'a mut base::ExtCtxt, sp: codemap::Span,
                name: ast::Ident, tokens: Vec<ast::TokenTree>) -> Box<base::MacResult + 'a> {
    // Parse a full DescribeState from the input, emitting errors if used incorrectly.
    let state: DescribeState = Parse::parse(&mut parse::tts_to_parser(cx.parse_sess(), tokens, cx.cfg()), (sp, &mut*cx, Some(name)));

    // Export the new module.
    base::MacItems::new(Some(state.generate(sp, cx, ())).into_iter())
}

<|endoftext|>


<commit_before>use cargo_helper::CargoInfo;
use Bytes;
use client::GetResponse;
use hyper::client::Client;
use hyper::error::Error;
use hyper::header::{ByteRangeSpec, Headers, Range};
use pbr::{MultiBar, Pipe, ProgressBar, Units};
use response::CheckResponseStatus;
use std::cmp::min;
use std::io::Read;
use std::thread;
use std::time::{Instant, Duration};
use write::{OutputFileWriter, OutputChunkWriter};

/// Constant to represent the length of the buffer to download
/// the remote content
const DOWNLOAD_BUFFER_BYTES: usize = 1024 * 64;

/// Constant to represent the refresh interval (in milliseconds)
/// for the CLI
const PROGRESS_UPDATE_INTERVAL_MILLIS: u64 = 500;

/// Represents a range between two Bytes types
#[derive(Debug, PartialEq)]
struct RangeBytes(Bytes, Bytes);

macro_rules! initbar {
    ($mp:ident,$mpb:ident,$length:expr,$index:expr) => {
        let mut $mp = $mpb.create_bar($length);
        $mp.tick_format("▏▎▍▌▋▊▉██▉▊▋▌▍▎▏");
        $mp.format("|#--|");
        $mp.show_tick = true;
        $mp.show_speed = true;
        $mp.show_percent = true;
        $mp.show_counter = false;
        $mp.show_time_left = true;
        $mp.set_units(Units::Bytes);
        $mp.message(&format!("Chunk {} ", $index));
    }
}

/// Function to get the current chunk length, based on the chunk index.
fn get_chunk_length(chunk_index: u64,
                    content_length: Bytes,
                    global_chunk_length: Bytes)
                    -> Option<RangeBytes> {

    if content_length == 0 || global_chunk_length == 0 {
        return None;
    }

    let b_range: Bytes = chunk_index * global_chunk_length;

    if b_range >= (content_length - 1) {
        return None;
    }

    let e_range: Bytes = min(content_length - 1,
                             ((chunk_index + 1) * global_chunk_length) - 1);

    Some(RangeBytes(b_range, e_range))

}

/// Function to get the HTTP header to send to the file server, for a chunk (specified by its index)
fn get_header_from_index(chunk_index: u64,
                         content_length: Bytes,
                         global_chunk_length: Bytes)
                         -> Option<(Headers, RangeBytes)> {

    get_chunk_length(chunk_index, content_length, global_chunk_length).map(|range| {
        let mut header = Headers::new();
        header.set(Range::Bytes(vec![ByteRangeSpec::FromTo(range.0, range.1)]));
        (header, RangeBytes(range.0, range.1 - range.0))
    })
}

/// Function to get from the server the content of a chunk.
/// This function returns a Result type - Bytes if the content of the header is accessible, an Error type otherwise.
fn download_a_chunk(http_client: &Client,
                    http_header: Headers,
                    mut chunk_writer: OutputChunkWriter,
                    url: &str,
                    mpb: &mut ProgressBar<Pipe>,
                    monothreading: bool)
                    -> Result<Bytes, Error> {

    let mut body = http_client
        .get_http_response_using_headers(url, http_header)
        .unwrap();
    if monothreading && !body.check_partialcontent_status() {
        return Err(Error::Status);
    }
    let mut bytes_buffer = [0; DOWNLOAD_BUFFER_BYTES];
    let mut sum_bytes = 0;

    let progress_update_interval = Duration::from_millis(PROGRESS_UPDATE_INTERVAL_MILLIS);
    let mut last_progress_bytes = 0;
    let mut last_progress_time = Instant::now() - progress_update_interval;

    while let Ok(n) = body.read(&mut bytes_buffer) {
        if n == 0 {
            return Ok(sum_bytes);
        }

        chunk_writer.write(sum_bytes, &bytes_buffer[0..n]);

        sum_bytes += n as u64;

        // Update the CLI
        if Instant::now().duration_since(last_progress_time) > progress_update_interval {
            last_progress_time = Instant::now();
            let progress_bytes_delta = sum_bytes - last_progress_bytes;
            last_progress_bytes = sum_bytes;
            mpb.add(progress_bytes_delta);
        }
    }
    mpb.add(sum_bytes - last_progress_bytes);
    return Ok(0u64);
}

/// Function to download each chunk of a remote content (given by its URL).
/// This function takes as parameters:
/// * the remote content length,
/// * a mutable reference to share between threads, which contains each chunk,
/// * the number of chunks that contains the remote content,
/// * the URL of the remote content server,
/// * a custom authorization to access and download the remote content.
pub fn download_chunks(cargo_info: CargoInfo,
                       mut out_file: OutputFileWriter,
                       nb_chunks: u64,
                       url: &str)
                       -> bool {
    let (content_length, auth_header_factory) = (cargo_info.content_length, cargo_info.auth_header);

    let global_chunk_length: u64 = (content_length / nb_chunks) + 1;
    let mut jobs = vec![];

    let mut mpb = MultiBar::new();
    mpb.println(&format!("Downloading {} chunks: ", nb_chunks));

    for chunk_index in 0..nb_chunks {

        let (mut http_header, RangeBytes(chunk_offset, chunk_length)) =
            get_header_from_index(chunk_index, content_length, global_chunk_length).unwrap();
        let hyper_client = Client::new();
        let url_clone = String::from(url);
        if let Some(auth_header_factory) = auth_header_factory.clone() {
            http_header.set(auth_header_factory.build_header());
        }
        let monothreading = cargo_info.accept_partialcontent;

        // Initialize the progress bar for that chunk
        initbar!(mp, mpb, chunk_length, chunk_index);

        let chunk_writer = out_file.get_chunk_writer(chunk_offset);

        // In this work, we push a boolean value to know if the chunk is OK
        jobs.push(thread::spawn(move || match download_a_chunk(&hyper_client,
                                                               http_header,
                                                               chunk_writer,
                                                               &url_clone,
                                                               &mut mp,
                                                               monothreading) {
                                    Ok(bytes_written) => {
            mp.finish();
            if bytes_written == 0 {
                println!("The downloaded chunk {} is empty", chunk_index);
            }
            return true;
        }
                                    Err(error) => {
            mp.finish();
            println!("Cannot download the chunk {}, due to error {}",
                     chunk_index,
                     error);
            return false;
        }
                                }));
    }

    mpb.listen();

    // Contain the result state for chunks
    let mut child_results: Vec<bool> = Vec::with_capacity(nb_chunks as usize);

    for child in jobs {
        match child.join() {
            Ok(b) => {
                println!("{}", b);
                child_results.push(b);
            }
            Err(error) => {
                println!("{:?}", error);
                child_results.push(false);
            }
        }
    }

    // Check if all chunks are OK
    return child_results.iter().all(|x| *x);
}

#[cfg(test)]
mod test_chunk_length {

    use super::get_chunk_length;
    use super::RangeBytes;

    #[test]
    fn wrong_content_length_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 15, 0));
    }

    #[test]
    fn wrong_global_chunk_length_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 0, 15));
    }

    #[test]
    fn wrong_length_parameters_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 0, 0));
    }

    #[test]
    fn get_the_first_range_in_chunk() {
        assert_eq!(Some(RangeBytes(0, 249)), get_chunk_length(0, 1000, 250));
    }

    #[test]
    fn get_the_last_range_in_chunk() {
        assert_eq!(Some(RangeBytes(750, 999)), get_chunk_length(3, 1000, 250));
    }

    #[test]
    fn get_the_last_range_in_shorten_chunk() {
        assert_eq!(Some(RangeBytes(750, 997)), get_chunk_length(3, 998, 250));
    }

    #[test]
    fn wrong_index_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(4, 1000, 250));
    }

}

#[cfg(test)]
mod test_header {

    use super::{get_header_from_index, RangeBytes};
    use hyper::header::{ByteRangeSpec, Headers, Range};

    #[test]
    fn wrong_chunk_length_should_return_none() {
        assert_eq!(None, get_header_from_index(0, 0, 0));
    }

    #[test]
    fn good_chunk_length_should_return_a_good_header() {
        let mut test_header = Headers::new();
        test_header.set(Range::Bytes(vec![ByteRangeSpec::FromTo(750, 997)]));
        assert_eq!(Some((test_header, RangeBytes(750, 247))),
                   get_header_from_index(3, 998, 250));
    }

}
<commit_msg>Remove debug logs<commit_after>use cargo_helper::CargoInfo;
use Bytes;
use client::GetResponse;
use hyper::client::Client;
use hyper::error::Error;
use hyper::header::{ByteRangeSpec, Headers, Range};
use pbr::{MultiBar, Pipe, ProgressBar, Units};
use response::CheckResponseStatus;
use std::cmp::min;
use std::io::Read;
use std::thread;
use std::time::{Instant, Duration};
use write::{OutputFileWriter, OutputChunkWriter};

/// Constant to represent the length of the buffer to download
/// the remote content
const DOWNLOAD_BUFFER_BYTES: usize = 1024 * 64;

/// Constant to represent the refresh interval (in milliseconds)
/// for the CLI
const PROGRESS_UPDATE_INTERVAL_MILLIS: u64 = 500;

/// Represents a range between two Bytes types
#[derive(Debug, PartialEq)]
struct RangeBytes(Bytes, Bytes);

macro_rules! initbar {
    ($mp:ident,$mpb:ident,$length:expr,$index:expr) => {
        let mut $mp = $mpb.create_bar($length);
        $mp.tick_format("▏▎▍▌▋▊▉██▉▊▋▌▍▎▏");
        $mp.format("|#--|");
        $mp.show_tick = true;
        $mp.show_speed = true;
        $mp.show_percent = true;
        $mp.show_counter = false;
        $mp.show_time_left = true;
        $mp.set_units(Units::Bytes);
        $mp.message(&format!("Chunk {} ", $index));
    }
}

/// Function to get the current chunk length, based on the chunk index.
fn get_chunk_length(chunk_index: u64,
                    content_length: Bytes,
                    global_chunk_length: Bytes)
                    -> Option<RangeBytes> {

    if content_length == 0 || global_chunk_length == 0 {
        return None;
    }

    let b_range: Bytes = chunk_index * global_chunk_length;

    if b_range >= (content_length - 1) {
        return None;
    }

    let e_range: Bytes = min(content_length - 1,
                             ((chunk_index + 1) * global_chunk_length) - 1);

    Some(RangeBytes(b_range, e_range))

}

/// Function to get the HTTP header to send to the file server, for a chunk (specified by its index)
fn get_header_from_index(chunk_index: u64,
                         content_length: Bytes,
                         global_chunk_length: Bytes)
                         -> Option<(Headers, RangeBytes)> {

    get_chunk_length(chunk_index, content_length, global_chunk_length).map(|range| {
        let mut header = Headers::new();
        header.set(Range::Bytes(vec![ByteRangeSpec::FromTo(range.0, range.1)]));
        (header, RangeBytes(range.0, range.1 - range.0))
    })
}

/// Function to get from the server the content of a chunk.
/// This function returns a Result type - Bytes if the content of the header is accessible, an Error type otherwise.
fn download_a_chunk(http_client: &Client,
                    http_header: Headers,
                    mut chunk_writer: OutputChunkWriter,
                    url: &str,
                    mpb: &mut ProgressBar<Pipe>,
                    monothreading: bool)
                    -> Result<Bytes, Error> {

    let mut body = http_client
        .get_http_response_using_headers(url, http_header)
        .unwrap();
    if monothreading && !body.check_partialcontent_status() {
        return Err(Error::Status);
    }
    let mut bytes_buffer = [0; DOWNLOAD_BUFFER_BYTES];
    let mut sum_bytes = 0;

    let progress_update_interval = Duration::from_millis(PROGRESS_UPDATE_INTERVAL_MILLIS);
    let mut last_progress_bytes = 0;
    let mut last_progress_time = Instant::now() - progress_update_interval;

    while let Ok(n) = body.read(&mut bytes_buffer) {
        if n == 0 {
            return Ok(sum_bytes);
        }

        chunk_writer.write(sum_bytes, &bytes_buffer[0..n]);

        sum_bytes += n as u64;

        // Update the CLI
        if Instant::now().duration_since(last_progress_time) > progress_update_interval {
            last_progress_time = Instant::now();
            let progress_bytes_delta = sum_bytes - last_progress_bytes;
            last_progress_bytes = sum_bytes;
            mpb.add(progress_bytes_delta);
        }
    }
    mpb.add(sum_bytes - last_progress_bytes);
    return Ok(0u64);
}

/// Function to download each chunk of a remote content (given by its URL).
/// This function takes as parameters:
/// * the remote content length,
/// * a mutable reference to share between threads, which contains each chunk,
/// * the number of chunks that contains the remote content,
/// * the URL of the remote content server,
/// * a custom authorization to access and download the remote content.
pub fn download_chunks(cargo_info: CargoInfo,
                       mut out_file: OutputFileWriter,
                       nb_chunks: u64,
                       url: &str)
                       -> bool {
    let (content_length, auth_header_factory) = (cargo_info.content_length, cargo_info.auth_header);

    let global_chunk_length: u64 = (content_length / nb_chunks) + 1;
    let mut jobs = vec![];

    let mut mpb = MultiBar::new();
    mpb.println(&format!("Downloading {} chunks: ", nb_chunks));

    for chunk_index in 0..nb_chunks {

        let (mut http_header, RangeBytes(chunk_offset, chunk_length)) =
            get_header_from_index(chunk_index, content_length, global_chunk_length).unwrap();
        let hyper_client = Client::new();
        let url_clone = String::from(url);
        if let Some(auth_header_factory) = auth_header_factory.clone() {
            http_header.set(auth_header_factory.build_header());
        }
        let monothreading = cargo_info.accept_partialcontent;

        // Initialize the progress bar for that chunk
        initbar!(mp, mpb, chunk_length, chunk_index);

        let chunk_writer = out_file.get_chunk_writer(chunk_offset);

        // In this work, we push a boolean value to know if the chunk is OK
        jobs.push(thread::spawn(move || match download_a_chunk(&hyper_client,
                                                               http_header,
                                                               chunk_writer,
                                                               &url_clone,
                                                               &mut mp,
                                                               monothreading) {
                                    Ok(bytes_written) => {
            mp.finish();
            if bytes_written == 0 {
                println!("The downloaded chunk {} is empty", chunk_index);
            }
            return true;
        }
                                    Err(error) => {
            mp.finish();
            println!("Cannot download the chunk {}, due to error {}",
                     chunk_index,
                     error);
            return false;
        }
                                }));
    }

    mpb.listen();

    // Contain the result state for chunks
    let mut child_results: Vec<bool> = Vec::with_capacity(nb_chunks as usize);

    for child in jobs {
        match child.join() {
            Ok(b) => child_results.push(b),
            Err(_) => child_results.push(false),
        }
    }

    // Check if all chunks are OK
    return child_results.iter().all(|x| *x);
}

#[cfg(test)]
mod test_chunk_length {

    use super::get_chunk_length;
    use super::RangeBytes;

    #[test]
    fn wrong_content_length_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 15, 0));
    }

    #[test]
    fn wrong_global_chunk_length_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 0, 15));
    }

    #[test]
    fn wrong_length_parameters_should_return_none() {
        assert_eq!(None, get_chunk_length(0, 0, 0));
    }

    #[test]
    fn get_the_first_range_in_chunk() {
        assert_eq!(Some(RangeBytes(0, 249)), get_chunk_length(0, 1000, 250));
    }

    #[test]
    fn get_the_last_range_in_chunk() {
        assert_eq!(Some(RangeBytes(750, 999)), get_chunk_length(3, 1000, 250));
    }

    #[test]
    fn get_the_last_range_in_shorten_chunk() {
        assert_eq!(Some(RangeBytes(750, 997)), get_chunk_length(3, 998, 250));
    }

    #[test]
    fn wrong_index_parameter_should_return_none() {
        assert_eq!(None, get_chunk_length(4, 1000, 250));
    }

}

#[cfg(test)]
mod test_header {

    use super::{get_header_from_index, RangeBytes};
    use hyper::header::{ByteRangeSpec, Headers, Range};

    #[test]
    fn wrong_chunk_length_should_return_none() {
        assert_eq!(None, get_header_from_index(0, 0, 0));
    }

    #[test]
    fn good_chunk_length_should_return_a_good_header() {
        let mut test_header = Headers::new();
        test_header.set(Range::Bytes(vec![ByteRangeSpec::FromTo(750, 997)]));
        assert_eq!(Some((test_header, RangeBytes(750, 247))),
                   get_header_from_index(3, 998, 250));
    }

}
<|endoftext|>


<commit_before>use serde::Deserialize;
use std::fmt::{self, Display, Formatter};
use std::fs::{remove_file};
use std::path::{PathBuf};
use std::process::{self, Command, Output};

const RUSTC_COLOR_ARGS: &[&str] = &["--color", "always"];

fn temp_file() -> String {
    format!("./temp_{}", process::id())
}

#[derive(Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Mode {
    Compile,
    Test,
}

#[derive(Deserialize)]
pub struct ExerciseList {
    pub exercises: Vec<Exercise>,
}

#[derive(Deserialize)]
pub struct Exercise {
    pub path: PathBuf,
    pub mode: Mode,
}

impl Exercise {
    pub fn compile(&self) -> Output {
        match self.mode {
            Mode::Compile => Command::new("rustc")
                .args(&[self.path.to_str().unwrap(), "-o", &temp_file()])
                .args(RUSTC_COLOR_ARGS)
                .output(),
            Mode::Test => Command::new("rustc")
                .args(&["--test", self.path.to_str().unwrap(), "-o", &temp_file()])
                .args(RUSTC_COLOR_ARGS)
                .args(&["--" "--nocapture"])
                .output(),
        }
        .expect("Failed to run 'compile' command.")
    }

    pub fn run(&self) -> Output {
        Command::new(&temp_file())
            .output()
            .expect("Failed to run 'run' command")
    }

    pub fn clean(&self) {
        let _ignored = remove_file(&temp_file());
    }
}

impl Display for Exercise {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "{}", self.path.to_str().unwrap())
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::path::Path;
    use std::fs::File;

    #[test]
    fn test_clean() {
        File::create(&temp_file()).unwrap();
        let exercise = Exercise {
            path: PathBuf::from("example.rs"),
            mode: Mode::Test,
        };
        exercise.clean();
        assert!(!Path::new(&temp_file()).exists());
    }
}
<commit_msg>damn it<commit_after>use serde::Deserialize;
use std::fmt::{self, Display, Formatter};
use std::fs::{remove_file};
use std::path::{PathBuf};
use std::process::{self, Command, Output};

const RUSTC_COLOR_ARGS: &[&str] = &["--color", "always"];

fn temp_file() -> String {
    format!("./temp_{}", process::id())
}

#[derive(Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Mode {
    Compile,
    Test,
}

#[derive(Deserialize)]
pub struct ExerciseList {
    pub exercises: Vec<Exercise>,
}

#[derive(Deserialize)]
pub struct Exercise {
    pub path: PathBuf,
    pub mode: Mode,
}

impl Exercise {
    pub fn compile(&self) -> Output {
        match self.mode {
            Mode::Compile => Command::new("rustc")
                .args(&[self.path.to_str().unwrap(), "-o", &temp_file()])
                .args(RUSTC_COLOR_ARGS)
                .output(),
            Mode::Test => Command::new("rustc")
                .args(&["--test", self.path.to_str().unwrap(), "-o", &temp_file()])
                .args(RUSTC_COLOR_ARGS)
                .args(&["--", "--nocapture"])
                .output(),
        }
        .expect("Failed to run 'compile' command.")
    }

    pub fn run(&self) -> Output {
        Command::new(&temp_file())
            .output()
            .expect("Failed to run 'run' command")
    }

    pub fn clean(&self) {
        let _ignored = remove_file(&temp_file());
    }
}

impl Display for Exercise {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "{}", self.path.to_str().unwrap())
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::path::Path;
    use std::fs::File;

    #[test]
    fn test_clean() {
        File::create(&temp_file()).unwrap();
        let exercise = Exercise {
            path: PathBuf::from("example.rs"),
            mode: Mode::Test,
        };
        exercise.clean();
        assert!(!Path::new(&temp_file()).exists());
    }
}
<|endoftext|>


<commit_before>extern crate reqwest;

pub mod statistics;
mod scrape;

use config::Config;
use failure::{Error, ResultExt};
use std::io::Read;

#[derive(Debug, Clone)]
pub struct State {
    pub id: u32,
    pub abbrev: String,
}

impl State {
    pub fn new(id: u32, abbrev: String) -> State {
        State { id, abbrev }
    }
}

#[derive(Debug)]
pub struct Feed {
    pub id: u32,
    pub name: String,
    pub listeners: u32,
    pub state: State,
    pub county: String,
    pub alert: Option<String>,
}

impl Feed {
    pub fn download_and_scrape(config: &Config) -> Result<Vec<Feed>, Error> {
        let client = reqwest::Client::new();

        let mut feeds = FeedSource::Top.download_and_scrape(&client)?;

        if let Some(state_id) = config.misc.state_feeds_id {
            let state = State::new(state_id, "CS".into()); // CS = Config Specified
            let state_feeds = FeedSource::State(state).download_and_scrape(&client)?;

            feeds.extend(state_feeds);
        }

        filter_whitelist_blacklist(config, &mut feeds);

        feeds.sort_by_key(|feed| feed.id);
        feeds.dedup();

        Ok(feeds)
    }
}

impl PartialEq for Feed {
    fn eq(&self, other: &Feed) -> bool {
        self.id == other.id
    }
}

fn filter_whitelist_blacklist(config: &Config, feeds: &mut Vec<Feed>) {
    if !config.whitelist.is_empty() {
        feeds.retain(|feed| {
            config
                .whitelist
                .iter()
                .any(|entry| entry.matches_feed(feed))
        });
    }

    if !config.blacklist.is_empty() {
        feeds.retain(|feed| {
            config
                .blacklist
                .iter()
                .any(|entry| !entry.matches_feed(feed))
        });
    }
}

// TODO: move to scrape module (?)
#[derive(Fail, Debug)]
pub enum FeedSourceError {
    #[fail(display = "failed to parse top feeds")] FailedToParseTopFeeds,
    #[fail(display = "failed to parse state feeds")] FailedToParseStateFeeds,
}

enum FeedSource {
    Top,
    State(State),
}

impl FeedSource {
    fn get_url(&self) -> String {
        match *self {
            FeedSource::Top => "http://broadcastify.com/listen/top".into(),
            FeedSource::State(ref state) => {
                format!("http://broadcastify.com/listen/stid/{}", state.id)
            }
        }
    }

    fn download_page(&self, client: &reqwest::Client) -> Result<String, Error> {
        let mut resp = client.get(&self.get_url()).send()?;
        let mut body = String::new();

        resp.read_to_string(&mut body)?;

        Ok(body)
    }

    fn scrape(&self, body: &str) -> Result<Vec<Feed>, Error> {
        match *self {
            FeedSource::Top => {
                let scraped =
                    scrape::scrape_top(body).context(FeedSourceError::FailedToParseTopFeeds)?;

                Ok(scraped)
            }
            FeedSource::State(ref state) => {
                let scraped = scrape::scrape_state(state, body)
                    .context(FeedSourceError::FailedToParseStateFeeds)?;

                Ok(scraped)
            }
        }
    }

    fn download_and_scrape(&self, client: &reqwest::Client) -> Result<Vec<Feed>, Error> {
        let body = self.download_page(client)?;
        self.scrape(&body)
    }
}
<commit_msg>Fix requests to Broadcastify returning 404<commit_after>extern crate reqwest;

pub mod statistics;
mod scrape;

use config::Config;
use failure::{Error, ResultExt};
use std::io::Read;

#[derive(Debug, Clone)]
pub struct State {
    pub id: u32,
    pub abbrev: String,
}

impl State {
    pub fn new(id: u32, abbrev: String) -> State {
        State { id, abbrev }
    }
}

#[derive(Debug)]
pub struct Feed {
    pub id: u32,
    pub name: String,
    pub listeners: u32,
    pub state: State,
    pub county: String,
    pub alert: Option<String>,
}

impl Feed {
    pub fn download_and_scrape(config: &Config) -> Result<Vec<Feed>, Error> {
        let client = reqwest::Client::new();

        let mut feeds = FeedSource::Top.download_and_scrape(&client)?;

        if let Some(state_id) = config.misc.state_feeds_id {
            let state = State::new(state_id, "CS".into()); // CS = Config Specified
            let state_feeds = FeedSource::State(state).download_and_scrape(&client)?;

            feeds.extend(state_feeds);
        }

        filter_whitelist_blacklist(config, &mut feeds);

        feeds.sort_by_key(|feed| feed.id);
        feeds.dedup();

        Ok(feeds)
    }
}

impl PartialEq for Feed {
    fn eq(&self, other: &Feed) -> bool {
        self.id == other.id
    }
}

fn filter_whitelist_blacklist(config: &Config, feeds: &mut Vec<Feed>) {
    if !config.whitelist.is_empty() {
        feeds.retain(|feed| {
            config
                .whitelist
                .iter()
                .any(|entry| entry.matches_feed(feed))
        });
    }

    if !config.blacklist.is_empty() {
        feeds.retain(|feed| {
            config
                .blacklist
                .iter()
                .any(|entry| !entry.matches_feed(feed))
        });
    }
}

// TODO: move to scrape module (?)
#[derive(Fail, Debug)]
pub enum FeedSourceError {
    #[fail(display = "failed to parse top feeds")] FailedToParseTopFeeds,
    #[fail(display = "failed to parse state feeds")] FailedToParseStateFeeds,
}

enum FeedSource {
    Top,
    State(State),
}

impl FeedSource {
    fn get_url(&self) -> String {
        match *self {
            FeedSource::Top => "http://www.broadcastify.com/listen/top".into(),
            FeedSource::State(ref state) => {
                format!("http://www.broadcastify.com/listen/stid/{}", state.id)
            }
        }
    }

    fn download_page(&self, client: &reqwest::Client) -> Result<String, Error> {
        let mut resp = client.get(&self.get_url()).send()?;
        let mut body = String::new();

        resp.read_to_string(&mut body)?;

        Ok(body)
    }

    fn scrape(&self, body: &str) -> Result<Vec<Feed>, Error> {
        match *self {
            FeedSource::Top => {
                let scraped =
                    scrape::scrape_top(body).context(FeedSourceError::FailedToParseTopFeeds)?;

                Ok(scraped)
            }
            FeedSource::State(ref state) => {
                let scraped = scrape::scrape_state(state, body)
                    .context(FeedSourceError::FailedToParseStateFeeds)?;

                Ok(scraped)
            }
        }
    }

    fn download_and_scrape(&self, client: &reqwest::Client) -> Result<Vec<Feed>, Error> {
        let body = self.download_page(client)?;
        self.scrape(&body)
    }
}
<|endoftext|>


<commit_before>extern crate find_folder;

use screen;
use screen::Screen;

use piston_window;

type Screens = Vec<Box<screen::Screen>>;

pub struct Game {
    window: piston_window::PistonWindow,
    context: screen::Context,
    screens: Screens,
}

impl Game {
    pub fn new() -> Game {
        let window: piston_window::PistonWindow = piston_window::WindowSettings::new("rrthozopsi", [600, 600])
                                                      .exit_on_esc(true)
                                                      .build()
                                                      .unwrap();
        let mut context = screen::Context {
            assets_path: find_folder::Search::KidsThenParents(3, 5).for_folder("assets").unwrap(),
        };
        let screens: Screens = vec![Box::new(screen::MainMenuScreen::new(&window, &mut context))];
        Game {
            window: window,
            screens: screens,
            context: context,
        }
    }

    pub fn run(&mut self) {
        use piston_window::Event;

        let screens = &mut self.screens;

        // TODO: Remove clone?
        for window in &mut self.window {
            match window.event {
                Some(Event::Update(ref args)) => {
                    last(screens).on_update(&args);
                }
                Some(Event::Render(ref args)) => {
                    last(screens).on_draw(&args, &window);
                }
                Some(Event::Input(ref input)) => {
                    handle_input(screens, &input, &window, &mut self.context);
                }
                _ => {}
            }
        }
    }
}

fn last<'a>(vec: &'a mut Screens) -> &'a mut Box<screen::Screen> {
    vec.last_mut().unwrap()
}

fn handle_input(screens: &mut Screens,
                input: &piston_window::Input,
                window: &piston_window::PistonWindow,
                context: &mut screen::Context) {
    for action in last(screens).on_input(&input, &window, context) {
        match action {
            screen::InputResult::PushScreen(new_screen) => {
                screens.push(new_screen);
            }
            screen::InputResult::PopScreen => {
                screens.pop();
            }
        }
    }
}
<commit_msg>Handle ExitGame<commit_after>extern crate find_folder;

use screen;
use screen::Screen;

use piston_window;

type Screens = Vec<Box<screen::Screen>>;

pub struct Game {
    window: piston_window::PistonWindow,
    context: screen::Context,
    screens: Screens,
}

impl Game {
    pub fn new() -> Game {
        let window: piston_window::PistonWindow = piston_window::WindowSettings::new("rrthozopsi", [600, 600])
                                                      .exit_on_esc(true)
                                                      .build()
                                                      .unwrap();
        let mut context = screen::Context {
            assets_path: find_folder::Search::KidsThenParents(3, 5).for_folder("assets").unwrap(),
        };
        let screens: Screens = vec![Box::new(screen::MainMenuScreen::new(&window, &mut context))];
        Game {
            window: window,
            screens: screens,
            context: context,
        }
    }

    pub fn run(&mut self) {
        use piston_window::Event;

        let screens = &mut self.screens;

        // TODO: Remove clone?
        for window in &mut self.window {
            match window.event {
                Some(Event::Update(ref args)) => {
                    last(screens).on_update(&args);
                }
                Some(Event::Render(ref args)) => {
                    last(screens).on_draw(&args, &window);
                }
                Some(Event::Input(ref input)) => {
                    handle_input(screens, &input, &window, &mut self.context);
                }
                _ => {}
            }
        }
    }
}

fn last<'a>(vec: &'a mut Screens) -> &'a mut Box<screen::Screen> {
    vec.last_mut().unwrap()
}

fn handle_input(screens: &mut Screens,
                input: &piston_window::Input,
                window: &piston_window::PistonWindow,
                context: &mut screen::Context) {
    for action in last(screens).on_input(&input, &window, context) {
        match action {
            screen::InputResult::PushScreen(new_screen) => {
                screens.push(new_screen);
            }
            screen::InputResult::PopScreen => {
                screens.pop();
            }
            screen::InputResult::ExitGame => {}
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use base::CGFloat;

#[repr(C)]
pub struct CGSize {
    pub width: CGFloat,
    pub height: CGFloat,
}

#[repr(C)]
pub struct CGPoint {
    pub x: CGFloat,
    pub y: CGFloat,
}

#[repr(C)]
pub struct CGRect {
    pub origin: CGPoint,
    pub size: CGSize
}
<commit_msg>geometry: Make `CGPoint`, `CGRect`, and `CGSize` copyable and clonable.<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use base::CGFloat;

#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct CGSize {
    pub width: CGFloat,
    pub height: CGFloat,
}

#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct CGPoint {
    pub x: CGFloat,
    pub y: CGFloat,
}

#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct CGRect {
    pub origin: CGPoint,
    pub size: CGSize
}
<|endoftext|>