<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/. */

//! The high-level interface from script to constellation. Using this abstract interface helps
//! reduce coupling between these two components.

use geom::rect::Rect;
use geom::size::TypedSize2D;
use geom::scale_factor::ScaleFactor;
use hyper::header::Headers;
use hyper::method::Method;
use layers::geometry::DevicePixel;
use util::cursor::Cursor;
use util::geometry::{PagePx, ViewportPx};
use std::sync::mpsc::{channel, Sender, Receiver};
use url::Url;

#[derive(Clone)]
pub struct ConstellationChan(pub Sender<Msg>);

impl ConstellationChan {
    pub fn new() -> (Receiver<Msg>, ConstellationChan) {
        let (chan, port) = channel();
        (port, ConstellationChan(chan))
    }
}

#[derive(PartialEq, Eq, Copy, Debug)]
pub enum IFrameSandboxState {
    IFrameSandboxed,
    IFrameUnsandboxed
}

// We pass this info to various tasks, so it lives in a separate, cloneable struct.
#[derive(Clone, Copy)]
pub struct Failure {
    pub pipeline_id: PipelineId,
    pub parent_info: Option<(PipelineId, SubpageId)>,
}

#[derive(Copy)]
pub struct WindowSizeData {
    /// The size of the initial layout viewport, before parsing an
    /// http://www.w3.org/TR/css-device-adapt/#initial-viewport
    pub initial_viewport: TypedSize2D<ViewportPx, f32>,

    /// The "viewing area" in page px. See `PagePx` documentation for details.
    pub visible_viewport: TypedSize2D<PagePx, f32>,

    /// The resolution of the window in dppx, not including any "pinch zoom" factor.
    pub device_pixel_ratio: ScaleFactor<ViewportPx, DevicePixel, f32>,
}

#[derive(PartialEq, Eq, Copy, Clone)]
pub enum KeyState {
    Pressed,
    Released,
    Repeated,
}

//N.B. Based on the glutin key enum
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum Key {
    Space,
    Apostrophe,
    Comma,
    Minus,
    Period,
    Slash,
    Num0,
    Num1,
    Num2,
    Num3,
    Num4,
    Num5,
    Num6,
    Num7,
    Num8,
    Num9,
    Semicolon,
    Equal,
    A,
    B,
    C,
    D,
    E,
    F,
    G,
    H,
    I,
    J,
    K,
    L,
    M,
    N,
    O,
    P,
    Q,
    R,
    S,
    T,
    U,
    V,
    W,
    X,
    Y,
    Z,
    LeftBracket,
    Backslash,
    RightBracket,
    GraveAccent,
    World1,
    World2,

    Escape,
    Enter,
    Tab,
    Backspace,
    Insert,
    Delete,
    Right,
    Left,
    Down,
    Up,
    PageUp,
    PageDown,
    Home,
    End,
    CapsLock,
    ScrollLock,
    NumLock,
    PrintScreen,
    Pause,
    F1,
    F2,
    F3,
    F4,
    F5,
    F6,
    F7,
    F8,
    F9,
    F10,
    F11,
    F12,
    F13,
    F14,
    F15,
    F16,
    F17,
    F18,
    F19,
    F20,
    F21,
    F22,
    F23,
    F24,
    F25,
    Kp0,
    Kp1,
    Kp2,
    Kp3,
    Kp4,
    Kp5,
    Kp6,
    Kp7,
    Kp8,
    Kp9,
    KpDecimal,
    KpDivide,
    KpMultiply,
    KpSubtract,
    KpAdd,
    KpEnter,
    KpEqual,
    LeftShift,
    LeftControl,
    LeftAlt,
    LeftSuper,
    RightShift,
    RightControl,
    RightAlt,
    RightSuper,
    Menu,
}

bitflags! {
    flags KeyModifiers: u8 {
        const SHIFT = 0x01,
        const CONTROL = 0x02,
        const ALT = 0x04,
        const SUPER = 0x08,
    }
}

/// Messages from the compositor and script to the constellation.
pub enum Msg {
    Exit,
    Failure(Failure),
    InitLoadUrl(Url),
    LoadComplete,
    FrameRect(PipelineId, SubpageId, Rect<f32>),
    LoadUrl(PipelineId, LoadData),
    ScriptLoadedURLInIFrame(Url, PipelineId, SubpageId, Option<SubpageId>, IFrameSandboxState),
    Navigate(Option<(PipelineId, SubpageId)>, NavigationDirection),
    PainterReady(PipelineId),
    ResizedWindow(WindowSizeData),
    KeyEvent(Key, KeyState, KeyModifiers),
    /// Requests that the constellation inform the compositor of the title of the pipeline
    /// immediately.
    GetPipelineTitle(PipelineId),
    /// Requests that the constellation inform the compositor of the a cursor change.
    SetCursor(Cursor),
    /// Dispatch a mozbrowser event to a given iframe. Only available in experimental mode.
    MozBrowserEventMsg(PipelineId, SubpageId, MozBrowserEvent),
}

// https://developer.mozilla.org/en-US/docs/Web/API/Using_the_Browser_API#Events
pub enum MozBrowserEvent {
    /// Sent when the scroll position within a browser <iframe> changes.
    AsyncScroll,
    /// Sent when window.close() is called within a browser <iframe>.
    Close,
    /// Sent when a browser <iframe> tries to open a context menu. This allows  handling <menuitem> element available within the browser <iframe>'s content.
    ContextMenu,
    /// Sent when an error occurred while trying to load content within a browser <iframe>.
    Error,
    /// Sent when the favicon of a browser <iframe> changes.
    IconChange,
    /// Sent when the browser <iframe> has finished loading all its assets.
    LoadEnd,
    /// Sent when the browser <iframe> starts to load a new page.
    LoadStart,
    /// Sent when a browser <iframe>'s location changes.
    LocationChange(String),
    /// Sent when window.open() is called within a browser <iframe>.
    OpenWindow,
    /// Sent when the SSL state changes within a browser <iframe>.
    SecurityChange,
    /// Sent when alert(), confirm(), or prompt() is called within a browser <iframe>.
    ShowModalPrompt,
    /// Sent when the document.title changes within a browser <iframe>.
    TitleChange(String),
    /// Sent when an HTTP authentification is requested.
    UsernameAndPasswordRequired,
    /// Sent when a link to a search engine is found.
    OpenSearch,
}

impl MozBrowserEvent {
    pub fn name(&self) -> &'static str {
        match *self {
            MozBrowserEvent::AsyncScroll => "mozbrowserasyncscroll",
            MozBrowserEvent::Close => "mozbrowserclose",
            MozBrowserEvent::ContextMenu => "mozbrowsercontextmenu",
            MozBrowserEvent::Error => "mozbrowsererror",
            MozBrowserEvent::IconChange => "mozbrowsericonchange",
            MozBrowserEvent::LoadEnd => "mozbrowserloadend",
            MozBrowserEvent::LoadStart => "mozbrowserloadstart",
            MozBrowserEvent::LocationChange(_) => "mozbrowserlocationchange",
            MozBrowserEvent::OpenWindow => "mozbrowseropenwindow",
            MozBrowserEvent::SecurityChange => "mozbrowsersecuritychange",
            MozBrowserEvent::ShowModalPrompt => "mozbrowsershowmodalprompt",
            MozBrowserEvent::TitleChange(_) => "mozbrowsertitlechange",
            MozBrowserEvent::UsernameAndPasswordRequired => "mozbrowserusernameandpasswordrequired",
            MozBrowserEvent::OpenSearch => "mozbrowseropensearch"
        }
    }
    pub fn detail(&self) -> Option<String> {
        match *self {
            MozBrowserEvent::AsyncScroll | MozBrowserEvent::Close | MozBrowserEvent::ContextMenu |
            MozBrowserEvent::Error | MozBrowserEvent::IconChange | MozBrowserEvent::LoadEnd |
            MozBrowserEvent::LoadStart | MozBrowserEvent::OpenWindow | MozBrowserEvent::SecurityChange |
            MozBrowserEvent::ShowModalPrompt | MozBrowserEvent::UsernameAndPasswordRequired | MozBrowserEvent::OpenSearch => None,
            MozBrowserEvent::LocationChange(ref new_location) => Some(new_location.clone()),
            MozBrowserEvent::TitleChange(ref new_title) => Some(new_title.clone()),
        }
    }
}

/// Similar to net::resource_task::LoadData
/// can be passed to LoadUrl to load a page with GET/POST
/// parameters or headers
#[derive(Clone)]
pub struct LoadData {
    pub url: Url,
    pub method: Method,
    pub headers: Headers,
    pub data: Option<Vec<u8>>,
}

impl LoadData {
    pub fn new(url: Url) -> LoadData {
        LoadData {
            url: url,
            method: Method::Get,
            headers: Headers::new(),
            data: None,
        }
    }
}

#[derive(Clone, PartialEq, Eq, Copy, Hash, Debug)]
pub enum NavigationDirection {
    Forward,
    Back,
}

#[derive(Clone, PartialEq, Eq, Copy, Hash, Debug)]
pub struct FrameId(pub u32);

#[derive(Clone, PartialEq, Eq, Copy, Hash, Debug)]
pub struct WorkerId(pub uint);

#[derive(Clone, PartialEq, Eq, Copy, Hash, Debug)]
pub struct PipelineId(pub uint);

#[derive(Clone, PartialEq, Eq, Copy, Hash, Debug)]
pub struct SubpageId(pub uint);

// The type of pipeline exit. During complete shutdowns, pipelines do not have to
// release resources automatically released on process termination.
#[derive(Copy)]
pub enum PipelineExitType {
    PipelineOnly,
    Complete,
}
<commit_msg>Use u32 for WorkerId.<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/. */

//! The high-level interface from script to constellation. Using this abstract interface helps
//! reduce coupling between these two components.

use geom::rect::Rect;
use geom::size::TypedSize2D;
use geom::scale_factor::ScaleFactor;
use hyper::header::Headers;
use hyper::method::Method;
use layers::geometry::DevicePixel;
use util::cursor::Cursor;
use util::geometry::{PagePx, ViewportPx};
use std::sync::mpsc::{channel, Sender, Receiver};
use url::Url;

#[derive(Clone)]
pub struct ConstellationChan(pub Sender<Msg>);

impl ConstellationChan {
    pub fn new() -> (Receiver<Msg>, ConstellationChan) {
        let (chan, port) = channel();
        (port, ConstellationChan(chan))
    }
}

#[derive(PartialEq, Eq, Copy, Debug)]
pub enum IFrameSandboxState {
    IFrameSandboxed,
    IFrameUnsandboxed
}

// We pass this info to various tasks, so it lives in a separate, cloneable struct.
#[derive(Clone, Copy)]
pub struct Failure {
    pub pipeline_id: PipelineId,
    pub parent_info: Option<(PipelineId, SubpageId)>,
}

#[derive(Copy)]
pub struct WindowSizeData {
    /// The size of the initial layout viewport, before parsing an
    /// http://www.w3.org/TR/css-device-adapt/#initial-viewport
    pub initial_viewport: TypedSize2D<ViewportPx, f32>,

    /// The "viewing area" in page px. See `PagePx` documentation for details.
    pub visible_viewport: TypedSize2D<PagePx, f32>,

    /// The resolution of the window in dppx, not including any "pinch zoom" factor.
    pub device_pixel_ratio: ScaleFactor<ViewportPx, DevicePixel, f32>,
}

#[derive(PartialEq, Eq, Copy, Clone)]
pub enum KeyState {
    Pressed,
    Released,
    Repeated,
}

//N.B. Based on the glutin key enum
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum Key {
    Space,
    Apostrophe,
    Comma,
    Minus,
    Period,
    Slash,
    Num0,
    Num1,
    Num2,
    Num3,
    Num4,
    Num5,
    Num6,
    Num7,
    Num8,
    Num9,
    Semicolon,
    Equal,
    A,
    B,
    C,
    D,
    E,
    F,
    G,
    H,
    I,
    J,
    K,
    L,
    M,
    N,
    O,
    P,
    Q,
    R,
    S,
    T,
    U,
    V,
    W,
    X,
    Y,
    Z,
    LeftBracket,
    Backslash,
    RightBracket,
    GraveAccent,
    World1,
    World2,

    Escape,
    Enter,
    Tab,
    Backspace,
    Insert,
    Delete,
    Right,
    Left,
    Down,
    Up,
    PageUp,
    PageDown,
    Home,
    End,
    CapsLock,
    ScrollLock,
    NumLock,
    PrintScreen,
    Pause,
    F1,
    F2,
    F3,
    F4,
    F5,
    F6,
    F7,
    F8,
    F9,
    F10,
    F11,
    F12,
    F13,
    F14,
    F15,
    F16,
    F17,
    F18,
    F19,
    F20,
    F21,
    F22,
    F23,
    F24,
    F25,
    Kp0,
    Kp1,
    Kp2,
    Kp3,
    Kp4,
    Kp5,
    Kp6,
    Kp7,
    Kp8,
    Kp9,
    KpDecimal,
    KpDivide,
    KpMultiply,
    KpSubtract,
    KpAdd,
    KpEnter,
    KpEqual,
    LeftShift,
    LeftControl,
    LeftAlt,
    LeftSuper,
    RightShift,
    RightControl,
    RightAlt,
    RightSuper,
    Menu,
}

bitflags! {
    flags KeyModifiers: u8 {
        const SHIFT = 0x01,
        const CONTROL = 0x02,
        const ALT = 0x04,
        const SUPER = 0x08,
    }
}

/// Messages from the compositor and script to the constellation.
pub enum Msg {
    Exit,
    Failure(Failure),
    InitLoadUrl(Url),
    LoadComplete,
    FrameRect(PipelineId, SubpageId, Rect<f32>),
    LoadUrl(PipelineId, LoadData),
    ScriptLoadedURLInIFrame(Url, PipelineId, SubpageId, Option<SubpageId>, IFrameSandboxState),
    Navigate(Option<(PipelineId, SubpageId)>, NavigationDirection),
    PainterReady(PipelineId),
    ResizedWindow(WindowSizeData),
    KeyEvent(Key, KeyState, KeyModifiers),
    /// Requests that the constellation inform the compositor of the title of the pipeline
    /// immediately.
    GetPipelineTitle(PipelineId),
    /// Requests that the constellation inform the compositor of the a cursor change.
    SetCursor(Cursor),
    /// Dispatch a mozbrowser event to a given iframe. Only available in experimental mode.
    MozBrowserEventMsg(PipelineId, SubpageId, MozBrowserEvent),
}

// https://developer.mozilla.org/en-US/docs/Web/API/Using_the_Browser_API#Events
pub enum MozBrowserEvent {
    /// Sent when the scroll position within a browser <iframe> changes.
    AsyncScroll,
    /// Sent when window.close() is called within a browser <iframe>.
    Close,
    /// Sent when a browser <iframe> tries to open a context menu. This allows  handling <menuitem> element available within the browser <iframe>'s content.
    ContextMenu,
    /// Sent when an error occurred while trying to load content within a browser <iframe>.
    Error,
    /// Sent when the favicon of a browser <iframe> changes.
    IconChange,
    /// Sent when the browser <iframe> has finished loading all its assets.
    LoadEnd,
    /// Sent when the browser <iframe> starts to load a new page.
    LoadStart,
    /// Sent when a browser <iframe>'s location changes.
    LocationChange(String),
    /// Sent when window.open() is called within a browser <iframe>.
    OpenWindow,
    /// Sent when the SSL state changes within a browser <iframe>.
    SecurityChange,
    /// Sent when alert(), confirm(), or prompt() is called within a browser <iframe>.
    ShowModalPrompt,
    /// Sent when the document.title changes within a browser <iframe>.
    TitleChange(String),
    /// Sent when an HTTP authentification is requested.
    UsernameAndPasswordRequired,
    /// Sent when a link to a search engine is found.
    OpenSearch,
}

impl MozBrowserEvent {
    pub fn name(&self) -> &'static str {
        match *self {
            MozBrowserEvent::AsyncScroll => "mozbrowserasyncscroll",
            MozBrowserEvent::Close => "mozbrowserclose",
            MozBrowserEvent::ContextMenu => "mozbrowsercontextmenu",
            MozBrowserEvent::Error => "mozbrowsererror",
            MozBrowserEvent::IconChange => "mozbrowsericonchange",
            MozBrowserEvent::LoadEnd => "mozbrowserloadend",
            MozBrowserEvent::LoadStart => "mozbrowserloadstart",
            MozBrowserEvent::LocationChange(_) => "mozbrowserlocationchange",
            MozBrowserEvent::OpenWindow => "mozbrowseropenwindow",
            MozBrowserEvent::SecurityChange => "mozbrowsersecuritychange",
            MozBrowserEvent::ShowModalPrompt => "mozbrowsershowmodalprompt",
            MozBrowserEvent::TitleChange(_) => "mozbrowsertitlechange",
            MozBrowserEvent::UsernameAndPasswordRequired => "mozbrowserusernameandpasswordrequired",
            MozBrowserEvent::OpenSearch => "mozbrowseropensearch"
        }
    }
    pub fn detail(&self) -> Option<String> {
        match *self {
            MozBrowserEvent::AsyncScroll | MozBrowserEvent::Close | MozBrowserEvent::ContextMenu |
            MozBrowserEvent::Error | MozBrowserEvent::IconChange | MozBrowserEvent::LoadEnd |
            MozBrowserEvent::LoadStart | MozBrowserEvent::OpenWindow | MozBrowserEvent::SecurityChange |
            MozBrowserEvent::ShowModalPrompt | MozBrowserEvent::UsernameAndPasswordRequired | MozBrowserEvent::OpenSearch => None,
            MozBrowserEvent::LocationChange(ref new_location) => Some(new_location.clone()),
            MozBrowserEvent::TitleChange(ref new_title) => Some(new_title.clone()),
        }
    }
}

/// Similar to net::resource_task::LoadData
/// can be passed to LoadUrl to load a page with GET/POST
/// parameters or headers
#[derive(Clone)]
pub struct LoadData {
    pub url: Url,
    pub method: Method,
    pub headers: Headers,
    pub data: Option<Vec<u8>>,
}

impl LoadData {
    pub fn new(url: Url) -> LoadData {
        LoadData {
            url: url,
            method: Method::Get,
            headers: Headers::new(),
            data: None,
        }
    }
}

#[derive(Clone, PartialEq, Eq, Copy, Hash, Debug)]
pub enum NavigationDirection {
    Forward,
    Back,
}

#[derive(Clone, PartialEq, Eq, Copy, Hash, Debug)]
pub struct FrameId(pub u32);

#[derive(Clone, PartialEq, Eq, Copy, Hash, Debug)]
pub struct WorkerId(pub u32);

#[derive(Clone, PartialEq, Eq, Copy, Hash, Debug)]
pub struct PipelineId(pub uint);

#[derive(Clone, PartialEq, Eq, Copy, Hash, Debug)]
pub struct SubpageId(pub uint);

// The type of pipeline exit. During complete shutdowns, pipelines do not have to
// release resources automatically released on process termination.
#[derive(Copy)]
pub enum PipelineExitType {
    PipelineOnly,
    Complete,
}
<|endoftext|>


<commit_before>use std::fmt::{Debug, Display, Formatter};
use std::fmt;

use clap::ArgMatches;

use runtime::Runtime;
use module::Module;

pub struct BM<'a> {
    rt: &'a Runtime<'a>,
}

impl<'a> BM<'a> {

    pub fn new(rt: &'a Runtime<'a>) -> BM<'a> {
        BM {
            rt: rt,
        }
    }

    fn runtime(&self) -> &Runtime {
        &self.rt
    }

    fn command_add(&self) -> bool {
        unimplemented!()
    }

    fn command_list(&self) -> bool {
        unimplemented!()
    }

    fn command_remove(&self) -> bool {
        unimplemented!()
    }

}

impl<'a> Module<'a> for BM<'a> {

    fn exec(&self, matches: &ArgMatches) -> bool {
        match matches.subcommand_name() {
            Some("add")     => self.command_add(),
            Some("list")    => self.command_list(),
            Some("remove")  => self.command_remove(),
            Some(_) | None  => {
                info!("No command given, doing nothing");
                false
            },
        }
    }

    fn name(&self) -> &'static str {
        "bm"
    }
}

impl<'a> Debug for BM<'a> {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt, "BM");
        Ok(())
    }

}

<commit_msg>BM subcommands: Pass ArgMatches<commit_after>use std::fmt::{Debug, Display, Formatter};
use std::fmt;

use clap::ArgMatches;

use runtime::Runtime;
use module::Module;

pub struct BM<'a> {
    rt: &'a Runtime<'a>,
}

impl<'a> BM<'a> {

    pub fn new(rt: &'a Runtime<'a>) -> BM<'a> {
        BM {
            rt: rt,
        }
    }

    fn runtime(&self) -> &Runtime {
        &self.rt
    }

    fn command_add(&self, matches: &ArgMatches) -> bool {
        unimplemented!()
    }

    fn command_list(&self, matches: &ArgMatches) -> bool {
        unimplemented!()
    }

    fn command_remove(&self, matches: &ArgMatches) -> bool {
        unimplemented!()
    }

}

impl<'a> Module<'a> for BM<'a> {

    fn exec(&self, matches: &ArgMatches) -> bool {
        match matches.subcommand_name() {
            Some("add") => {
                self.command_add(matches.subcommand_matches("add").unwrap())
            },

            Some("list") => {
                self.command_list(matches.subcommand_matches("list").unwrap())
            },

            Some("remove") => {
                self.command_remove(matches.subcommand_matches("remove").unwrap())
            },

            Some(_) | None => {
                info!("No command given, doing nothing");
                false
            },
        }
    }

    fn name(&self) -> &'static str {
        "bm"
    }
}

impl<'a> Debug for BM<'a> {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        write!(fmt, "BM");
        Ok(())
    }

}

<|endoftext|>


<commit_before>use super::{Mesh, PointId};
use crate::shapes::Scratchpad;

/// Sets the pad's matrix of coordinates X given a list of point ids
///
/// # Panics
///
/// 1. Make sure `pad.kind.nnode() == points.len()`; otherwise a panic will occur
/// 2. This function does not check for bounds on point indices and dimensions
/// 3. Use [crate::mesh::check_all()] to capture (some) errors
#[inline]
pub fn set_pad_coords(pad: &mut Scratchpad, points: &[PointId], mesh: &Mesh) {
    let nnode = pad.kind.nnode();
    assert_eq!(nnode, points.len());
    for m in 0..nnode {
        for j in 0..mesh.ndim {
            pad.set_xx(m, j, mesh.points[points[m]].coords[j]);
        }
    }
}

/// Returns the (min,max) point coordinates in a mesh
#[inline]
pub fn get_mesh_limits(mesh: &Mesh) -> (Vec<f64>, Vec<f64>) {
    let mut min = vec![f64::MAX; mesh.ndim];
    let mut max = vec![f64::MIN; mesh.ndim];
    for point in &mesh.points {
        for i in 0..mesh.ndim {
            min[i] = f64::min(min[i], point.coords[i]);
            max[i] = f64::max(max[i], point.coords[i]);
        }
    }
    (min, max)
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#[cfg(test)]
mod tests {
    use super::{get_mesh_limits, set_pad_coords};
    use crate::mesh::Samples;
    use crate::shapes::Scratchpad;
    use crate::StrError;

    #[test]
    fn set_pad_coords_works_2d() -> Result<(), StrError> {
        //  3--------2--------5
        //  |        |        |
        //  |        |        |
        //  |        |        |
        //  0--------1--------4
        let mesh = Samples::two_qua4();
        let cell = &mesh.cells[0];
        let mut pad = Scratchpad::new(mesh.ndim, cell.kind)?;
        set_pad_coords(&mut pad, &cell.points, &mesh);
        assert_eq!(
            format!("{}", pad.xxt),
            "┌         ┐\n\
             │ 0 1 1 0 │\n\
             │ 0 0 1 1 │\n\
             └         ┘"
        );
        let cell = &mesh.cells[1];
        let mut pad = Scratchpad::new(mesh.ndim, cell.kind)?;
        set_pad_coords(&mut pad, &cell.points, &mesh);
        assert_eq!(
            format!("{}", pad.xxt),
            "┌         ┐\n\
             │ 1 2 2 1 │\n\
             │ 0 0 1 1 │\n\
             └         ┘"
        );
        Ok(())
    }

    #[test]
    fn set_pad_coords_works_3d() -> Result<(), StrError> {
        //       8-------------11
        //      /.             /|
        //     / .            / |
        //    /  .           /  |
        //   /   .          /   |
        //  9-------------10    |
        //  |    .         |    |
        //  |    4---------|----7
        //  |   /.         |   /|
        //  |  / .         |  / |
        //  | /  .         | /  |
        //  |/   .         |/   |
        //  5--------------6    |
        //  |    .         |    |
        //  |    0---------|----3
        //  |   /          |   /
        //  |  /           |  /
        //  | /            | /
        //  |/             |/
        //  1--------------2
        let mesh = Samples::two_hex8();
        let cell = &mesh.cells[0];
        let mut pad = Scratchpad::new(mesh.ndim, cell.kind)?;
        set_pad_coords(&mut pad, &cell.points, &mesh);
        assert_eq!(
            format!("{}", pad.xxt),
            "┌                 ┐\n\
             │ 0 1 1 0 0 1 1 0 │\n\
             │ 0 0 1 1 0 0 1 1 │\n\
             │ 0 0 0 0 1 1 1 1 │\n\
             └                 ┘"
        );
        let cell = &mesh.cells[1];
        let mut pad = Scratchpad::new(mesh.ndim, cell.kind)?;
        set_pad_coords(&mut pad, &cell.points, &mesh);
        assert_eq!(
            format!("{}", pad.xxt),
            "┌                 ┐\n\
             │ 0 1 1 0 0 1 1 0 │\n\
             │ 0 0 1 1 0 0 1 1 │\n\
             │ 1 1 1 1 2 2 2 2 │\n\
             └                 ┘"
        );
        Ok(())
    }

    #[test]
    fn get_mesh_limits_works() {
        let mesh = &Samples::two_qua4();
        let (min, max) = get_mesh_limits(&mesh);
        assert_eq!(min, &[0.0, 0.0]);
        assert_eq!(max, &[2.0, 1.0]);

        let mesh = &Samples::two_hex8();
        let (min, max) = get_mesh_limits(&mesh);
        assert_eq!(min, &[0.0, 0.0, 0.0]);
        assert_eq!(max, &[1.0, 1.0, 2.0]);
    }
}
<commit_msg>Use unwrap in helpers tests<commit_after>use super::{Mesh, PointId};
use crate::shapes::Scratchpad;

/// Sets the pad's matrix of coordinates X given a list of point ids
///
/// # Panics
///
/// 1. Make sure `pad.kind.nnode() == points.len()`; otherwise a panic will occur
/// 2. This function does not check for bounds on point indices and dimensions
/// 3. Use [crate::mesh::check_all()] to capture (some) errors
#[inline]
pub fn set_pad_coords(pad: &mut Scratchpad, points: &[PointId], mesh: &Mesh) {
    let nnode = pad.kind.nnode();
    assert_eq!(nnode, points.len());
    for m in 0..nnode {
        for j in 0..mesh.ndim {
            pad.set_xx(m, j, mesh.points[points[m]].coords[j]);
        }
    }
}

/// Returns the (min,max) point coordinates in a mesh
#[inline]
pub fn get_mesh_limits(mesh: &Mesh) -> (Vec<f64>, Vec<f64>) {
    let mut min = vec![f64::MAX; mesh.ndim];
    let mut max = vec![f64::MIN; mesh.ndim];
    for point in &mesh.points {
        for i in 0..mesh.ndim {
            min[i] = f64::min(min[i], point.coords[i]);
            max[i] = f64::max(max[i], point.coords[i]);
        }
    }
    (min, max)
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#[cfg(test)]
mod tests {
    use super::{get_mesh_limits, set_pad_coords};
    use crate::mesh::Samples;
    use crate::shapes::Scratchpad;

    #[test]
    fn set_pad_coords_works_2d() {
        //  3--------2--------5
        //  |        |        |
        //  |        |        |
        //  |        |        |
        //  0--------1--------4
        let mesh = Samples::two_qua4();
        let cell = &mesh.cells[0];
        let mut pad = Scratchpad::new(mesh.ndim, cell.kind).unwrap();
        set_pad_coords(&mut pad, &cell.points, &mesh);
        assert_eq!(
            format!("{}", pad.xxt),
            "┌         ┐\n\
             │ 0 1 1 0 │\n\
             │ 0 0 1 1 │\n\
             └         ┘"
        );
        let cell = &mesh.cells[1];
        let mut pad = Scratchpad::new(mesh.ndim, cell.kind).unwrap();
        set_pad_coords(&mut pad, &cell.points, &mesh);
        assert_eq!(
            format!("{}", pad.xxt),
            "┌         ┐\n\
             │ 1 2 2 1 │\n\
             │ 0 0 1 1 │\n\
             └         ┘"
        );
    }

    #[test]
    fn set_pad_coords_works_3d() {
        //       8-------------11
        //      /.             /|
        //     / .            / |
        //    /  .           /  |
        //   /   .          /   |
        //  9-------------10    |
        //  |    .         |    |
        //  |    4---------|----7
        //  |   /.         |   /|
        //  |  / .         |  / |
        //  | /  .         | /  |
        //  |/   .         |/   |
        //  5--------------6    |
        //  |    .         |    |
        //  |    0---------|----3
        //  |   /          |   /
        //  |  /           |  /
        //  | /            | /
        //  |/             |/
        //  1--------------2
        let mesh = Samples::two_hex8();
        let cell = &mesh.cells[0];
        let mut pad = Scratchpad::new(mesh.ndim, cell.kind).unwrap();
        set_pad_coords(&mut pad, &cell.points, &mesh);
        assert_eq!(
            format!("{}", pad.xxt),
            "┌                 ┐\n\
             │ 0 1 1 0 0 1 1 0 │\n\
             │ 0 0 1 1 0 0 1 1 │\n\
             │ 0 0 0 0 1 1 1 1 │\n\
             └                 ┘"
        );
        let cell = &mesh.cells[1];
        let mut pad = Scratchpad::new(mesh.ndim, cell.kind).unwrap();
        set_pad_coords(&mut pad, &cell.points, &mesh);
        assert_eq!(
            format!("{}", pad.xxt),
            "┌                 ┐\n\
             │ 0 1 1 0 0 1 1 0 │\n\
             │ 0 0 1 1 0 0 1 1 │\n\
             │ 1 1 1 1 2 2 2 2 │\n\
             └                 ┘"
        );
    }

    #[test]
    fn get_mesh_limits_works() {
        let mesh = &Samples::two_qua4();
        let (min, max) = get_mesh_limits(&mesh);
        assert_eq!(min, &[0.0, 0.0]);
        assert_eq!(max, &[2.0, 1.0]);

        let mesh = &Samples::two_hex8();
        let (min, max) = get_mesh_limits(&mesh);
        assert_eq!(min, &[0.0, 0.0, 0.0]);
        assert_eq!(max, &[1.0, 1.0, 2.0]);
    }
}
<|endoftext|>


<commit_before>
use {
    AddBevelBorder,
    AddColor,
    AddRoundBorder,
    BevelBorderLineContext,
    Borrowed,
    Field,
    Line,
    LineColorContext,
    Matrix2d,
    RoundBorderLineContext,
    Value,
    View,
};
use vecmath::{
    identity,
};
use internal::{
    CanTransform,
    HasTransform,
};

/// A line context.
pub struct LineContext<'a> {
    /// Base/original transform.
    pub base: Field<'a, Matrix2d>,
    /// Current transform.
    pub transform: Field<'a, Matrix2d>,
    /// Current line.
    pub line: Field<'a, Line>,
}

impl<'a> HasTransform<'a, Matrix2d> for LineContext<'a> {
    #[inline(always)]
    fn get_transform(&'a self) -> &'a Matrix2d {
        self.transform.get()
    }
}

impl<'a> CanTransform<'a, LineContext<'a>, Matrix2d> for LineContext<'a> {
    #[inline(always)]
    fn transform(&'a self, value: Matrix2d) -> LineContext<'a> {
        LineContext {
            base: Borrowed(self.base.get()),
            transform: Value(value),
            line: Borrowed(self.line.get()),
        }
    }
}

impl<'a> View<'a> for LineContext<'a> {
    #[inline(always)]
    fn view(&'a self) -> LineContext<'a> {
        LineContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.base.get()),
            line: Borrowed(self.line.get()),
        }
    }

    #[inline(always)]
    fn reset(&'a self) -> LineContext<'a> {
        LineContext {
            base: Borrowed(self.base.get()),
            transform: Value(identity()),
            line: Borrowed(self.line.get()),
        }
    }

    #[inline(always)]
    fn store_view(&'a self) -> LineContext<'a> {
        LineContext {
            base: Borrowed(self.transform.get()),
            transform: Borrowed(self.transform.get()),
            line: Borrowed(self.line.get()),
        }
    }
}

impl<'a> AddColor<'a, LineColorContext<'a>> for LineContext<'a> {
    #[inline(always)]
    fn rgba(&'a self, r: f32, g: f32, b: f32, a: f32) -> LineColorContext<'a> {
        LineColorContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            line: Borrowed(self.line.get()),
            color: Value([r, g, b, a]),
        }
    }
}

impl<'a> AddRoundBorder<'a, RoundBorderLineContext<'a>> for LineContext<'a> {
    #[inline(always)]
    fn round_border_radius(&'a self, radius: f64) -> RoundBorderLineContext<'a> {
        RoundBorderLineContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            line: Borrowed(self.line.get()),
            round_border_radius: Value(radius),
        }
    }
}

impl<'a> AddBevelBorder<'a, BevelBorderLineContext<'a>> for LineContext<'a> {
    #[inline(always)]
    fn bevel_border_radius(&'a self, radius: f64) -> BevelBorderLineContext<'a> {
        BevelBorderLineContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            line: Borrowed(self.line.get()),
            bevel_border_radius: Value(radius),
        }
    }
}

<commit_msg>Implemented `AddSquareBorder` for `LineContext`<commit_after>
use {
    AddBevelBorder,
    AddColor,
    AddRoundBorder,
    AddSquareBorder,
    BevelBorderLineContext,
    Borrowed,
    Field,
    Line,
    LineColorContext,
    Matrix2d,
    RoundBorderLineContext,
    SquareBorderLineContext,
    Value,
    View,
};
use vecmath::{
    identity,
};
use internal::{
    CanTransform,
    HasTransform,
};

/// A line context.
pub struct LineContext<'a> {
    /// Base/original transform.
    pub base: Field<'a, Matrix2d>,
    /// Current transform.
    pub transform: Field<'a, Matrix2d>,
    /// Current line.
    pub line: Field<'a, Line>,
}

impl<'a> HasTransform<'a, Matrix2d> for LineContext<'a> {
    #[inline(always)]
    fn get_transform(&'a self) -> &'a Matrix2d {
        self.transform.get()
    }
}

impl<'a> CanTransform<'a, LineContext<'a>, Matrix2d> for LineContext<'a> {
    #[inline(always)]
    fn transform(&'a self, value: Matrix2d) -> LineContext<'a> {
        LineContext {
            base: Borrowed(self.base.get()),
            transform: Value(value),
            line: Borrowed(self.line.get()),
        }
    }
}

impl<'a> View<'a> for LineContext<'a> {
    #[inline(always)]
    fn view(&'a self) -> LineContext<'a> {
        LineContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.base.get()),
            line: Borrowed(self.line.get()),
        }
    }

    #[inline(always)]
    fn reset(&'a self) -> LineContext<'a> {
        LineContext {
            base: Borrowed(self.base.get()),
            transform: Value(identity()),
            line: Borrowed(self.line.get()),
        }
    }

    #[inline(always)]
    fn store_view(&'a self) -> LineContext<'a> {
        LineContext {
            base: Borrowed(self.transform.get()),
            transform: Borrowed(self.transform.get()),
            line: Borrowed(self.line.get()),
        }
    }
}

impl<'a> AddColor<'a, LineColorContext<'a>> for LineContext<'a> {
    #[inline(always)]
    fn rgba(&'a self, r: f32, g: f32, b: f32, a: f32) -> LineColorContext<'a> {
        LineColorContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            line: Borrowed(self.line.get()),
            color: Value([r, g, b, a]),
        }
    }
}

impl<'a> AddRoundBorder<'a, RoundBorderLineContext<'a>> for LineContext<'a> {
    #[inline(always)]
    fn round_border_radius(&'a self, radius: f64) -> RoundBorderLineContext<'a> {
        RoundBorderLineContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            line: Borrowed(self.line.get()),
            round_border_radius: Value(radius),
        }
    }
}

impl<'a> AddBevelBorder<'a, BevelBorderLineContext<'a>> for LineContext<'a> {
    #[inline(always)]
    fn bevel_border_radius(&'a self, radius: f64) -> BevelBorderLineContext<'a> {
        BevelBorderLineContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            line: Borrowed(self.line.get()),
            bevel_border_radius: Value(radius),
        }
    }
}

impl<'a> AddSquareBorder<'a, SquareBorderLineContext<'a>> for LineContext<'a> {
    #[inline(always)]
    fn square_border_radius(&'a self, radius: f64) -> SquareBorderLineContext<'a> {
        SquareBorderLineContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            line: Borrowed(self.line.get()),
            square_border_radius: Value(radius),
        }
    }
}

<|endoftext|>


<commit_before>use postgres;
use uuid::Uuid;
use std::fmt;
use chrono::{NaiveDateTime, Utc};

use soundcloud;
use spotify;
use apple_music;
use error::Error;
use super::{conn, Model};
use model::provider::Provider;
use model::enclosure::Enclosure;

static PROPS: [&'static str; 9]  = ["id",
                                    "provider",
                                    "identifier",
                                    "url",
                                    "name",
                                    "thumbnail_url",
                                    "artwork_url",
                                    "created_at",
                                    "updated_at"];

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Artist {
    pub id:            Uuid,
    pub provider:      Provider,
    pub identifier:    String,
    pub url:           String,
    pub name:          String,
    pub thumbnail_url: Option<String>,
    pub artwork_url:   Option<String>,
    pub created_at:    NaiveDateTime,
    pub updated_at:    NaiveDateTime,
}

impl fmt::Display for Artist {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}:{}", self.provider, self.identifier)
    }
}

impl<'a> Model<'a> for Artist {
    fn table_name() -> String { "artists".to_string() }
    fn props_str(prefix: &str) -> String {
        PROPS
            .iter()
            .map(|&p| format!("{}{}", prefix, p))
            .collect::<Vec<String>>().join(",")
    }
    fn rows_to_items(rows: postgres::rows::Rows) -> Vec<Artist> {
        let mut artists = Vec::new();
        for row in rows.iter() {
            let artist = Artist {
                id:            row.get(0),
                provider:      Provider::new(row.get(1)),
                identifier:    row.get(2),
                url:           row.get(3),
                name:          row.get(4),
                thumbnail_url: row.get(5),
                artwork_url:   row.get(6),
                created_at:    row.get(7),
                updated_at:    row.get(8),
            };
            artists.push(artist)
        }
        artists
    }

    fn create(&self) -> Result<Artist, Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO artists (provider, identifier, url, name)
                                 VALUES ($1, $2, $3, $4) RETURNING id"));
        let rows = try!(stmt.query(&[&self.provider.to_string(), &self.identifier, &self.url, &self.name]));
        let mut artist = self.clone();
        for row in rows.iter() {
            artist.id = row.get(0);
        }
        Ok(artist)
    }

    fn save(&mut self) -> Result<(), Error> {
        self.updated_at = Utc::now().naive_utc();
        let conn = conn().unwrap();
        let stmt = conn.prepare("UPDATE artists SET
                                 provider      = $2,
                                 identifier    = $3,
                                 url           = $4,
                                 name          = $5,
                                 thumbnail_url = $6,
                                 artwork_url   = $7,
                                 created_at    = $8,
                                 updated_at    = $9
                                 WHERE id = $1").unwrap();
        let result = stmt.query(&[&self.id,
                                  &self.provider.to_string(),
                                  &self.identifier,
                                  &self.url,
                                  &self.name,
                                  &self.thumbnail_url,
                                  &self.artwork_url,
                                  &self.created_at,
                                  &self.updated_at,
        ]);
        match result {
            Ok(_)  => Ok(()),
            Err(_) => Err(Error::Unexpected),
        }
    }
}

impl<'a> Enclosure<'a> for Artist {
    fn new(provider: Provider, identifier: String) -> Artist {
        Artist {
            id:            Uuid::new_v4(),
            provider:      provider,
            identifier:    identifier,
            url:           "".to_string(),
            name:          "".to_string(),
            thumbnail_url: None,
            artwork_url:   None,
            created_at:    Utc::now().naive_utc(),
            updated_at:    Utc::now().naive_utc(),
        }
    }

    fn set_url(&mut self, url: String) -> &mut Artist {
        self.url = url;
        self
    }

    fn set_owner_id(&mut self, _owner_id: Option<String>) -> &mut Artist {
        self
    }

    fn fetch_props(&mut self) -> Result<(), Error> {
        match self.provider {
            Provider::AppleMusic => {
                let country = apple_music::country(&self.url);
                match apple_music::fetch_artist(&country, &self.identifier) {
                    Ok(artist) => self.update_with_am_artist(&artist),
                    Err(_)     => self,
                }
            },
            Provider::Spotify => match spotify::fetch_artist(&self.identifier) {
                Ok(artist) => self.update_with_sp_artist(&artist),
                Err(_)     => self,
            },
            _ => self,
        };
        Ok(())
    }

    fn find_by_entry_id(_entry_id: Uuid) -> Vec<Artist> {
        vec![]
    }
}

impl Artist {
    fn find_by(provider: &Provider, identifier: &str) -> Result<Artist, Error> {
        let conn = conn().unwrap();
        let stmt = conn.prepare(
            &format!("SELECT {} FROM {}
                     WHERE provider = $1 AND identifier = $2
                     ORDER BY updated_at DESC",
                     Artist::props_str(""), Artist::table_name())).unwrap();
        let rows = stmt.query(&[&(*provider).to_string(), &identifier]).unwrap();
        let items = Artist::rows_to_items(rows);
        if items.len() > 0 {
            return Ok(items[0].clone());
        }
        return Err(Error::NotFound)
    }
    pub fn find_or_create(provider: Provider, identifier: String) -> Result<Artist, Error> {
        return match Artist::find_by(&provider, &identifier) {
            Ok(item) => Ok(item),
            Err(_)   => Artist::new(provider, identifier).create()
        }
    }
    pub fn from_yt_channel(channel_id: &str, channel_title: &str) -> Artist {
        let mut artist = Artist::new(Provider::YouTube, channel_id.to_string());
        artist.name = channel_title.to_string();
        artist.clone()
    }
    pub fn from_sp_artist(artist: &spotify::Artist) -> Artist {
        Artist::new(Provider::Spotify, (*artist).id.to_string())
            .update_with_sp_artist(artist)
            .clone()
    }

    pub fn from_sc_user(playlist: &soundcloud::User) -> Artist {
        Artist::new(Provider::SoundCloud, (*playlist).id.to_string())
            .update_with_sc_user(playlist)
            .clone()
    }

    pub fn from_am_artist(artist: &str) -> Artist {
        let mut artist = Artist::new(Provider::AppleMusic, artist.to_string());
        artist.name = artist.to_string();
        artist.clone()
    }

    pub fn update_with_sp_artist(&mut self, artist: &spotify::Artist) -> &mut Artist {
        self.provider       = Provider::Spotify;
        self.identifier     = artist.id.to_string();
        self.url            = artist.uri.clone();
        self.name           = artist.name.clone();
        if let Some(ref images) = artist.images {
            if images.len() > 0 {
                self.artwork_url   = Some(images[0].url.clone());
                self.thumbnail_url = Some(images[0].url.clone());
            }
            if images.len() > 1 {
                self.thumbnail_url = Some(images[1].url.clone());
            }
        }
        self
    }

    pub fn update_with_am_artist(&mut self, artist: &apple_music::Artist) -> &mut Artist {
        self.provider      = Provider::AppleMusic;
        self.identifier    = artist.id.to_string();
        self.url           = artist.attributes.url.to_string();
        self.name          = artist.attributes.name.to_string();
        if let Some(album) = artist.clone().relationships
            .and_then(|r| r.albums.data.first().map(|a| a.clone())) {
                self.thumbnail_url = Some(album.attributes.artwork.url.to_string());
                self.artwork_url   = Some(album.attributes.artwork.url.to_string());
            }
        self
    }

    pub fn update_with_sc_user(&mut self, user: &soundcloud::User) -> &mut Artist {
        self.provider      = Provider::SoundCloud;
        self.identifier    = user.id.to_string();
        self.url           = user.permalink.to_string();
        self.name          = user.username.to_string();
        self.thumbnail_url = Some(user.avatar_url.clone());
        self.artwork_url   = Some(user.avatar_url.clone());
        self
    }
}

#[cfg(test)]
mod test {
    use model::Model;
    use model::enclosure::Enclosure;
    use super::Artist;
    use Provider;
    #[test]
    fn test_new() {
        let artist = Artist::new(Provider::Spotify, "4tZwfgrHOc3mvqYlEYSvVi".to_string());
        assert_eq!(artist.provider, Provider::Spotify);
        assert_eq!(&artist.identifier, "4tZwfgrHOc3mvqYlEYSvVi");
    }
    #[test]
    fn test_find_of_create() {
        let identifier = "4tZwfgrHOc3mvqYlEYSvVi".to_string();
        let result     = Artist::find_or_create(Provider::Spotify, identifier);
        assert!(result.is_ok());
    }
    #[test]
    fn test_save() {
        let id = "test_save";
        let mut artist = Artist::find_or_create(Provider::Spotify, id.to_string()).unwrap();
        artist.name   = "name".to_string();
        let result    = artist.save();
        assert!(result.is_ok());
        let artist = Artist::find_or_create(Provider::Spotify, id.to_string()).unwrap();
        assert_eq!(&artist.name, "name");
    }
}
<commit_msg>Implement Artist::find_by_provider<commit_after>use postgres;
use uuid::Uuid;
use std::fmt;
use chrono::{NaiveDateTime, Utc};

use soundcloud;
use spotify;
use apple_music;
use error::Error;
use super::{conn, Model};
use model::provider::Provider;
use model::enclosure::Enclosure;

static PROPS: [&'static str; 9]  = ["id",
                                    "provider",
                                    "identifier",
                                    "url",
                                    "name",
                                    "thumbnail_url",
                                    "artwork_url",
                                    "created_at",
                                    "updated_at"];

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Artist {
    pub id:            Uuid,
    pub provider:      Provider,
    pub identifier:    String,
    pub url:           String,
    pub name:          String,
    pub thumbnail_url: Option<String>,
    pub artwork_url:   Option<String>,
    pub created_at:    NaiveDateTime,
    pub updated_at:    NaiveDateTime,
}

impl fmt::Display for Artist {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}:{}", self.provider, self.identifier)
    }
}

impl<'a> Model<'a> for Artist {
    fn table_name() -> String { "artists".to_string() }
    fn props_str(prefix: &str) -> String {
        PROPS
            .iter()
            .map(|&p| format!("{}{}", prefix, p))
            .collect::<Vec<String>>().join(",")
    }
    fn rows_to_items(rows: postgres::rows::Rows) -> Vec<Artist> {
        let mut artists = Vec::new();
        for row in rows.iter() {
            let artist = Artist {
                id:            row.get(0),
                provider:      Provider::new(row.get(1)),
                identifier:    row.get(2),
                url:           row.get(3),
                name:          row.get(4),
                thumbnail_url: row.get(5),
                artwork_url:   row.get(6),
                created_at:    row.get(7),
                updated_at:    row.get(8),
            };
            artists.push(artist)
        }
        artists
    }

    fn create(&self) -> Result<Artist, Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO artists (provider, identifier, url, name)
                                 VALUES ($1, $2, $3, $4) RETURNING id"));
        let rows = try!(stmt.query(&[&self.provider.to_string(), &self.identifier, &self.url, &self.name]));
        let mut artist = self.clone();
        for row in rows.iter() {
            artist.id = row.get(0);
        }
        Ok(artist)
    }

    fn save(&mut self) -> Result<(), Error> {
        self.updated_at = Utc::now().naive_utc();
        let conn = conn().unwrap();
        let stmt = conn.prepare("UPDATE artists SET
                                 provider      = $2,
                                 identifier    = $3,
                                 url           = $4,
                                 name          = $5,
                                 thumbnail_url = $6,
                                 artwork_url   = $7,
                                 created_at    = $8,
                                 updated_at    = $9
                                 WHERE id = $1").unwrap();
        let result = stmt.query(&[&self.id,
                                  &self.provider.to_string(),
                                  &self.identifier,
                                  &self.url,
                                  &self.name,
                                  &self.thumbnail_url,
                                  &self.artwork_url,
                                  &self.created_at,
                                  &self.updated_at,
        ]);
        match result {
            Ok(_)  => Ok(()),
            Err(_) => Err(Error::Unexpected),
        }
    }
}

impl<'a> Enclosure<'a> for Artist {
    fn new(provider: Provider, identifier: String) -> Artist {
        Artist {
            id:            Uuid::new_v4(),
            provider:      provider,
            identifier:    identifier,
            url:           "".to_string(),
            name:          "".to_string(),
            thumbnail_url: None,
            artwork_url:   None,
            created_at:    Utc::now().naive_utc(),
            updated_at:    Utc::now().naive_utc(),
        }
    }

    fn set_url(&mut self, url: String) -> &mut Artist {
        self.url = url;
        self
    }

    fn set_owner_id(&mut self, _owner_id: Option<String>) -> &mut Artist {
        self
    }

    fn fetch_props(&mut self) -> Result<(), Error> {
        match self.provider {
            Provider::AppleMusic => {
                let country = apple_music::country(&self.url);
                match apple_music::fetch_artist(&country, &self.identifier) {
                    Ok(artist) => self.update_with_am_artist(&artist),
                    Err(_)     => self,
                }
            },
            Provider::Spotify => match spotify::fetch_artist(&self.identifier) {
                Ok(artist) => self.update_with_sp_artist(&artist),
                Err(_)     => self,
            },
            _ => self,
        };
        Ok(())
    }

    fn find_by_entry_id(_entry_id: Uuid) -> Vec<Artist> {
        vec![]
    }
}

impl Artist {
    fn find_by(provider: &Provider, identifier: &str) -> Result<Artist, Error> {
        let conn = conn().unwrap();
        let stmt = conn.prepare(
            &format!("SELECT {} FROM {}
                     WHERE provider = $1 AND identifier = $2
                     ORDER BY updated_at DESC",
                     Artist::props_str(""), Artist::table_name())).unwrap();
        let rows = stmt.query(&[&(*provider).to_string(), &identifier]).unwrap();
        let items = Artist::rows_to_items(rows);
        if items.len() > 0 {
            return Ok(items[0].clone());
        }
        return Err(Error::NotFound)
    }
    pub fn find_or_create(provider: Provider, identifier: String) -> Result<Artist, Error> {
        return match Artist::find_by(&provider, &identifier) {
            Ok(item) => Ok(item),
            Err(_)   => Artist::new(provider, identifier).create()
        }
    }

    pub fn find_by_provider(provider: &Provider) -> Vec<Artist> {
        let conn = conn().unwrap();
        let stmt = conn.prepare(
            &format!("SELECT {} FROM artists WHERE artists.provider = $1
                        ORDER BY artists.created_at DESC",
                     Artist::props_str(""))).unwrap();
        let rows = stmt.query(&[&(*provider).to_string()]).unwrap();
        Artist::rows_to_items(rows)
    }

    pub fn from_yt_channel(channel_id: &str, channel_title: &str) -> Artist {
        let mut artist = Artist::new(Provider::YouTube, channel_id.to_string());
        artist.name = channel_title.to_string();
        artist.clone()
    }
    pub fn from_sp_artist(artist: &spotify::Artist) -> Artist {
        Artist::new(Provider::Spotify, (*artist).id.to_string())
            .update_with_sp_artist(artist)
            .clone()
    }

    pub fn from_sc_user(playlist: &soundcloud::User) -> Artist {
        Artist::new(Provider::SoundCloud, (*playlist).id.to_string())
            .update_with_sc_user(playlist)
            .clone()
    }

    pub fn from_am_artist(artist: &str) -> Artist {
        let mut artist = Artist::new(Provider::AppleMusic, artist.to_string());
        artist.name = artist.to_string();
        artist.clone()
    }

    pub fn update_with_sp_artist(&mut self, artist: &spotify::Artist) -> &mut Artist {
        self.provider       = Provider::Spotify;
        self.identifier     = artist.id.to_string();
        self.url            = artist.uri.clone();
        self.name           = artist.name.clone();
        if let Some(ref images) = artist.images {
            if images.len() > 0 {
                self.artwork_url   = Some(images[0].url.clone());
                self.thumbnail_url = Some(images[0].url.clone());
            }
            if images.len() > 1 {
                self.thumbnail_url = Some(images[1].url.clone());
            }
        }
        self
    }

    pub fn update_with_am_artist(&mut self, artist: &apple_music::Artist) -> &mut Artist {
        self.provider      = Provider::AppleMusic;
        self.identifier    = artist.id.to_string();
        self.url           = artist.attributes.url.to_string();
        self.name          = artist.attributes.name.to_string();
        if let Some(album) = artist.clone().relationships
            .and_then(|r| r.albums.data.first().map(|a| a.clone())) {
                self.thumbnail_url = Some(album.attributes.artwork.url.to_string());
                self.artwork_url   = Some(album.attributes.artwork.url.to_string());
            }
        self
    }

    pub fn update_with_sc_user(&mut self, user: &soundcloud::User) -> &mut Artist {
        self.provider      = Provider::SoundCloud;
        self.identifier    = user.id.to_string();
        self.url           = user.permalink.to_string();
        self.name          = user.username.to_string();
        self.thumbnail_url = Some(user.avatar_url.clone());
        self.artwork_url   = Some(user.avatar_url.clone());
        self
    }
}

#[cfg(test)]
mod test {
    use model::Model;
    use model::enclosure::Enclosure;
    use super::Artist;
    use Provider;
    #[test]
    fn test_new() {
        let artist = Artist::new(Provider::Spotify, "4tZwfgrHOc3mvqYlEYSvVi".to_string());
        assert_eq!(artist.provider, Provider::Spotify);
        assert_eq!(&artist.identifier, "4tZwfgrHOc3mvqYlEYSvVi");
    }
    #[test]
    fn test_find_of_create() {
        let identifier = "4tZwfgrHOc3mvqYlEYSvVi".to_string();
        let result     = Artist::find_or_create(Provider::Spotify, identifier);
        assert!(result.is_ok());
    }
    #[test]
    fn test_save() {
        let id = "test_save";
        let mut artist = Artist::find_or_create(Provider::Spotify, id.to_string()).unwrap();
        artist.name   = "name".to_string();
        let result    = artist.save();
        assert!(result.is_ok());
        let artist = Artist::find_or_create(Provider::Spotify, id.to_string()).unwrap();
        assert_eq!(&artist.name, "name");
    }
}
<|endoftext|>


<commit_before>//! Functions for parsing a molecule or a chemical reaction.
//!
//! Mostly based on the HTML parser from the robinson browser engine:
//! https://github.com/mbrubeck/robinson
//!
//! The parser works with the following grammar:
//! R = (R)eaction
//! S = one (S)ide of a reaction
//! M = (M)olecule
//! P = Combination of a (P)eriodic element and maybe a coefficient
//! E = Periodic (E)lement
//! C = (C)oefficient
//!
//! R --> S -> S
//! S --> M + S
//!    |  M
//! M --> PM
//!    |  P
//! P --> EC
//!    |  E
//! E --> <text>
//!    |  (M)
//! C --> <number>

use std::str::CharRange;
use elem::{PerElem, Molecule};
use error::{CTResult, CTError};
use error::CTErrorKind::InputError;

pub struct Parser {
    pos: usize,
    input: String,
    paren_level: u32,
}

impl Parser {
    /// Create a new Parser with the given input string
    pub fn new(input: &str) -> Parser {
        Parser { pos: 0, input: String::from_str(input), paren_level: 0 }
    }

    /// Returns true if there is nothing left to parse
    ///
    /// If there is nothing but whitespace left, then we are done with the parsing.
    pub fn is_done(&self) -> bool {
        self.input.chars().skip(self.pos).all(|ch| ch.is_whitespace())
    }

    /// Parses the formula for a chemical reaction
    ///
    /// This runs through the full grammar and parses a reaction conforming to it.
    /// The two sides of the reaction are represented as Vecs of Molecules (which
    /// are themselves Vecs of PerElems).
    pub fn parse_reaction(&mut self) -> CTResult<(Vec<Molecule>, Vec<Molecule>)> {
        let lhs = try!(self.parse_side());
        self.consume_whitespace();

        // we do not care if some of the consumes are not called, since this is
        // an error anyway, and will abort the parsing
        if self.pos + 2 >= self.input.len() || self.consume_char() != '-' ||
                                               self.consume_char() != '>' {
            return Err(CTError {
                kind: InputError,
                desc: "Missing arrow (->) in chemical reaction".to_string(),
                pos: Some((self.pos - 2, 1))
            });
        }
        self.consume_whitespace();

        let rhs = try!(self.parse_side());

        Ok((lhs, rhs))
    }

    /// Parses a single side in a chemical reaction
    ///
    /// This runs through a subset of the grammar in order to parse a single side
    /// of a checmical reaction. Note that there still be more to parse after a
    /// call to this function.
    pub fn parse_side(&mut self) -> CTResult<Vec<Molecule>> {
        let mut out = Vec::new();
        let molecule = try!(self.parse_molecule());
        out.push(molecule);
        self.consume_whitespace();

        if !self.eof() && self.peek_char() == '+' {
            self.consume_char();
            self.consume_whitespace();
            let mut rest = try!(self.parse_side());
            out.append(&mut rest);
        }

        Ok(out)
    }

    /// Parses a single molecule
    ///
    /// This runs through a subset of the grammar in order to parse a single
    /// molecule. Note that there might still be more to parse after a
    /// call to this function, and no whitespace is allowed in a molecule.
    pub fn parse_molecule(&mut self) -> CTResult<Molecule> {
        let mut out = Vec::new();
        let mut per = try!(self.parse_periodic());
        out.append(&mut per);

        // TODO: Make this cleaner
        if !self.eof() && (self.peek_char().is_alphabetic() || self.peek_char() == '(') {
            let mut molecule = try!(self.parse_molecule());
            out.append(&mut molecule);
        }
        if !self.eof() && self.peek_char() == ')' && self.paren_level == 0 {
            Err(CTError {
                kind: InputError,
                desc: "Missing opening parentheses".to_string(),
                pos: Some((self.pos, 1))
            })
        } else if !self.eof() && !self.on_legal_char() {
            Err(CTError {
                kind: InputError,
                desc: "Unexpected character".to_string(),
                pos: Some((self.pos, 1))
            })
        } else {
            Ok(out)
        }
    }

    fn parse_periodic(&mut self) -> CTResult<Vec<PerElem>> {
        let mut elem = try!(self.parse_element());

        if !self.eof() && self.peek_char().is_numeric() {
            let coef = try!(self.parse_coefficient());
            for e in elem.iter_mut() {
                e.coef *= coef;
            }
        }

        Ok(elem)
    }

    fn parse_element(&mut self) -> CTResult<Vec<PerElem>> {
        if self.eof() {
            return Err(CTError {
                kind: InputError,
                desc: "Found no periodic element".to_string(),
                pos: Some((self.pos, 1))
            });
        }
        // we store the current position here, so the consumes don't mess it up
        let start_pos = self.pos;
        let first = self.consume_char();
        if first == '(' {
            self.paren_level += 1;
            let molecule = try!(self.parse_molecule());
            if self.eof() || self.consume_char() != ')' {
                Err(CTError {
                    kind: InputError,
                    desc: "Missing closing parentheses".to_string(),
                    pos: Some((self.pos - 1, 1))
                })
            } else {
                self.paren_level -= 1;
                Ok(molecule)
            }
        } else if first.is_uppercase() {
            let mut name = String::new();
            name.push(first);
            name.push_str(self.consume_while(|ch| ch.is_lowercase()).as_slice());
            let len = name.len();
            Ok(vec!(PerElem { name: name, coef: 1, pos: start_pos, len: len }))
        } else {
            Err(CTError {
                kind: InputError,
                desc: "Missing uppercase letter at the beginning of the element".to_string(),
                pos: Some((self.pos - 1, 1))
            })
        }
    }

    fn parse_coefficient(&mut self) -> CTResult<u32> {
        let start_pos = self.pos;
        let num_str = self.consume_while(|ch| ch.is_numeric());
        if let Ok(num) = num_str.parse::<u32>() {
            Ok(num)
        } else {
            Err(CTError {
                kind: InputError,
                desc: "Could not parse coefficient".to_string(),
                pos: Some((start_pos, num_str.len()))
            })
        }
    }

    fn peek_char(&self) -> char {
        self.input.char_at(self.pos)
    }

    fn consume_char(&mut self) -> char {
        let CharRange { ch, next } = self.input.char_range_at(self.pos);
        self.pos = next;
        ch
    }

    fn consume_while<F>(&mut self, pred: F) -> String where F: Fn(char) -> bool {
        let mut out = String::new();
        while !self.eof() && pred(self.peek_char()) {
            out.push(self.consume_char());
        }
        out
    }

    fn consume_whitespace(&mut self) {
        self.consume_while(|ch| ch.is_whitespace());
    }

    fn eof(&mut self) -> bool {
        self.pos >= self.input.len()
    }

    fn on_legal_char(&self) -> bool {
        match self.peek_char() {
            ch if ch.is_alphanumeric() => true,
            '+' | '-' | '>' | '(' | ')' | ' ' => true,
            _ => false,
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use elem::PerElem;

    macro_rules! check_raw_result(
        ($raw:expr, $expected:expr) => (
            if let Ok(result) = $raw {
                assert_eq!(result, $expected);
            } else {
                panic!($raw);
            }
        )
    );

    #[test]
    fn elems() {
        let mut parser = Parser::new("CHeH");
        let raw_result = parser.parse_molecule();
        let expected = vec!(PerElem { name: "C".to_string(), coef: 1, pos: 0, len: 1 },
                            PerElem { name: "He".to_string(), coef: 1, pos: 1, len: 2 },
                            PerElem { name: "H".to_string(), coef: 1, pos: 3, len: 1 });
        check_raw_result!(raw_result, expected);
    }

    #[test]
    fn coefs() {
        let mut parser = Parser::new("C23");
        let raw_result = parser.parse_molecule();
        let expected = vec!(PerElem { name: "C".to_string(), coef: 23, pos: 0, len: 1 });
        check_raw_result!(raw_result, expected);
    }

    #[test]
    fn parens() {
        let mut parser = Parser::new("(CH3)2");
        let raw_result = parser.parse_molecule();
        let expected = vec!(PerElem { name: "C".to_string(), coef: 2, pos: 1, len: 1 },
                            PerElem { name: "H".to_string(), coef: 6, pos: 2, len: 1 });
        check_raw_result!(raw_result, expected);
    }

    #[test]
    fn multiple_elems() {
        let mut parser = Parser::new("C + H");
        let raw_result = parser.parse_side();
        let expected = vec!(vec!(PerElem { name: "C".to_string(), coef: 1, pos: 0, len: 1 }),
                            vec!(PerElem { name: "H".to_string(), coef: 1, pos: 4, len: 1 }));
        check_raw_result!(raw_result, expected);
    }

    #[test]
    fn reaction() {
        let mut parser = Parser::new("C -> H");
        let raw_result = parser.parse_reaction();
        let expected = (vec!(vec!(PerElem { name: "C".to_string(), coef: 1, pos: 0, len: 1 })),
                        vec!(vec!(PerElem { name: "H".to_string(), coef: 1, pos: 5, len: 1 })));
        check_raw_result!(raw_result, expected);
    }

    #[test]
    fn empty() {
        let mut parser = Parser::new("");
        assert!(parser.parse_molecule().is_err());
        assert!(parser.parse_reaction().is_err());
    }

    #[test]
    fn no_uppercase() {
        let mut parser = Parser::new("c");
        assert!(parser.parse_molecule().is_err());
        assert!(parser.parse_reaction().is_err());
    }

    #[test]
    fn missing_close_paren() {
        let mut parser = Parser::new("(C");
        assert!(parser.parse_molecule().is_err());
        assert!(parser.parse_reaction().is_err());
    }

    #[test]
    fn missing_open_paren() {
        let mut parser = Parser::new("C)");
        assert!(parser.parse_molecule().is_err());
        assert!(parser.parse_reaction().is_err());
    }

    #[test]
    fn invald_char() {
        let mut parser = Parser::new("%");
        assert!(parser.parse_molecule().is_err());
        assert!(parser.parse_reaction().is_err());
    }

    #[test]
    fn is_done() {
        let mut parser = Parser::new("    ");
        assert!(parser.is_done());
    }

    #[test]
    fn not_done() {
        let mut parser = Parser::new("    C");
        assert!(!parser.is_done());
    }

    #[test]
    fn invald_num() {
        let mut parser = Parser::new("C999999999999999999999");
        assert!(parser.parse_molecule().is_err());
        assert!(parser.parse_reaction().is_err());
    }

    #[test]
    fn dangling_plus() {
        let mut parser = Parser::new("C + -> H");
        assert!(parser.parse_reaction().is_err());
    }
}<commit_msg>Remove unneeded mut in some tests<commit_after>//! Functions for parsing a molecule or a chemical reaction.
//!
//! Mostly based on the HTML parser from the robinson browser engine:
//! https://github.com/mbrubeck/robinson
//!
//! The parser works with the following grammar:
//! R = (R)eaction
//! S = one (S)ide of a reaction
//! M = (M)olecule
//! P = Combination of a (P)eriodic element and maybe a coefficient
//! E = Periodic (E)lement
//! C = (C)oefficient
//!
//! R --> S -> S
//! S --> M + S
//!    |  M
//! M --> PM
//!    |  P
//! P --> EC
//!    |  E
//! E --> <text>
//!    |  (M)
//! C --> <number>

use std::str::CharRange;
use elem::{PerElem, Molecule};
use error::{CTResult, CTError};
use error::CTErrorKind::InputError;

pub struct Parser {
    pos: usize,
    input: String,
    paren_level: u32,
}

impl Parser {
    /// Create a new Parser with the given input string
    pub fn new(input: &str) -> Parser {
        Parser { pos: 0, input: String::from_str(input), paren_level: 0 }
    }

    /// Returns true if there is nothing left to parse
    ///
    /// If there is nothing but whitespace left, then we are done with the parsing.
    pub fn is_done(&self) -> bool {
        self.input.chars().skip(self.pos).all(|ch| ch.is_whitespace())
    }

    /// Parses the formula for a chemical reaction
    ///
    /// This runs through the full grammar and parses a reaction conforming to it.
    /// The two sides of the reaction are represented as Vecs of Molecules (which
    /// are themselves Vecs of PerElems).
    pub fn parse_reaction(&mut self) -> CTResult<(Vec<Molecule>, Vec<Molecule>)> {
        let lhs = try!(self.parse_side());
        self.consume_whitespace();

        // we do not care if some of the consumes are not called, since this is
        // an error anyway, and will abort the parsing
        if self.pos + 2 >= self.input.len() || self.consume_char() != '-' ||
                                               self.consume_char() != '>' {
            return Err(CTError {
                kind: InputError,
                desc: "Missing arrow (->) in chemical reaction".to_string(),
                pos: Some((self.pos - 2, 1))
            });
        }
        self.consume_whitespace();

        let rhs = try!(self.parse_side());

        Ok((lhs, rhs))
    }

    /// Parses a single side in a chemical reaction
    ///
    /// This runs through a subset of the grammar in order to parse a single side
    /// of a checmical reaction. Note that there still be more to parse after a
    /// call to this function.
    pub fn parse_side(&mut self) -> CTResult<Vec<Molecule>> {
        let mut out = Vec::new();
        let molecule = try!(self.parse_molecule());
        out.push(molecule);
        self.consume_whitespace();

        if !self.eof() && self.peek_char() == '+' {
            self.consume_char();
            self.consume_whitespace();
            let mut rest = try!(self.parse_side());
            out.append(&mut rest);
        }

        Ok(out)
    }

    /// Parses a single molecule
    ///
    /// This runs through a subset of the grammar in order to parse a single
    /// molecule. Note that there might still be more to parse after a
    /// call to this function, and no whitespace is allowed in a molecule.
    pub fn parse_molecule(&mut self) -> CTResult<Molecule> {
        let mut out = Vec::new();
        let mut per = try!(self.parse_periodic());
        out.append(&mut per);

        // TODO: Make this cleaner
        if !self.eof() && (self.peek_char().is_alphabetic() || self.peek_char() == '(') {
            let mut molecule = try!(self.parse_molecule());
            out.append(&mut molecule);
        }
        if !self.eof() && self.peek_char() == ')' && self.paren_level == 0 {
            Err(CTError {
                kind: InputError,
                desc: "Missing opening parentheses".to_string(),
                pos: Some((self.pos, 1))
            })
        } else if !self.eof() && !self.on_legal_char() {
            Err(CTError {
                kind: InputError,
                desc: "Unexpected character".to_string(),
                pos: Some((self.pos, 1))
            })
        } else {
            Ok(out)
        }
    }

    fn parse_periodic(&mut self) -> CTResult<Vec<PerElem>> {
        let mut elem = try!(self.parse_element());

        if !self.eof() && self.peek_char().is_numeric() {
            let coef = try!(self.parse_coefficient());
            for e in elem.iter_mut() {
                e.coef *= coef;
            }
        }

        Ok(elem)
    }

    fn parse_element(&mut self) -> CTResult<Vec<PerElem>> {
        if self.eof() {
            return Err(CTError {
                kind: InputError,
                desc: "Found no periodic element".to_string(),
                pos: Some((self.pos, 1))
            });
        }
        // we store the current position here, so the consumes don't mess it up
        let start_pos = self.pos;
        let first = self.consume_char();
        if first == '(' {
            self.paren_level += 1;
            let molecule = try!(self.parse_molecule());
            if self.eof() || self.consume_char() != ')' {
                Err(CTError {
                    kind: InputError,
                    desc: "Missing closing parentheses".to_string(),
                    pos: Some((self.pos - 1, 1))
                })
            } else {
                self.paren_level -= 1;
                Ok(molecule)
            }
        } else if first.is_uppercase() {
            let mut name = String::new();
            name.push(first);
            name.push_str(self.consume_while(|ch| ch.is_lowercase()).as_slice());
            let len = name.len();
            Ok(vec!(PerElem { name: name, coef: 1, pos: start_pos, len: len }))
        } else {
            Err(CTError {
                kind: InputError,
                desc: "Missing uppercase letter at the beginning of the element".to_string(),
                pos: Some((self.pos - 1, 1))
            })
        }
    }

    fn parse_coefficient(&mut self) -> CTResult<u32> {
        let start_pos = self.pos;
        let num_str = self.consume_while(|ch| ch.is_numeric());
        if let Ok(num) = num_str.parse::<u32>() {
            Ok(num)
        } else {
            Err(CTError {
                kind: InputError,
                desc: "Could not parse coefficient".to_string(),
                pos: Some((start_pos, num_str.len()))
            })
        }
    }

    fn peek_char(&self) -> char {
        self.input.char_at(self.pos)
    }

    fn consume_char(&mut self) -> char {
        let CharRange { ch, next } = self.input.char_range_at(self.pos);
        self.pos = next;
        ch
    }

    fn consume_while<F>(&mut self, pred: F) -> String where F: Fn(char) -> bool {
        let mut out = String::new();
        while !self.eof() && pred(self.peek_char()) {
            out.push(self.consume_char());
        }
        out
    }

    fn consume_whitespace(&mut self) {
        self.consume_while(|ch| ch.is_whitespace());
    }

    fn eof(&mut self) -> bool {
        self.pos >= self.input.len()
    }

    fn on_legal_char(&self) -> bool {
        match self.peek_char() {
            ch if ch.is_alphanumeric() => true,
            '+' | '-' | '>' | '(' | ')' | ' ' => true,
            _ => false,
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use elem::PerElem;

    macro_rules! check_raw_result(
        ($raw:expr, $expected:expr) => (
            if let Ok(result) = $raw {
                assert_eq!(result, $expected);
            } else {
                panic!($raw);
            }
        )
    );

    #[test]
    fn elems() {
        let mut parser = Parser::new("CHeH");
        let raw_result = parser.parse_molecule();
        let expected = vec!(PerElem { name: "C".to_string(), coef: 1, pos: 0, len: 1 },
                            PerElem { name: "He".to_string(), coef: 1, pos: 1, len: 2 },
                            PerElem { name: "H".to_string(), coef: 1, pos: 3, len: 1 });
        check_raw_result!(raw_result, expected);
    }

    #[test]
    fn coefs() {
        let mut parser = Parser::new("C23");
        let raw_result = parser.parse_molecule();
        let expected = vec!(PerElem { name: "C".to_string(), coef: 23, pos: 0, len: 1 });
        check_raw_result!(raw_result, expected);
    }

    #[test]
    fn parens() {
        let mut parser = Parser::new("(CH3)2");
        let raw_result = parser.parse_molecule();
        let expected = vec!(PerElem { name: "C".to_string(), coef: 2, pos: 1, len: 1 },
                            PerElem { name: "H".to_string(), coef: 6, pos: 2, len: 1 });
        check_raw_result!(raw_result, expected);
    }

    #[test]
    fn multiple_elems() {
        let mut parser = Parser::new("C + H");
        let raw_result = parser.parse_side();
        let expected = vec!(vec!(PerElem { name: "C".to_string(), coef: 1, pos: 0, len: 1 }),
                            vec!(PerElem { name: "H".to_string(), coef: 1, pos: 4, len: 1 }));
        check_raw_result!(raw_result, expected);
    }

    #[test]
    fn reaction() {
        let mut parser = Parser::new("C -> H");
        let raw_result = parser.parse_reaction();
        let expected = (vec!(vec!(PerElem { name: "C".to_string(), coef: 1, pos: 0, len: 1 })),
                        vec!(vec!(PerElem { name: "H".to_string(), coef: 1, pos: 5, len: 1 })));
        check_raw_result!(raw_result, expected);
    }

    #[test]
    fn empty() {
        let mut parser = Parser::new("");
        assert!(parser.parse_molecule().is_err());
        assert!(parser.parse_reaction().is_err());
    }

    #[test]
    fn no_uppercase() {
        let mut parser = Parser::new("c");
        assert!(parser.parse_molecule().is_err());
        assert!(parser.parse_reaction().is_err());
    }

    #[test]
    fn missing_close_paren() {
        let mut parser = Parser::new("(C");
        assert!(parser.parse_molecule().is_err());
        assert!(parser.parse_reaction().is_err());
    }

    #[test]
    fn missing_open_paren() {
        let mut parser = Parser::new("C)");
        assert!(parser.parse_molecule().is_err());
        assert!(parser.parse_reaction().is_err());
    }

    #[test]
    fn invald_char() {
        let mut parser = Parser::new("%");
        assert!(parser.parse_molecule().is_err());
        assert!(parser.parse_reaction().is_err());
    }

    #[test]
    fn is_done() {
        let parser = Parser::new("    ");
        assert!(parser.is_done());
    }

    #[test]
    fn not_done() {
        let parser = Parser::new("    C");
        assert!(!parser.is_done());
    }

    #[test]
    fn invald_num() {
        let mut parser = Parser::new("C999999999999999999999");
        assert!(parser.parse_molecule().is_err());
        assert!(parser.parse_reaction().is_err());
    }

    #[test]
    fn dangling_plus() {
        let mut parser = Parser::new("C + -> H");
        assert!(parser.parse_reaction().is_err());
    }
}<|endoftext|>


<commit_before>use std::io::{Error, ErrorKind};

use self::reader::LogReader;
use super::*;

pub struct LogIter {
    pub config: Config,
    pub segment_iter: Box<Iterator<Item = (Lsn, LogID)>>,
    pub segment_base: Option<LogID>,
    pub segment_len: usize,
    pub use_compression: bool,
    pub max_lsn: Lsn,
    pub cur_lsn: Lsn,
    pub trailer: Option<Lsn>,
}

impl Iterator for LogIter {
    type Item = (Lsn, LogID, Vec<u8>);

    fn next(&mut self) -> Option<Self::Item> {
        // If segment is None, get next on segment_iter, panic
        // if we can't read something we expect to be able to,
        // return None if there are no more remaining segments.
        loop {
            let invalid =
                !valid_entry_offset(self.cur_lsn as LogID, self.segment_len);
            if self.trailer.is_none() && invalid {
                // We've read to the end of a torn
                // segment and should stop now.
                return None;
            } else if self.segment_base.is_none() || invalid {
                if let Some((next_lsn, next_lid)) = self.segment_iter.next() {
                    assert!(
                        next_lsn + (self.segment_len as Lsn) >= self.cur_lsn,
                        "caller is responsible for providing segments \
                            that contain the initial cur_lsn value or higher"
                    );
                    if let Err(e) = self.read_segment(next_lsn, next_lid) {
                        debug!(
                            "hit snap while reading segments in \
                            iterator: {:?}",
                            e
                        );
                        return None;
                    }
                } else {
                    return None;
                }
            }

            if self.cur_lsn > self.max_lsn {
                // all done
                return None;
            }

            let lid = self.segment_base.unwrap() +
                (self.cur_lsn % self.segment_len as Lsn) as LogID;

            if self.max_lsn < lid as Lsn {
                // we've hit the end of the log.
                trace!(
                    "in LogIter::next self.max_lsn {} < lid {}",
                    self.max_lsn,
                    lid
                );
                return None;
            }

            if let Ok(f) = self.config.file() {
                match f.read_message(
                    lid,
                    self.segment_len,
                    self.use_compression,
                ) {
                    Ok(LogRead::Flush(lsn, buf, on_disk_len)) => {
                        trace!("read flush in LogIter::next");
                        self.cur_lsn += (MSG_HEADER_LEN + on_disk_len) as Lsn;
                        return Some((lsn, lid, buf));
                    }
                    Ok(LogRead::Failed(on_disk_len)) => {
                        trace!("read zeroed in LogIter::next");
                        self.cur_lsn += (MSG_HEADER_LEN + on_disk_len) as Lsn;
                    }
                    _ => {
                        trace!("read failed in LogIter::next");
                        if self.trailer.is_none() {
                            // This segment was torn, nothing left to read.
                            return None;
                        }
                        self.segment_base.take();
                        self.trailer.take();
                        continue;
                    }
                }
            } else {
                return None;
            }
        }
    }
}

impl LogIter {
    /// read a segment of log messages. Only call after
    /// pausing segment rewriting on the segment accountant!
    fn read_segment(&mut self, lsn: Lsn, offset: LogID) -> CacheResult<(), ()> {
        trace!(
            "LogIter::read_segment lsn: {:?} cur_lsn: {:?}",
            lsn,
            self.cur_lsn
        );
        // we add segment_len to this check because we may be getting the
        // initial segment that is a bit behind where we left off before.
        assert!(lsn + self.segment_len as Lsn >= self.cur_lsn);
        let f = self.config.file()?;
        let segment_header = f.read_segment_header(offset)?;
        assert_eq!(offset % self.segment_len as LogID, 0);
        assert_eq!(
            segment_header.lsn % self.segment_len as Lsn,
            0,
            "expected a segment header lsn that is divisible \
            by the io_buf_size ({}) instead it was {}",
            self.segment_len,
            segment_header.lsn
        );

        if segment_header.lsn != lsn {
            // this page was torn, nothing to read
            error!(
                "segment header lsn ({}) != expected lsn ({})",
                segment_header.lsn,
                lsn
            );
            return Err(
                Error::new(ErrorKind::Other, "encountered torn segment")
                    .into(),
            );
        }

        let trailer_offset = offset + self.segment_len as LogID -
            SEG_TRAILER_LEN as LogID;
        let trailer_lsn = segment_header.lsn + self.segment_len as Lsn -
            SEG_TRAILER_LEN as Lsn;

        trace!("trying to read trailer from {}", trailer_offset);
        let segment_trailer = f.read_segment_trailer(trailer_offset);

        trace!("read segment header {:?}", segment_header);
        trace!("read segment trailer {:?}", segment_trailer);

        let trailer_lsn = segment_trailer.ok().and_then(|st| if st.ok &&
            st.lsn == trailer_lsn
        {
            Some(st.lsn)
        } else {
            None
        });

        self.trailer = trailer_lsn;
        self.cur_lsn = segment_header.lsn + SEG_HEADER_LEN as Lsn;
        self.segment_base = Some(offset);

        Ok(())
    }
}

fn valid_entry_offset(lid: LogID, segment_len: usize) -> bool {
    let seg_start = lid / segment_len as LogID * segment_len as LogID;

    let max_lid = seg_start + segment_len as LogID -
        SEG_TRAILER_LEN as LogID - MSG_HEADER_LEN as LogID;

    let min_lid = seg_start + SEG_HEADER_LEN as LogID;

    lid >= min_lid && lid <= max_lid
}
<commit_msg>gracefully handle log overreads in iterator<commit_after>use std::io;

use self::reader::LogReader;
use super::*;

pub struct LogIter {
    pub config: Config,
    pub segment_iter: Box<Iterator<Item = (Lsn, LogID)>>,
    pub segment_base: Option<LogID>,
    pub segment_len: usize,
    pub use_compression: bool,
    pub max_lsn: Lsn,
    pub cur_lsn: Lsn,
    pub trailer: Option<Lsn>,
}

impl Iterator for LogIter {
    type Item = (Lsn, LogID, Vec<u8>);

    fn next(&mut self) -> Option<Self::Item> {
        // If segment is None, get next on segment_iter, panic
        // if we can't read something we expect to be able to,
        // return None if there are no more remaining segments.
        loop {
            let invalid =
                !valid_entry_offset(self.cur_lsn as LogID, self.segment_len);
            if self.trailer.is_none() && invalid {
                // We've read to the end of a torn
                // segment and should stop now.
                return None;
            } else if self.segment_base.is_none() || invalid {
                if let Some((next_lsn, next_lid)) = self.segment_iter.next() {
                    assert!(
                        next_lsn + (self.segment_len as Lsn) >= self.cur_lsn,
                        "caller is responsible for providing segments \
                            that contain the initial cur_lsn value or higher"
                    );
                    if let Err(e) = self.read_segment(next_lsn, next_lid) {
                        debug!(
                            "hit snap while reading segments in \
                            iterator: {:?}",
                            e
                        );
                        return None;
                    }
                } else {
                    return None;
                }
            }

            if self.cur_lsn > self.max_lsn {
                // all done
                return None;
            }

            let lid = self.segment_base.unwrap() +
                (self.cur_lsn % self.segment_len as Lsn) as LogID;

            if self.max_lsn < lid as Lsn {
                // we've hit the end of the log.
                trace!(
                    "in LogIter::next self.max_lsn {} < lid {}",
                    self.max_lsn,
                    lid
                );
                return None;
            }

            if let Ok(f) = self.config.file() {
                match f.read_message(
                    lid,
                    self.segment_len,
                    self.use_compression,
                ) {
                    Ok(LogRead::Flush(lsn, buf, on_disk_len)) => {
                        trace!("read flush in LogIter::next");
                        self.cur_lsn += (MSG_HEADER_LEN + on_disk_len) as Lsn;
                        return Some((lsn, lid, buf));
                    }
                    Ok(LogRead::Failed(on_disk_len)) => {
                        trace!("read zeroed in LogIter::next");
                        self.cur_lsn += (MSG_HEADER_LEN + on_disk_len) as Lsn;
                    }
                    _ => {
                        trace!("read failed in LogIter::next");
                        if self.trailer.is_none() {
                            // This segment was torn, nothing left to read.
                            return None;
                        }
                        self.segment_base.take();
                        self.trailer.take();
                        continue;
                    }
                }
            } else {
                return None;
            }
        }
    }
}

impl LogIter {
    /// read a segment of log messages. Only call after
    /// pausing segment rewriting on the segment accountant!
    fn read_segment(&mut self, lsn: Lsn, offset: LogID) -> CacheResult<(), ()> {
        trace!(
            "LogIter::read_segment lsn: {:?} cur_lsn: {:?}",
            lsn,
            self.cur_lsn
        );
        // we add segment_len to this check because we may be getting the
        // initial segment that is a bit behind where we left off before.
        assert!(lsn + self.segment_len as Lsn >= self.cur_lsn);
        let f = self.config.file()?;
        let segment_header = f.read_segment_header(offset)?;
        if offset % self.segment_len as LogID != 0 {
            debug!("segment offset not divisible by segment length");
            return Err(Error::Corruption {
                at: offset,
            });
        }
        if segment_header.lsn % self.segment_len as Lsn != 0 {
            debug!(
                "expected a segment header lsn that is divisible \
            by the io_buf_size ({}) instead it was {}",
                self.segment_len,
                segment_header.lsn
            );
            return Err(Error::Corruption {
                at: offset,
            });
        }

        if segment_header.lsn != lsn {
            // this page was torn, nothing to read
            error!(
                "segment header lsn ({}) != expected lsn ({})",
                segment_header.lsn,
                lsn
            );
            return Err(
                io::Error::new(
                    io::ErrorKind::Other,
                    "encountered torn segment",
                ).into(),
            );
        }

        let trailer_offset = offset + self.segment_len as LogID -
            SEG_TRAILER_LEN as LogID;
        let trailer_lsn = segment_header.lsn + self.segment_len as Lsn -
            SEG_TRAILER_LEN as Lsn;

        trace!("trying to read trailer from {}", trailer_offset);
        let segment_trailer = f.read_segment_trailer(trailer_offset);

        trace!("read segment header {:?}", segment_header);
        trace!("read segment trailer {:?}", segment_trailer);

        let trailer_lsn = segment_trailer.ok().and_then(|st| if st.ok &&
            st.lsn == trailer_lsn
        {
            Some(st.lsn)
        } else {
            None
        });

        self.trailer = trailer_lsn;
        self.cur_lsn = segment_header.lsn + SEG_HEADER_LEN as Lsn;
        self.segment_base = Some(offset);

        Ok(())
    }
}

fn valid_entry_offset(lid: LogID, segment_len: usize) -> bool {
    let seg_start = lid / segment_len as LogID * segment_len as LogID;

    let max_lid = seg_start + segment_len as LogID -
        SEG_TRAILER_LEN as LogID - MSG_HEADER_LEN as LogID;

    let min_lid = seg_start + SEG_HEADER_LEN as LogID;

    lid >= min_lid && lid <= max_lid
}
<|endoftext|>


<commit_before>//! This module defines Concrete Syntax Tree (CST), used by rust-analyzer.
//!
//! The CST includes comments and whitespace, provides a single node type,
//! `SyntaxNode`, and a basic traversal API (parent, children, siblings).
//!
//! The *real* implementation is in the (language-agnostic) `rowan` crate, this
//! module just wraps its API.

pub(crate) use rowan::{GreenNode, GreenToken};
use rowan::{GreenNodeBuilder, Language};

use crate::{Parse, SmolStr, SyntaxError, SyntaxKind, TextUnit};

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum RustLanguage {}
impl Language for RustLanguage {
    type Kind = SyntaxKind;

    fn kind_from_raw(raw: rowan::SyntaxKind) -> SyntaxKind {
        SyntaxKind::from(raw.0)
    }

    fn kind_to_raw(kind: SyntaxKind) -> rowan::SyntaxKind {
        rowan::SyntaxKind(kind.into())
    }
}

pub type SyntaxNode = rowan::SyntaxNode<RustLanguage>;
pub type SyntaxToken = rowan::SyntaxToken<RustLanguage>;
pub type SyntaxElement = rowan::SyntaxElement<RustLanguage>;
pub type SyntaxNodeChildren = rowan::SyntaxNodeChildren<RustLanguage>;
pub type SyntaxElementChildren = rowan::SyntaxElementChildren<RustLanguage>;

pub use rowan::{Direction, NodeOrToken};

#[derive(Default)]
pub struct SyntaxTreeBuilder {
    errors: Vec<SyntaxError>,
    inner: GreenNodeBuilder<'static>,
}

impl SyntaxTreeBuilder {
    pub(crate) fn finish_raw(self) -> (GreenNode, Vec<SyntaxError>) {
        let green = self.inner.finish();
        (green, self.errors)
    }

    pub fn finish(self) -> Parse<SyntaxNode> {
        let (green, errors) = self.finish_raw();
        let node = SyntaxNode::new_root(green);
        if cfg!(debug_assertions) {
            crate::validation::validate_block_structure(&node);
        }
        Parse::new(node.green().clone(), errors)
    }

    pub fn token(&mut self, kind: SyntaxKind, text: SmolStr) {
        let kind = RustLanguage::kind_to_raw(kind);
        self.inner.token(kind, text)
    }

    pub fn start_node(&mut self, kind: SyntaxKind) {
        let kind = RustLanguage::kind_to_raw(kind);
        self.inner.start_node(kind)
    }

    pub fn finish_node(&mut self) {
        self.inner.finish_node()
    }

    pub fn error(&mut self, error: ra_parser::ParseError, text_pos: TextUnit) {
        self.errors.push(SyntaxError::new_at_offset(error.0, text_pos))
    }
}
<commit_msg>ra_syntax: moved reexport declaration after imports as per matklad<commit_after>//! This module defines Concrete Syntax Tree (CST), used by rust-analyzer.
//!
//! The CST includes comments and whitespace, provides a single node type,
//! `SyntaxNode`, and a basic traversal API (parent, children, siblings).
//!
//! The *real* implementation is in the (language-agnostic) `rowan` crate, this
//! module just wraps its API.

use rowan::{GreenNodeBuilder, Language};

use crate::{Parse, SmolStr, SyntaxError, SyntaxKind, TextUnit};

pub(crate) use rowan::{GreenNode, GreenToken};

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum RustLanguage {}
impl Language for RustLanguage {
    type Kind = SyntaxKind;

    fn kind_from_raw(raw: rowan::SyntaxKind) -> SyntaxKind {
        SyntaxKind::from(raw.0)
    }

    fn kind_to_raw(kind: SyntaxKind) -> rowan::SyntaxKind {
        rowan::SyntaxKind(kind.into())
    }
}

pub type SyntaxNode = rowan::SyntaxNode<RustLanguage>;
pub type SyntaxToken = rowan::SyntaxToken<RustLanguage>;
pub type SyntaxElement = rowan::SyntaxElement<RustLanguage>;
pub type SyntaxNodeChildren = rowan::SyntaxNodeChildren<RustLanguage>;
pub type SyntaxElementChildren = rowan::SyntaxElementChildren<RustLanguage>;

pub use rowan::{Direction, NodeOrToken};

#[derive(Default)]
pub struct SyntaxTreeBuilder {
    errors: Vec<SyntaxError>,
    inner: GreenNodeBuilder<'static>,
}

impl SyntaxTreeBuilder {
    pub(crate) fn finish_raw(self) -> (GreenNode, Vec<SyntaxError>) {
        let green = self.inner.finish();
        (green, self.errors)
    }

    pub fn finish(self) -> Parse<SyntaxNode> {
        let (green, errors) = self.finish_raw();
        let node = SyntaxNode::new_root(green);
        if cfg!(debug_assertions) {
            crate::validation::validate_block_structure(&node);
        }
        Parse::new(node.green().clone(), errors)
    }

    pub fn token(&mut self, kind: SyntaxKind, text: SmolStr) {
        let kind = RustLanguage::kind_to_raw(kind);
        self.inner.token(kind, text)
    }

    pub fn start_node(&mut self, kind: SyntaxKind) {
        let kind = RustLanguage::kind_to_raw(kind);
        self.inner.start_node(kind)
    }

    pub fn finish_node(&mut self) {
        self.inner.finish_node()
    }

    pub fn error(&mut self, error: ra_parser::ParseError, text_pos: TextUnit) {
        self.errors.push(SyntaxError::new_at_offset(error.0, text_pos))
    }
}
<|endoftext|>