<commit_before>use std::ops::{Deref, DerefMut};
use rustwlc::Geometry;
use super::super::borders::Borders;
use ::render::{BaseDraw, Drawable, DrawErr};
use super::super::container::Layout;

/// Draws the borders around the container.
/// The only borders that are drawn are for the title bars, as the individual
/// edge borders are handled by the child views.
pub struct ContainerDraw {
    base: BaseDraw<Borders>,
}

impl ContainerDraw {
    pub fn new(base: BaseDraw<Borders>) -> Self {
        ContainerDraw {
            base
        }
    }

    fn draw_title_bar(mut self,
                      mut x: f64,
                      mut w: f64,) -> Result<Self, DrawErr<Borders>> {
        let gap = Borders::gap_size() as f64;
        let title_size = Borders::title_bar_size() as f64;
        let title_color = self.base.inner().title_background_color();
        let title_font_color = self.base.inner().title_font_color();
        if x < 0.0 {
            w += x;
        }
        let title_x = Borders::thickness() as f64 + gap / 2.0;
        let title_y = title_size - 5.0;
        x = gap / 2.0;
        w -= gap;

        // Children data necessary for tabbed and stacked containers
        let child_data = self.base.inner().children.as_ref().map(
            |children| (children.titles.len(), children.index)
        );

        // For some reason, when changing a container to be tabbed or stacked,
        // the borders are drawn when the layout is set but the children are
        // not, so the layout being Tabbed/Stacked doesn't guarantee having
        // the children information.
        match (self.base.inner().layout, child_data) {
            (Some(Layout::Tabbed), Some((child_count, active_index))) => {

                let tab_width = w / child_count as f64;

                // Iterate over the indices to not borrow self
                for i in 0..child_count {
                    let active = i == active_index;

                    let title = match self.base.inner().children.as_ref() {
                        Some(children) => children.titles[i].clone(),
                        None => unreachable!()
                    };

                    let title_color = if active { title_color }
                        else { Borders::default_title_color() };

                    let title_font_color = if active { title_font_color }
                        else { Borders::default_title_font_color() };

                    let x = x + tab_width*i as f64;
                    let title_x = title_x + tab_width*i as f64;

                    // Draw background of title bar
                    self.base.set_source_rgb(1.0, 0.0, 0.0);
                    self.base.set_color_source(title_color);
                    self.base.rectangle(x, 0.0, tab_width, title_size);
                    self.base = try!(self.base.check_cairo());
                    self.base.fill();
                    self.base = try!(self.base.check_cairo());

                    // Draw title text
                    self.base.move_to(title_x, title_y);
                    self.base = try!(self.base.check_cairo());
                    self.base.set_color_source(title_font_color);
                    self.base = try!(self.base.check_cairo());
                    self.base.show_text(title.as_str());
                    self.base = try!(self.base.check_cairo());
                }
            }
            (Some(Layout::Stacked), Some((child_count, active_index))) => {

                // Iterate over the indices to not borrow self
                for i in 0..child_count {
                    let active = i == active_index;

                    let title = match self.base.inner().children.as_ref() {
                        Some(children) => children.titles[i].clone(),
                        None => unreachable!()
                    };

                    let title_color = if active { title_color }
                        else { Borders::default_title_color() };

                    let title_font_color = if active { title_font_color }
                        else { Borders::default_title_font_color() };

                    let y = title_size*i as f64;
                    let title_y = title_y as f64 + y;

                    // Draw background of title bar
                    self.base.set_source_rgb(1.0, 0.0, 0.0);
                    self.base.set_color_source(title_color);
                    self.base.rectangle(x, y, w, title_size);
                    self.base = try!(self.base.check_cairo());
                    self.base.fill();
                    self.base = try!(self.base.check_cairo());

                    // Draw title text
                    self.base.move_to(title_x, title_y);
                    self.base = try!(self.base.check_cairo());
                    self.base.set_color_source(title_font_color);
                    self.base = try!(self.base.check_cairo());
                    self.base.show_text(title.as_str());
                    self.base = try!(self.base.check_cairo());
                }
            }
            _ => {
                let title: String = self.inner().title().into();

                // Draw background of title bar
                self.base.set_source_rgb(1.0, 0.0, 0.0);
                self.base.set_color_source(title_color);
                self.base.rectangle(x, 0.0, w, title_size);
                self.base = try!(self.base.check_cairo());
                self.base.fill();
                self.base = try!(self.base.check_cairo());

                // Draw title text
                self.base.move_to(title_x, title_y);
                self.base = try!(self.base.check_cairo());
                self.base.set_color_source(title_font_color);
                self.base = try!(self.base.check_cairo());
                self.base.show_text(title.as_str());
                self.base = try!(self.base.check_cairo());
            }
        }

        Ok(self)
    }
}

impl Drawable<Borders> for ContainerDraw {
    // Draw the title bar around the container
    fn draw(mut self, container_g: Geometry) -> Result<Borders, DrawErr<Borders>> {
        let mut border_g = container_g;
        let thickness = Borders::thickness();

        let title_count = match (self.inner().layout, &self.inner().children) {
            (Some(Layout::Stacked), &Some(ref children)) =>
                // I don't understand why I have to use this
                // instead of just child_count, but seems to work...
                2 * children.titles.len() as u32 - 1,
            _ => 1
        };

        let title_size = Borders::title_bar_size() * title_count;
        let edge_thickness = thickness / 2;

        border_g.origin.x -= edge_thickness as i32;
        border_g.origin.y -= edge_thickness as i32;
        border_g.origin.y -= (title_size / 2) as i32;
        border_g.size.w += thickness;
        border_g.size.h = edge_thickness + title_size/2;

        self.base.set_source_rgba(0.0, 0.0, 0.0, 0.0);
        self.base.paint();
        let color = self.base.inner().color();
        self.base.set_color_source(color);

        let (x, w) = (border_g.origin.x as f64,
                      border_g.size.w as f64);
        self = self.draw_title_bar(x, w)?;
        Ok(self.base.finish(border_g))
    }
}

impl Deref for ContainerDraw {
    type Target = BaseDraw<Borders>;

    fn deref(&self) -> &BaseDraw<Borders> {
        &self.base
    }
}

impl DerefMut for ContainerDraw {
    fn deref_mut(&mut self) -> &mut BaseDraw<Borders> {
        &mut self.base
    }
}
<commit_msg>Avoid String copying<commit_after>use std::ops::{Deref, DerefMut};
use rustwlc::Geometry;
use super::super::borders::Borders;
use ::render::{BaseDraw, Drawable, DrawErr};
use super::super::container::Layout;

/// Draws the borders around the container.
/// The only borders that are drawn are for the title bars, as the individual
/// edge borders are handled by the child views.
pub struct ContainerDraw {
    base: BaseDraw<Borders>,
}

impl ContainerDraw {
    pub fn new(base: BaseDraw<Borders>) -> Self {
        ContainerDraw {
            base
        }
    }

    fn draw_title_bar(mut self,
                      mut x: f64,
                      mut w: f64,) -> Result<Self, DrawErr<Borders>> {
        let gap = Borders::gap_size() as f64;
        let title_size = Borders::title_bar_size() as f64;
        let title_color = self.base.inner().title_background_color();
        let title_font_color = self.base.inner().title_font_color();
        if x < 0.0 {
            w += x;
        }
        let title_x = Borders::thickness() as f64 + gap / 2.0;
        let title_y = title_size - 5.0;
        x = gap / 2.0;
        w -= gap;

        // Children data necessary for tabbed and stacked containers
        let child_data = self.base.inner().children.as_ref().map(
            |children| (children.titles.len(), children.index)
        );

        // For some reason, when changing a container to be tabbed or stacked,
        // the borders are drawn when the layout is set but the children are
        // not, so the layout being Tabbed/Stacked doesn't guarantee having
        // the children information.
        match (self.base.inner().layout, child_data) {
            (Some(Layout::Tabbed), Some((child_count, active_index))) => {

                let tab_width = w / child_count as f64;

                // Iterate over the indices to not borrow self
                for i in 0..child_count {
                    let active = i == active_index;

                    let title_color = if active { title_color }
                        else { Borders::default_title_color() };

                    let title_font_color = if active { title_font_color }
                        else { Borders::default_title_font_color() };

                    let x = x + tab_width*i as f64;
                    let title_x = title_x + tab_width*i as f64;

                    // Draw background of title bar
                    self.base.set_source_rgb(1.0, 0.0, 0.0);
                    self.base.set_color_source(title_color);
                    self.base.rectangle(x, 0.0, tab_width, title_size);
                    self.base = try!(self.base.check_cairo());
                    self.base.fill();
                    self.base = try!(self.base.check_cairo());

                    // Draw title text
                    self.base.move_to(title_x, title_y);
                    self.base = try!(self.base.check_cairo());
                    self.base.set_color_source(title_font_color);
                    self.base = try!(self.base.check_cairo());
                    self.base.show_text(
                        match self.base.inner().children.as_ref() {
                            Some(children) => children.titles[i].as_str(),
                            None => unreachable!()
                        }
                    );
                    self.base = try!(self.base.check_cairo());
                }
            }
            (Some(Layout::Stacked), Some((child_count, active_index))) => {

                // Iterate over the indices to not borrow self
                for i in 0..child_count {
                    let active = i == active_index;

                    let title_color = if active { title_color }
                        else { Borders::default_title_color() };

                    let title_font_color = if active { title_font_color }
                        else { Borders::default_title_font_color() };

                    let y = title_size*i as f64;
                    let title_y = title_y as f64 + y;

                    // Draw background of title bar
                    self.base.set_source_rgb(1.0, 0.0, 0.0);
                    self.base.set_color_source(title_color);
                    self.base.rectangle(x, y, w, title_size);
                    self.base = try!(self.base.check_cairo());
                    self.base.fill();
                    self.base = try!(self.base.check_cairo());

                    // Draw title text
                    self.base.move_to(title_x, title_y);
                    self.base = try!(self.base.check_cairo());
                    self.base.set_color_source(title_font_color);
                    self.base = try!(self.base.check_cairo());
                    self.base.show_text(
                        match self.base.inner().children.as_ref() {
                            Some(children) => children.titles[i].as_str(),
                            None => unreachable!()
                        }
                    );
                    self.base = try!(self.base.check_cairo());
                }
            }
            _ => {
                // Draw background of title bar
                self.base.set_source_rgb(1.0, 0.0, 0.0);
                self.base.set_color_source(title_color);
                self.base.rectangle(x, 0.0, w, title_size);
                self.base = try!(self.base.check_cairo());
                self.base.fill();
                self.base = try!(self.base.check_cairo());

                // Draw title text
                self.base.move_to(title_x, title_y);
                self.base = try!(self.base.check_cairo());
                self.base.set_color_source(title_font_color);
                self.base = try!(self.base.check_cairo());
                self.base.show_text(self.inner().title());
                self.base = try!(self.base.check_cairo());
            }
        }

        Ok(self)
    }
}

impl Drawable<Borders> for ContainerDraw {
    // Draw the title bar around the container
    fn draw(mut self, container_g: Geometry) -> Result<Borders, DrawErr<Borders>> {
        let mut border_g = container_g;
        let thickness = Borders::thickness();

        let title_count = match (self.inner().layout, &self.inner().children) {
            (Some(Layout::Stacked), &Some(ref children)) =>
                // I don't understand why I have to use this
                // instead of just child_count, but seems to work...
                2 * children.titles.len() as u32 - 1,
            _ => 1
        };

        let title_size = Borders::title_bar_size() * title_count;
        let edge_thickness = thickness / 2;

        border_g.origin.x -= edge_thickness as i32;
        border_g.origin.y -= edge_thickness as i32;
        border_g.origin.y -= (title_size / 2) as i32;
        border_g.size.w += thickness;
        border_g.size.h = edge_thickness + title_size/2;

        self.base.set_source_rgba(0.0, 0.0, 0.0, 0.0);
        self.base.paint();
        let color = self.base.inner().color();
        self.base.set_color_source(color);

        let (x, w) = (border_g.origin.x as f64,
                      border_g.size.w as f64);
        self = self.draw_title_bar(x, w)?;
        Ok(self.base.finish(border_g))
    }
}

impl Deref for ContainerDraw {
    type Target = BaseDraw<Borders>;

    fn deref(&self) -> &BaseDraw<Borders> {
        &self.base
    }
}

impl DerefMut for ContainerDraw {
    fn deref_mut(&mut self) -> &mut BaseDraw<Borders> {
        &mut self.base
    }
}
<|endoftext|>


<commit_before>use std::str::FromStr;
use std::default::Default;

use rustc_serialize::{Decodable, Decoder};

/// Objects will be returned with a certain ResourceState
///
/// Detailed contains the most data and Meta the least.
#[allow(dead_code)]
#[derive(Debug, PartialEq, RustcEncodable)]
pub enum ResourceState {
    Unknown,
    Meta,
    Summary,
    Detailed
}

// TODO refactor primitive conversion into custom trait. Maybe add a macro or compiler plugin to
// handle this.
impl Decodable for ResourceState {
    fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
        let num = try!(d.read_u8());
        Ok(match num {
            0 => ResourceState::Unknown,
            1 => ResourceState::Meta,
            2 => ResourceState::Summary,
            3 => ResourceState::Detailed,
            _ =>  unreachable!("ResourceState only valid for 0,1,2,3")
        })
    }
}

impl Default for ResourceState {
    fn default () -> ResourceState { ResourceState::Unknown }
}

/// Frame type for bikes
#[allow(dead_code)]
#[derive(Debug)]
pub enum FrameType {
    MTB,
    Cross,
    Road,
    TimeTrial
}

/// Activity Types
#[allow(dead_code)]
#[derive(Debug)]
pub enum ActivityType {
    Ride,
    Run,
    Swim,
    Hike,
    Walk,
    AlpineSki,
    BackcountrySki,
    Canoeing,
    CrossCountrySkiing,
    Crossfit,
    Elliptical,
    IceSkate,
    InlineSkate,
    Kayaking,
    Kitesurf,
    NordicSki,
    RockClimbing,
    RollerSki,
    Rowing,
    Snowboard,
    Snowshoe,
    StairStepper,
    StandUpPaddling,
    Surfing,
    WeightTraining,
    Windsurf,
    Workout,
    Yoga
}

pub enum ValueError {
    InvalidValue,
    Other
}

// TODO this is really just for handling JSON conversion which can be done automatically with
// rustc_serialize::Decodable
impl FromStr for ActivityType {
    type Err = ValueError;

    fn from_str(s: &str) -> Result<ActivityType, ValueError> {
        match s {
            "Ride" => { Ok(ActivityType::Ride) },
            "Run" => { Ok(ActivityType::Run) },
            "Swim" => { Ok(ActivityType::Swim) },
            "Hike" => { Ok(ActivityType::Hike) },
            "Walk" => { Ok(ActivityType::Walk) },
            "AlpineSki" => { Ok(ActivityType::AlpineSki) },
            "BackcountrySki" => { Ok(ActivityType::BackcountrySki) },
            "Canoeing" => { Ok(ActivityType::Canoeing) },
            "CrossCountrySkiing" => { Ok(ActivityType::CrossCountrySkiing) },
            "Crossfit" => { Ok(ActivityType::Crossfit) },
            "Elliptical" => { Ok(ActivityType::Elliptical) },
            "IceSkate" => { Ok(ActivityType::IceSkate) },
            "InlineSkate" => { Ok(ActivityType::InlineSkate) },
            "Kayaking" => { Ok(ActivityType::Kayaking) },
            "Kitesurf" => { Ok(ActivityType::Kitesurf) },
            "NordicSki" => { Ok(ActivityType::NordicSki) },
            "RockClimbing" => { Ok(ActivityType::RockClimbing) },
            "RollerSki" => { Ok(ActivityType::RollerSki) },
            "Rowing" => { Ok(ActivityType::Rowing) },
            "Snowboard" => { Ok(ActivityType::Snowboard) },
            "Snowshoe" => { Ok(ActivityType::Snowshoe) },
            "StairStepper" => { Ok(ActivityType::StairStepper) },
            "StandUpPaddling" => { Ok(ActivityType::StandUpPaddling) },
            "Surfing" => { Ok(ActivityType::Surfing) },
            "WeightTraining" => { Ok(ActivityType::WeightTraining) },
            "Windsurf" => { Ok(ActivityType::Windsurf) },
            "Workout" => { Ok(ActivityType::Workout) },
            "Yoga" => { Ok(ActivityType::Yoga) }
            _ => { Err(ValueError::InvalidValue) }
        }
    }
}

/// Types of clubs
#[allow(dead_code)]
#[derive(Debug)]
pub enum ClubType {
    Casual,
    Racing,
    Triathlon,
    Other
}

/// Types of sports
#[allow(dead_code)]
#[derive(Debug)]
pub enum SportType {
    Cycling,
    Running,
    Triathlon,
    Other
}

#[allow(dead_code)]
#[derive(Debug)]
pub enum WorkoutType {
    Default,
    Race,
    Long,
    Intervals
}

#[cfg(test)]
mod tests {
    use std::default::Default;

    use super::ResourceState;

    #[test]
    fn resource_state_values() {
        assert_eq!(ResourceState::Meta as i32, 1);
        assert_eq!(ResourceState::Summary as i32, 2);
        assert_eq!(ResourceState::Detailed as i32, 3);
    }

    #[test]
    fn default_resource_state() {
        let default_state: ResourceState = Default::default();
        assert_eq!(default_state, ResourceState::Unknown);
    }
}
<commit_msg>impl rustc_serialize::Decodable for ActivityType<commit_after>use std::default::Default;

use rustc_serialize::{Decodable, Decoder};

/// Objects will be returned with a certain ResourceState
///
/// Detailed contains the most data and Meta the least.
#[allow(dead_code)]
#[derive(Debug, PartialEq, RustcEncodable)]
pub enum ResourceState {
    Unknown,
    Meta,
    Summary,
    Detailed
}

// TODO refactor primitive conversion into custom trait. Maybe add a macro or compiler plugin to
// handle this.
impl Decodable for ResourceState {
    fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
        let num = try!(d.read_u8());
        Ok(match num {
            0 => ResourceState::Unknown,
            1 => ResourceState::Meta,
            2 => ResourceState::Summary,
            3 => ResourceState::Detailed,
            _ =>  unreachable!("ResourceState only valid for 0,1,2,3")
        })
    }
}

impl Default for ResourceState {
    fn default () -> ResourceState { ResourceState::Unknown }
}

/// Frame type for bikes
#[allow(dead_code)]
#[derive(Debug)]
pub enum FrameType {
    MTB,
    Cross,
    Road,
    TimeTrial
}

/// Activity Types
#[allow(dead_code)]
#[derive(Debug)]
pub enum ActivityType {
    Ride,
    Run,
    Swim,
    Hike,
    Walk,
    AlpineSki,
    BackcountrySki,
    Canoeing,
    CrossCountrySkiing,
    Crossfit,
    Elliptical,
    IceSkate,
    InlineSkate,
    Kayaking,
    Kitesurf,
    NordicSki,
    RockClimbing,
    RollerSki,
    Rowing,
    Snowboard,
    Snowshoe,
    StairStepper,
    StandUpPaddling,
    Surfing,
    WeightTraining,
    Windsurf,
    Workout,
    Yoga,
    Unknown
}

impl Decodable for ActivityType {
    fn decode<D: Decoder>(d: &mut D) -> Result<ActivityType, D::Error> {
        d.read_str().map(|s| {
            match &s[..] {
                "Ride" => { ActivityType::Ride },
                "Run" => { ActivityType::Run },
                "Swim" => { ActivityType::Swim },
                "Hike" => { ActivityType::Hike },
                "Walk" => { ActivityType::Walk },
                "AlpineSki" => { ActivityType::AlpineSki },
                "BackcountrySki" => { ActivityType::BackcountrySki },
                "Canoeing" => { ActivityType::Canoeing },
                "CrossCountrySkiing" => { ActivityType::CrossCountrySkiing },
                "Crossfit" => { ActivityType::Crossfit },
                "Elliptical" => { ActivityType::Elliptical },
                "IceSkate" => { ActivityType::IceSkate },
                "InlineSkate" => { ActivityType::InlineSkate },
                "Kayaking" => { ActivityType::Kayaking },
                "Kitesurf" => { ActivityType::Kitesurf },
                "NordicSki" => { ActivityType::NordicSki },
                "RockClimbing" => { ActivityType::RockClimbing },
                "RollerSki" => { ActivityType::RollerSki },
                "Rowing" => { ActivityType::Rowing },
                "Snowboard" => { ActivityType::Snowboard },
                "Snowshoe" => { ActivityType::Snowshoe },
                "StairStepper" => { ActivityType::StairStepper },
                "StandUpPaddling" => { ActivityType::StandUpPaddling },
                "Surfing" => { ActivityType::Surfing },
                "WeightTraining" => { ActivityType::WeightTraining },
                "Windsurf" => { ActivityType::Windsurf },
                "Workout" => { ActivityType::Workout },
                "Yoga" => { ActivityType::Yoga }
                _ => { ActivityType::Unknown }
            }
        })
    }
}

pub enum ValueError {
    InvalidValue,
    Other
}

/// Types of clubs
#[allow(dead_code)]
#[derive(Debug)]
pub enum ClubType {
    Casual,
    Racing,
    Triathlon,
    Other
}

/// Types of sports
#[allow(dead_code)]
#[derive(Debug)]
pub enum SportType {
    Cycling,
    Running,
    Triathlon,
    Other
}

#[allow(dead_code)]
#[derive(Debug)]
pub enum WorkoutType {
    Default,
    Race,
    Long,
    Intervals
}

#[cfg(test)]
mod tests {
    use std::default::Default;

    use super::ResourceState;

    #[test]
    fn resource_state_values() {
        assert_eq!(ResourceState::Meta as i32, 1);
        assert_eq!(ResourceState::Summary as i32, 2);
        assert_eq!(ResourceState::Detailed as i32, 3);
    }

    #[test]
    fn default_resource_state() {
        let default_state: ResourceState = Default::default();
        assert_eq!(default_state, ResourceState::Unknown);
    }
}
<|endoftext|>


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

use std::io::Write;

use rustc::hir::def_id::DefId;
use rustc_serialize::json::as_json;

use external_data::*;
use data::VariableKind;
use dump::Dump;
use super::Format;

pub struct JsonDumper<'b, W: Write + 'b> {
    output: &'b mut W,
    result: Analysis,
}

impl<'b, W: Write> JsonDumper<'b, W> {
    pub fn new(writer: &'b mut W) -> JsonDumper<'b, W> {
        JsonDumper { output: writer, result: Analysis::new() }
    }
}

impl<'b, W: Write> Drop for JsonDumper<'b, W> {
    fn drop(&mut self) {
        if let Err(_) = write!(self.output, "{}", as_json(&self.result)) {
            error!("Error writing output");
        }
    }
}

macro_rules! impl_fn {
    ($fn_name: ident, $data_type: ident, $bucket: ident) => {
        fn $fn_name(&mut self, data: $data_type) {
            self.result.$bucket.push(From::from(data));
        }
    }
}

impl<'b, W: Write + 'b> Dump for JsonDumper<'b, W> {
    fn crate_prelude(&mut self, data: CratePreludeData) {
        self.result.prelude = Some(data)
    }

    impl_fn!(extern_crate, ExternCrateData, imports);
    impl_fn!(use_data, UseData, imports);
    impl_fn!(use_glob, UseGlobData, imports);

    impl_fn!(enum_data, EnumData, defs);
    impl_fn!(tuple_variant, TupleVariantData, defs);
    impl_fn!(struct_variant, StructVariantData, defs);
    impl_fn!(struct_data, StructData, defs);
    impl_fn!(trait_data, TraitData, defs);
    impl_fn!(function, FunctionData, defs);
    impl_fn!(method, MethodData, defs);
    impl_fn!(macro_data, MacroData, defs);
    impl_fn!(mod_data, ModData, defs);
    impl_fn!(typedef, TypeDefData, defs);
    impl_fn!(variable, VariableData, defs);

    impl_fn!(function_ref, FunctionRefData, refs);
    impl_fn!(function_call, FunctionCallData, refs);
    impl_fn!(method_call, MethodCallData, refs);
    impl_fn!(mod_ref, ModRefData, refs);
    impl_fn!(type_ref, TypeRefData, refs);
    impl_fn!(variable_ref, VariableRefData, refs);

    impl_fn!(macro_use, MacroUseData, macro_refs);

    // FIXME store this instead of throwing it away.
    fn impl_data(&mut self, _data: ImplData) {}
    fn inheritance(&mut self, _data: InheritanceData) {}
}

// FIXME do we want to change ExternalData to this mode? It will break DXR.
// FIXME methods. The defs have information about possible overriding and the
// refs have decl information (e.g., a trait method where we know the required
// method, but not the supplied method). In both cases, we are currently
// ignoring it.

#[derive(Debug, RustcEncodable)]
struct Analysis {
    kind: Format,
    prelude: Option<CratePreludeData>,
    imports: Vec<Import>,
    defs: Vec<Def>,
    refs: Vec<Ref>,
    macro_refs: Vec<MacroRef>,
}

impl Analysis {
    fn new() -> Analysis {
        Analysis {
            kind: Format::Json,
            prelude: None,
            imports: vec![],
            defs: vec![],
            refs: vec![],
            macro_refs: vec![],
        }
    }
}

// DefId::index is a newtype and so the JSON serialisation is ugly. Therefore
// we use our own Id which is the same, but without the newtype.
#[derive(Debug, RustcEncodable)]
struct Id {
    krate: u32,
    index: u32,
}

impl From<DefId> for Id {
    fn from(id: DefId) -> Id {
        Id {
            krate: id.krate.as_u32(),
            index: id.index.as_u32(),
        }
    }
}

#[derive(Debug, RustcEncodable)]
struct Import {
    kind: ImportKind,
    id: Id,
    span: SpanData,
    name: String,
    value: String,
}

#[derive(Debug, RustcEncodable)]
enum ImportKind {
    ExternCrate,
    Use,
    GlobUse,
}

impl From<ExternCrateData> for Import {
    fn from(data: ExternCrateData) -> Import {
        Import {
            kind: ImportKind::ExternCrate,
            id: From::from(data.id),
            span: data.span,
            name: data.name,
            value: String::new(),
        }
    }
}
impl From<UseData> for Import {
    fn from(data: UseData) -> Import {
        Import {
            kind: ImportKind::Use,
            id: From::from(data.id),
            span: data.span,
            name: data.name,
            value: String::new(),
        }
    }
}
impl From<UseGlobData> for Import {
    fn from(data: UseGlobData) -> Import {
        Import {
            kind: ImportKind::GlobUse,
            id: From::from(data.id),
            span: data.span,
            name: "*".to_owned(),
            value: data.names.join(", "),
        }
    }
}

#[derive(Debug, RustcEncodable)]
struct Def {
    kind: DefKind,
    id: Id,
    span: SpanData,
    name: String,
    qualname: String,
    value: String,
    children: Vec<Id>,
    decl_id: Option<Id>,
    docs: String,
}

#[derive(Debug, RustcEncodable)]
enum DefKind {
    // value = variant names
    Enum,
    // value = enum name + variant name + types
    Tuple,
    // value = [enum name +] name + fields
    Struct,
    // value = signature
    Trait,
    // value = type + generics
    Function,
    // value = type + generics
    Method,
    // No id, no value.
    Macro,
    // value = file_name
    Mod,
    // value = aliased type
    Type,
    // value = type and init expression (for all variable kinds).
    Local,
    Static,
    Const,
    Field,
}

impl From<EnumData> for Def {
    fn from(data: EnumData) -> Def {
        Def {
            kind: DefKind::Enum,
            id: From::from(data.id),
            span: data.span,
            name: data.name,
            qualname: data.qualname,
            value: data.value,
            children: data.variants.into_iter().map(|id| From::from(id)).collect(),
            decl_id: None,
            docs: data.docs,
        }
    }
}

impl From<TupleVariantData> for Def {
    fn from(data: TupleVariantData) -> Def {
        Def {
            kind: DefKind::Tuple,
            id: From::from(data.id),
            span: data.span,
            name: data.name,
            qualname: data.qualname,
            value: data.value,
            children: vec![],
            decl_id: None,
            docs: data.docs,
        }
    }
}
impl From<StructVariantData> for Def {
    fn from(data: StructVariantData) -> Def {
        Def {
            kind: DefKind::Struct,
            id: From::from(data.id),
            span: data.span,
            name: data.name,
            qualname: data.qualname,
            value: data.value,
            children: vec![],
            decl_id: None,
            docs: data.docs,
        }
    }
}
impl From<StructData> for Def {
    fn from(data: StructData) -> Def {
        Def {
            kind: DefKind::Struct,
            id: From::from(data.id),
            span: data.span,
            name: data.name,
            qualname: data.qualname,
            value: data.value,
            children: data.fields.into_iter().map(|id| From::from(id)).collect(),
            decl_id: None,
            docs: data.docs,
        }
    }
}
impl From<TraitData> for Def {
    fn from(data: TraitData) -> Def {
        Def {
            kind: DefKind::Trait,
            id: From::from(data.id),
            span: data.span,
            name: data.name,
            qualname: data.qualname,
            value: data.value,
            children: data.items.into_iter().map(|id| From::from(id)).collect(),
            decl_id: None,
            docs: data.docs,
        }
    }
}
impl From<FunctionData> for Def {
    fn from(data: FunctionData) -> Def {
        Def {
            kind: DefKind::Function,
            id: From::from(data.id),
            span: data.span,
            name: data.name,
            qualname: data.qualname,
            value: data.value,
            children: vec![],
            decl_id: None,
            docs: data.docs,
        }
    }
}
impl From<MethodData> for Def {
    fn from(data: MethodData) -> Def {
        Def {
            kind: DefKind::Method,
            id: From::from(data.id),
            span: data.span,
            name: data.name,
            qualname: data.qualname,
            value: data.value,
            children: vec![],
            decl_id: data.decl_id.map(|id| From::from(id)),
            docs: data.docs,
        }
    }
}
impl From<MacroData> for Def {
    fn from(data: MacroData) -> Def {
        Def {
            kind: DefKind::Macro,
            id: From::from(null_def_id()),
            span: data.span,
            name: data.name,
            qualname: data.qualname,
            value: String::new(),
            children: vec![],
            decl_id: None,
            docs: data.docs,
        }
    }
}
impl From<ModData> for Def {
    fn from(data:ModData) -> Def {
        Def {
            kind: DefKind::Mod,
            id: From::from(data.id),
            span: data.span,
            name: data.name,
            qualname: data.qualname,
            value: data.filename,
            children: data.items.into_iter().map(|id| From::from(id)).collect(),
            decl_id: None,
            docs: data.docs,
        }
    }
}
impl From<TypeDefData> for Def {
    fn from(data: TypeDefData) -> Def {
        Def {
            kind: DefKind::Type,
            id: From::from(data.id),
            span: data.span,
            name: data.name,
            qualname: data.qualname,
            value: data.value,
            children: vec![],
            decl_id: None,
            docs: String::new(),
        }
    }
}
impl From<VariableData> for Def {
    fn from(data: VariableData) -> Def {
        Def {
            kind: match data.kind {
                VariableKind::Static => DefKind::Static,
                VariableKind::Const => DefKind::Const,
                VariableKind::Local => DefKind::Local,
                VariableKind::Field => DefKind::Field,
            },
            id: From::from(data.id),
            span: data.span,
            name: data.name,
            qualname: data.qualname,
            value: data.type_value,
            children: vec![],
            decl_id: None,
            docs: data.docs,
        }
    }
}

#[derive(Debug, RustcEncodable)]
enum RefKind {
    Function,
    Mod,
    Type,
    Variable,
}

#[derive(Debug, RustcEncodable)]
struct Ref {
    kind: RefKind,
    span: SpanData,
    ref_id: Id,
}

impl From<FunctionRefData> for Ref {
    fn from(data: FunctionRefData) -> Ref {
        Ref {
            kind: RefKind::Function,
            span: data.span,
            ref_id: From::from(data.ref_id),
        }
    }
}
impl From<FunctionCallData> for Ref {
    fn from(data: FunctionCallData) -> Ref {
        Ref {
            kind: RefKind::Function,
            span: data.span,
            ref_id: From::from(data.ref_id),
        }
    }
}
impl From<MethodCallData> for Ref {
    fn from(data: MethodCallData) -> Ref {
        Ref {
            kind: RefKind::Function,
            span: data.span,
            ref_id: From::from(data.ref_id.or(data.decl_id).unwrap_or(null_def_id())),
        }
    }
}
impl From<ModRefData> for Ref {
    fn from(data: ModRefData) -> Ref {
        Ref {
            kind: RefKind::Mod,
            span: data.span,
            ref_id: From::from(data.ref_id.unwrap_or(null_def_id())),
        }
    }
}
impl From<TypeRefData> for Ref {
    fn from(data: TypeRefData) -> Ref {
        Ref {
            kind: RefKind::Type,
            span: data.span,
            ref_id: From::from(data.ref_id.unwrap_or(null_def_id())),
        }
    }
}
impl From<VariableRefData> for Ref {
    fn from(data: VariableRefData) -> Ref {
        Ref {
            kind: RefKind::Variable,
            span: data.span,
            ref_id: From::from(data.ref_id),
        }
    }
}

#[derive(Debug, RustcEncodable)]
struct MacroRef {
    span: SpanData,
    qualname: String,
    callee_span: SpanData,
}

impl From<MacroUseData> for MacroRef {
    fn from(data: MacroUseData) -> MacroRef {
        MacroRef {
            span: data.span,
            qualname: data.qualname,
            callee_span: data.callee_span,
        }
    }
}
<commit_msg>save-analysis: change imports to carry a ref id rather than their own node id<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::io::Write;

use rustc::hir::def_id::DefId;
use rustc_serialize::json::as_json;

use external_data::*;
use data::VariableKind;
use dump::Dump;
use super::Format;

pub struct JsonDumper<'b, W: Write + 'b> {
    output: &'b mut W,
    result: Analysis,
}

impl<'b, W: Write> JsonDumper<'b, W> {
    pub fn new(writer: &'b mut W) -> JsonDumper<'b, W> {
        JsonDumper { output: writer, result: Analysis::new() }
    }
}

impl<'b, W: Write> Drop for JsonDumper<'b, W> {
    fn drop(&mut self) {
        if let Err(_) = write!(self.output, "{}", as_json(&self.result)) {
            error!("Error writing output");
        }
    }
}

macro_rules! impl_fn {
    ($fn_name: ident, $data_type: ident, $bucket: ident) => {
        fn $fn_name(&mut self, data: $data_type) {
            self.result.$bucket.push(From::from(data));
        }
    }
}

impl<'b, W: Write + 'b> Dump for JsonDumper<'b, W> {
    fn crate_prelude(&mut self, data: CratePreludeData) {
        self.result.prelude = Some(data)
    }

    impl_fn!(extern_crate, ExternCrateData, imports);
    impl_fn!(use_data, UseData, imports);
    impl_fn!(use_glob, UseGlobData, imports);

    impl_fn!(enum_data, EnumData, defs);
    impl_fn!(tuple_variant, TupleVariantData, defs);
    impl_fn!(struct_variant, StructVariantData, defs);
    impl_fn!(struct_data, StructData, defs);
    impl_fn!(trait_data, TraitData, defs);
    impl_fn!(function, FunctionData, defs);
    impl_fn!(method, MethodData, defs);
    impl_fn!(macro_data, MacroData, defs);
    impl_fn!(mod_data, ModData, defs);
    impl_fn!(typedef, TypeDefData, defs);
    impl_fn!(variable, VariableData, defs);

    impl_fn!(function_ref, FunctionRefData, refs);
    impl_fn!(function_call, FunctionCallData, refs);
    impl_fn!(method_call, MethodCallData, refs);
    impl_fn!(mod_ref, ModRefData, refs);
    impl_fn!(type_ref, TypeRefData, refs);
    impl_fn!(variable_ref, VariableRefData, refs);

    impl_fn!(macro_use, MacroUseData, macro_refs);

    // FIXME store this instead of throwing it away.
    fn impl_data(&mut self, _data: ImplData) {}
    fn inheritance(&mut self, _data: InheritanceData) {}
}

// FIXME do we want to change ExternalData to this mode? It will break DXR.
// FIXME methods. The defs have information about possible overriding and the
// refs have decl information (e.g., a trait method where we know the required
// method, but not the supplied method). In both cases, we are currently
// ignoring it.

#[derive(Debug, RustcEncodable)]
struct Analysis {
    kind: Format,
    prelude: Option<CratePreludeData>,
    imports: Vec<Import>,
    defs: Vec<Def>,
    refs: Vec<Ref>,
    macro_refs: Vec<MacroRef>,
}

impl Analysis {
    fn new() -> Analysis {
        Analysis {
            kind: Format::Json,
            prelude: None,
            imports: vec![],
            defs: vec![],
            refs: vec![],
            macro_refs: vec![],
        }
    }
}

// DefId::index is a newtype and so the JSON serialisation is ugly. Therefore
// we use our own Id which is the same, but without the newtype.
#[derive(Debug, RustcEncodable)]
struct Id {
    krate: u32,
    index: u32,
}

impl From<DefId> for Id {
    fn from(id: DefId) -> Id {
        Id {
            krate: id.krate.as_u32(),
            index: id.index.as_u32(),
        }
    }
}

#[derive(Debug, RustcEncodable)]
struct Import {
    kind: ImportKind,
    ref_id: Option<Id>,
    span: SpanData,
    name: String,
    value: String,
}

#[derive(Debug, RustcEncodable)]
enum ImportKind {
    ExternCrate,
    Use,
    GlobUse,
}

impl From<ExternCrateData> for Import {
    fn from(data: ExternCrateData) -> Import {
        Import {
            kind: ImportKind::ExternCrate,
            ref_id: None,
            span: data.span,
            name: data.name,
            value: String::new(),
        }
    }
}
impl From<UseData> for Import {
    fn from(data: UseData) -> Import {
        Import {
            kind: ImportKind::Use,
            ref_id: data.mod_id.map(|id| From::from(id)),
            span: data.span,
            name: data.name,
            value: String::new(),
        }
    }
}
impl From<UseGlobData> for Import {
    fn from(data: UseGlobData) -> Import {
        Import {
            kind: ImportKind::GlobUse,
            ref_id: None,
            span: data.span,
            name: "*".to_owned(),
            value: data.names.join(", "),
        }
    }
}

#[derive(Debug, RustcEncodable)]
struct Def {
    kind: DefKind,
    id: Id,
    span: SpanData,
    name: String,
    qualname: String,
    value: String,
    children: Vec<Id>,
    decl_id: Option<Id>,
    docs: String,
}

#[derive(Debug, RustcEncodable)]
enum DefKind {
    // value = variant names
    Enum,
    // value = enum name + variant name + types
    Tuple,
    // value = [enum name +] name + fields
    Struct,
    // value = signature
    Trait,
    // value = type + generics
    Function,
    // value = type + generics
    Method,
    // No id, no value.
    Macro,
    // value = file_name
    Mod,
    // value = aliased type
    Type,
    // value = type and init expression (for all variable kinds).
    Local,
    Static,
    Const,
    Field,
}

impl From<EnumData> for Def {
    fn from(data: EnumData) -> Def {
        Def {
            kind: DefKind::Enum,
            id: From::from(data.id),
            span: data.span,
            name: data.name,
            qualname: data.qualname,
            value: data.value,
            children: data.variants.into_iter().map(|id| From::from(id)).collect(),
            decl_id: None,
            docs: data.docs,
        }
    }
}

impl From<TupleVariantData> for Def {
    fn from(data: TupleVariantData) -> Def {
        Def {
            kind: DefKind::Tuple,
            id: From::from(data.id),
            span: data.span,
            name: data.name,
            qualname: data.qualname,
            value: data.value,
            children: vec![],
            decl_id: None,
            docs: data.docs,
        }
    }
}
impl From<StructVariantData> for Def {
    fn from(data: StructVariantData) -> Def {
        Def {
            kind: DefKind::Struct,
            id: From::from(data.id),
            span: data.span,
            name: data.name,
            qualname: data.qualname,
            value: data.value,
            children: vec![],
            decl_id: None,
            docs: data.docs,
        }
    }
}
impl From<StructData> for Def {
    fn from(data: StructData) -> Def {
        Def {
            kind: DefKind::Struct,
            id: From::from(data.id),
            span: data.span,
            name: data.name,
            qualname: data.qualname,
            value: data.value,
            children: data.fields.into_iter().map(|id| From::from(id)).collect(),
            decl_id: None,
            docs: data.docs,
        }
    }
}
impl From<TraitData> for Def {
    fn from(data: TraitData) -> Def {
        Def {
            kind: DefKind::Trait,
            id: From::from(data.id),
            span: data.span,
            name: data.name,
            qualname: data.qualname,
            value: data.value,
            children: data.items.into_iter().map(|id| From::from(id)).collect(),
            decl_id: None,
            docs: data.docs,
        }
    }
}
impl From<FunctionData> for Def {
    fn from(data: FunctionData) -> Def {
        Def {
            kind: DefKind::Function,
            id: From::from(data.id),
            span: data.span,
            name: data.name,
            qualname: data.qualname,
            value: data.value,
            children: vec![],
            decl_id: None,
            docs: data.docs,
        }
    }
}
impl From<MethodData> for Def {
    fn from(data: MethodData) -> Def {
        Def {
            kind: DefKind::Method,
            id: From::from(data.id),
            span: data.span,
            name: data.name,
            qualname: data.qualname,
            value: data.value,
            children: vec![],
            decl_id: data.decl_id.map(|id| From::from(id)),
            docs: data.docs,
        }
    }
}
impl From<MacroData> for Def {
    fn from(data: MacroData) -> Def {
        Def {
            kind: DefKind::Macro,
            id: From::from(null_def_id()),
            span: data.span,
            name: data.name,
            qualname: data.qualname,
            value: String::new(),
            children: vec![],
            decl_id: None,
            docs: data.docs,
        }
    }
}
impl From<ModData> for Def {
    fn from(data:ModData) -> Def {
        Def {
            kind: DefKind::Mod,
            id: From::from(data.id),
            span: data.span,
            name: data.name,
            qualname: data.qualname,
            value: data.filename,
            children: data.items.into_iter().map(|id| From::from(id)).collect(),
            decl_id: None,
            docs: data.docs,
        }
    }
}
impl From<TypeDefData> for Def {
    fn from(data: TypeDefData) -> Def {
        Def {
            kind: DefKind::Type,
            id: From::from(data.id),
            span: data.span,
            name: data.name,
            qualname: data.qualname,
            value: data.value,
            children: vec![],
            decl_id: None,
            docs: String::new(),
        }
    }
}
impl From<VariableData> for Def {
    fn from(data: VariableData) -> Def {
        Def {
            kind: match data.kind {
                VariableKind::Static => DefKind::Static,
                VariableKind::Const => DefKind::Const,
                VariableKind::Local => DefKind::Local,
                VariableKind::Field => DefKind::Field,
            },
            id: From::from(data.id),
            span: data.span,
            name: data.name,
            qualname: data.qualname,
            value: data.type_value,
            children: vec![],
            decl_id: None,
            docs: data.docs,
        }
    }
}

#[derive(Debug, RustcEncodable)]
enum RefKind {
    Function,
    Mod,
    Type,
    Variable,
}

#[derive(Debug, RustcEncodable)]
struct Ref {
    kind: RefKind,
    span: SpanData,
    ref_id: Id,
}

impl From<FunctionRefData> for Ref {
    fn from(data: FunctionRefData) -> Ref {
        Ref {
            kind: RefKind::Function,
            span: data.span,
            ref_id: From::from(data.ref_id),
        }
    }
}
impl From<FunctionCallData> for Ref {
    fn from(data: FunctionCallData) -> Ref {
        Ref {
            kind: RefKind::Function,
            span: data.span,
            ref_id: From::from(data.ref_id),
        }
    }
}
impl From<MethodCallData> for Ref {
    fn from(data: MethodCallData) -> Ref {
        Ref {
            kind: RefKind::Function,
            span: data.span,
            ref_id: From::from(data.ref_id.or(data.decl_id).unwrap_or(null_def_id())),
        }
    }
}
impl From<ModRefData> for Ref {
    fn from(data: ModRefData) -> Ref {
        Ref {
            kind: RefKind::Mod,
            span: data.span,
            ref_id: From::from(data.ref_id.unwrap_or(null_def_id())),
        }
    }
}
impl From<TypeRefData> for Ref {
    fn from(data: TypeRefData) -> Ref {
        Ref {
            kind: RefKind::Type,
            span: data.span,
            ref_id: From::from(data.ref_id.unwrap_or(null_def_id())),
        }
    }
}
impl From<VariableRefData> for Ref {
    fn from(data: VariableRefData) -> Ref {
        Ref {
            kind: RefKind::Variable,
            span: data.span,
            ref_id: From::from(data.ref_id),
        }
    }
}

#[derive(Debug, RustcEncodable)]
struct MacroRef {
    span: SpanData,
    qualname: String,
    callee_span: SpanData,
}

impl From<MacroUseData> for MacroRef {
    fn from(data: MacroUseData) -> MacroRef {
        MacroRef {
            span: data.span,
            qualname: data.qualname,
            callee_span: data.callee_span,
        }
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::u32;
use std::fs::File;

use utility::{ErrorKind, ReadStream, many_metadata};

use metadata::{
  Metadata, Data,
  StreamInfo, CueSheet, VorbisComment, Picture,
  PictureType,
};

// Will return true when the unwrapped value of `option` and `other` match
// or `option` is `Option::None`, otherwise false.
#[inline]
pub fn optional_eq<T: Eq>(option: Option<T>, other: T) -> bool {
  option.map_or(true, |value| value == other)
}

// With the given filename, return all metadata blocks available.
//
// This function expects a flac file, but will return a proper `Result::Err`
// when things go wrong.
//
// # Failures
//
// * `ErrorKind::IO(io::ErrorKind::NotFound)` is returned when the given
//   filename isn't found.
// * `ErrorKind::IO(io::ErrorKind::InvalidData)` is returned when the data
//   within the file isn't valid FLAC data.
pub fn get_metadata(filename: &str) -> Result<Vec<Metadata>, ErrorKind> {
  File::open(filename).map_err(|e| ErrorKind::IO(e.kind()))
                      .and_then(|file| {
    let mut stream   = ReadStream::new(file);
    let mut metadata = Vec::new();

    let result = many_metadata(&mut stream, |block| metadata.push(block));

    if let Err(kind) = result {
      match kind {
        ErrorKind::HeaderParser |
        ErrorKind::Unknown      => Err(ErrorKind::IO(
                                     io::ErrorKind::InvalidData)),
        _                       => Err(kind),
      }
    } else {
      Ok(metadata)
    }
  })
}

/// Reads and returns the `StreamInfo` metadata block of the given FLAC
/// file.
///
/// # Failures
///
/// * `ErrorKind::NotFound` is returned when the there is no `StreamInfo`
///   within the file.
/// * `ErrorKind::IO(io::ErrorKind::NotFound)` is returned when the given
///   filename isn't found.
/// * `ErrorKind::IO(io::ErrorKind::InvalidData)` is returned when the data
///   within the file isn't valid FLAC data.
///
/// # Examples
///
/// Handling errors might look something like this:
///
/// ```
/// use flac::metadata;
///
/// match metadata::get_stream_info("path/to/file.flac") {
///   Ok(stream_info) => {
///     // Use the stream_info variable...
///   }
///   Err(error)      => println!("{:?}", error),
/// }
/// ```
///
/// Or just ignore the errors:
///
/// ```no_run
/// use flac::metadata;
///
/// let stream_info = metadata::get_stream_info("path/to/file.flac").unwrap();
/// ```
pub fn get_stream_info(filename: &str) -> Result<StreamInfo, ErrorKind> {
  get_metadata(filename).and_then(|blocks| {
    let mut result = Err(ErrorKind::NotFound);

    for block in blocks {
      if let Data::StreamInfo(stream_info) = block.data {
        result = Ok(stream_info);
        break;
      }
    }

    result
  })
}

/// Reads and returns the `VorbisComment` metadata block of the given FLAC
/// file.
///
/// # Failures
///
/// * `ErrorKind::NotFound` is returned when the there is no `VorbisComment`
///   within the file.
/// * `ErrorKind::IO(io::ErrorKind::NotFound)` is returned when the given
///   filename isn't found.
/// * `ErrorKind::IO(io::ErrorKind::InvalidData)` is returned when the data
///   within the file isn't valid FLAC data.
///
/// # Examples
///
/// Handling errors might look something like this:
///
/// ```
/// use flac::metadata;
///
/// match metadata::get_vorbis_comment("path/to/file.flac") {
///   Ok(vorbis_comment) => {
///     // Use the vorbis_comment variable...
///   }
///   Err(error)         => println!("{:?}", error),
/// }
/// ```
///
/// Or just ignore the errors:
///
/// ```no_run
/// use flac::metadata;
///
/// let vorbis_comment =
///   metadata::get_vorbis_comment("path/to/file.flac").unwrap();
/// ```
pub fn get_vorbis_comment(filename: &str)
                          -> Result<VorbisComment, ErrorKind> {
  get_metadata(filename).and_then(|blocks| {
    let mut result = Err(ErrorKind::NotFound);

    for block in blocks {
      if let Data::VorbisComment(vorbis_comment) = block.data {
        result = Ok(vorbis_comment);
        break;
      }
    }

    result
  })
}

/// Reads and returns the `CueSheet` metadata block of the given FLAC file.
///
/// # Failures
///
/// * `ErrorKind::NotFound` is returned when the there is no `CueSheet`
///   within the file.
/// * `ErrorKind::IO(io::ErrorKind::NotFound)` is returned when the given
///   filename isn't found.
/// * `ErrorKind::IO(io::ErrorKind::InvalidData)` is returned when the data
///   within the file isn't valid FLAC data.
///
/// # Examples
///
/// Handling errors might look something like this:
///
/// ```
/// use flac::metadata;
///
/// match metadata::get_cue_sheet("path/to/file.flac") {
///   Ok(cue_sheet) => {
///     // Use the cue_sheet variable...
///   }
///   Err(error)    => println!("{:?}", error),
/// }
/// ```
///
/// Or just ignore the errors
///
/// ```no_run
/// use flac::metadata;
///
/// let cue_sheet = metadata::get_cue_sheet("path/to/file.flac").unwrap();
/// ```
pub fn get_cue_sheet(filename: &str) -> Result<CueSheet, ErrorKind> {
  get_metadata(filename).and_then(|blocks| {
    let mut result = Err(ErrorKind::NotFound);

    for block in blocks {
      if let Data::CueSheet(cue_sheet) = block.data {
        result = Ok(cue_sheet);
        break;
      }
    }

    result
  })
}

/// Reads and returns a `Picture` metadata block of the given FLAC file.
///
/// There can be more than one `Picture` block in a file and this function
/// takes optional, that being `Option<T>`, parameters that act as
/// constraints to search within. The `Picture` with the largest area
/// matching all constraints will be returned.
///
/// Putting `None` into any of the optional constraints conveys that you
/// want any of that parameter. Otherwise it will try to look for the image
/// that matches within the given constraints.
///
/// # Failures
///
/// * `ErrorKind::NotFound` is returned when the there is no `Picture`
///   within the file, or no `Picture` that fits the given constraints.
/// * `ErrorKind::IO(io::ErrorKind::NotFound)` is returned when the given
///   filename isn't found.
/// * `ErrorKind::IO(io::ErrorKind::InvalidData)` is returned when the data
///   within the file isn't valid FLAC data.
///
/// # Examples
///
/// Handling errors might look something like this:
///
/// ```
/// use flac::metadata;
/// use flac::metadata::PictureType;
///
/// let result = metadata::get_picture("path/to/file.flac",
///                                    Some(PictureType::FileIcon),
///                                    Some("image/gif"),
///                                    None, None, None,
///                                    None, None);
///
/// match result {
///   Ok(picture) => {
///     // Use the picture variable...
///   }
///   Err(error)  => println!("{:?}", error),
/// }
/// ```
///
/// Or just ignore the errors:
///
/// ```no_run
/// use flac::metadata;
/// use flac::metadata::PictureType;
///
/// let picture = metadata::get_picture("path/to/file.flac",
///                                     Some(PictureType::FileIcon),
///                                     Some("image/gif"),
///                                     None, None, None,
///                                     None, None).unwrap();
/// ```
pub fn get_picture(filename: &str,
                   picture_type: Option<PictureType>,
                   mime_type: Option<&str>,
                   description: Option<&str>,
                   max_width: Option<u32>,
                   max_height: Option<u32>,
                   max_depth: Option<u32>,
                   max_colors: Option<u32>)
                   -> Result<Picture, ErrorKind> {
  get_metadata(filename).and_then(|blocks| {
    let mut result = Err(ErrorKind::NotFound);

    let mut max_area_seen  = 0;
    let mut max_depth_seen = 0;

    let max_value      = u32::max_value();
    let max_width_num  = max_width.unwrap_or(max_value);
    let max_height_num = max_height.unwrap_or(max_value);
    let max_depth_num  = max_depth.unwrap_or(max_value);
    let max_colors_num = max_colors.unwrap_or(max_value);

    for block in blocks {
      if let Data::Picture(picture) = block.data {
        let area = (picture.width as u64) * (picture.height as u64);

        if optional_eq(picture_type, picture.picture_type) &&
           optional_eq(mime_type, &picture.mime_type) &&
           optional_eq(description, &picture.description) &&
           picture.width <= max_width_num &&
           picture.height <= max_height_num &&
           picture.depth <= max_depth_num &&
           picture.colors <= max_colors_num &&
           (area > max_area_seen || (area == max_area_seen &&
                                     picture.depth > max_depth_seen)) {
          max_area_seen  = area;
          max_depth_seen = picture.depth;
          result         = Ok(picture);
        }
      }
    }

    result
  })
}

#[cfg(test)]
mod tests {
  use super::*;
  use std::io;

  use utility::ErrorKind;

  #[test]
  #[should_panic]
  fn test_panic_optional_eq() {
    assert!(optional_eq(Some(1), 0));
  }

  #[test]
  fn test_optional_eq() {
    assert!(optional_eq(None, 0), "Should always return true when None");
    assert!(optional_eq(Some(0), 0), "Should return true (Some(0) == 0)");
  }

  #[test]
  fn test_get_metadata() {
    let not_found    = get_metadata("non-existent/file.txt");
    let invalid_data = get_metadata("README.md");
    let result       = get_metadata("tests/assets/input-SVAUP.flac");

    assert_eq!(not_found.unwrap_err(), ErrorKind::IO(
                                         io::ErrorKind::NotFound));
    assert_eq!(invalid_data.unwrap_err(), ErrorKind::IO(
                                            io::ErrorKind::InvalidData));
    assert!(result.is_ok());
  }
}
<commit_msg>Add `ErrorKind`s for parser failures<commit_after>use std::io;
use std::u32;
use std::fs::File;

use utility::{ErrorKind, ReadStream, many_metadata};

use metadata::{
  Metadata, Data,
  StreamInfo, CueSheet, VorbisComment, Picture,
  PictureType,
};

// Will return true when the unwrapped value of `option` and `other` match
// or `option` is `Option::None`, otherwise false.
#[inline]
pub fn optional_eq<T: Eq>(option: Option<T>, other: T) -> bool {
  option.map_or(true, |value| value == other)
}

// With the given filename, return all metadata blocks available.
//
// This function expects a flac file, but will return a proper `Result::Err`
// when things go wrong.
//
// # Failures
//
// * `ErrorKind::IO(io::ErrorKind::NotFound)` is returned when the given
//   filename isn't found.
// * `ErrorKind::IO(io::ErrorKind::InvalidData)` is returned when the data
//   within the file isn't valid FLAC data.
// * Several different parser specific errors that are structured as
//   `ErrorKind::<parser_name>Parser`.
pub fn get_metadata(filename: &str) -> Result<Vec<Metadata>, ErrorKind> {
  File::open(filename).map_err(|e| ErrorKind::IO(e.kind()))
                      .and_then(|file| {
    let mut stream   = ReadStream::new(file);
    let mut metadata = Vec::new();

    let result = many_metadata(&mut stream, |block| metadata.push(block));

    if let Err(kind) = result {
      match kind {
        ErrorKind::HeaderParser |
        ErrorKind::Unknown      => Err(ErrorKind::IO(
                                     io::ErrorKind::InvalidData)),
        _                       => Err(kind),
      }
    } else {
      Ok(metadata)
    }
  })
}

/// Reads and returns the `StreamInfo` metadata block of the given FLAC
/// file.
///
/// # Failures
///
/// * `ErrorKind::NotFound` is returned when the there is no `StreamInfo`
///   within the file.
/// * `ErrorKind::IO(io::ErrorKind::NotFound)` is returned when the given
///   filename isn't found.
/// * `ErrorKind::IO(io::ErrorKind::InvalidData)` is returned when the data
///   within the file isn't valid FLAC data.
/// * Several different parser specific errors that are structured as
///   `ErrorKind::<parser_name>Parser`.
///
/// # Examples
///
/// Handling errors might look something like this:
///
/// ```
/// use flac::metadata;
///
/// match metadata::get_stream_info("path/to/file.flac") {
///   Ok(stream_info) => {
///     // Use the stream_info variable...
///   }
///   Err(error)      => println!("{:?}", error),
/// }
/// ```
///
/// Or just ignore the errors:
///
/// ```no_run
/// use flac::metadata;
///
/// let stream_info = metadata::get_stream_info("path/to/file.flac").unwrap();
/// ```
pub fn get_stream_info(filename: &str) -> Result<StreamInfo, ErrorKind> {
  get_metadata(filename).and_then(|blocks| {
    let mut result = Err(ErrorKind::NotFound);

    for block in blocks {
      if let Data::StreamInfo(stream_info) = block.data {
        result = Ok(stream_info);
        break;
      }
    }

    result
  })
}

/// Reads and returns the `VorbisComment` metadata block of the given FLAC
/// file.
///
/// # Failures
///
/// * `ErrorKind::NotFound` is returned when the there is no `VorbisComment`
///   within the file.
/// * `ErrorKind::IO(io::ErrorKind::NotFound)` is returned when the given
///   filename isn't found.
/// * `ErrorKind::IO(io::ErrorKind::InvalidData)` is returned when the data
///   within the file isn't valid FLAC data.
/// * Several different parser specific errors that are structured as
///   `ErrorKind::<parser_name>Parser`.
///
/// # Examples
///
/// Handling errors might look something like this:
///
/// ```
/// use flac::metadata;
///
/// match metadata::get_vorbis_comment("path/to/file.flac") {
///   Ok(vorbis_comment) => {
///     // Use the vorbis_comment variable...
///   }
///   Err(error)         => println!("{:?}", error),
/// }
/// ```
///
/// Or just ignore the errors:
///
/// ```no_run
/// use flac::metadata;
///
/// let vorbis_comment =
///   metadata::get_vorbis_comment("path/to/file.flac").unwrap();
/// ```
pub fn get_vorbis_comment(filename: &str)
                          -> Result<VorbisComment, ErrorKind> {
  get_metadata(filename).and_then(|blocks| {
    let mut result = Err(ErrorKind::NotFound);

    for block in blocks {
      if let Data::VorbisComment(vorbis_comment) = block.data {
        result = Ok(vorbis_comment);
        break;
      }
    }

    result
  })
}

/// Reads and returns the `CueSheet` metadata block of the given FLAC file.
///
/// # Failures
///
/// * `ErrorKind::NotFound` is returned when the there is no `CueSheet`
///   within the file.
/// * `ErrorKind::IO(io::ErrorKind::NotFound)` is returned when the given
///   filename isn't found.
/// * `ErrorKind::IO(io::ErrorKind::InvalidData)` is returned when the data
///   within the file isn't valid FLAC data.
/// * Several different parser specific errors that are structured as
///   `ErrorKind::<parser_name>Parser`.
///
/// # Examples
///
/// Handling errors might look something like this:
///
/// ```
/// use flac::metadata;
///
/// match metadata::get_cue_sheet("path/to/file.flac") {
///   Ok(cue_sheet) => {
///     // Use the cue_sheet variable...
///   }
///   Err(error)    => println!("{:?}", error),
/// }
/// ```
///
/// Or just ignore the errors
///
/// ```no_run
/// use flac::metadata;
///
/// let cue_sheet = metadata::get_cue_sheet("path/to/file.flac").unwrap();
/// ```
pub fn get_cue_sheet(filename: &str) -> Result<CueSheet, ErrorKind> {
  get_metadata(filename).and_then(|blocks| {
    let mut result = Err(ErrorKind::NotFound);

    for block in blocks {
      if let Data::CueSheet(cue_sheet) = block.data {
        result = Ok(cue_sheet);
        break;
      }
    }

    result
  })
}

/// Reads and returns a `Picture` metadata block of the given FLAC file.
///
/// There can be more than one `Picture` block in a file and this function
/// takes optional, that being `Option<T>`, parameters that act as
/// constraints to search within. The `Picture` with the largest area
/// matching all constraints will be returned.
///
/// Putting `None` into any of the optional constraints conveys that you
/// want any of that parameter. Otherwise it will try to look for the image
/// that matches within the given constraints.
///
/// # Failures
///
/// * `ErrorKind::NotFound` is returned when the there is no `Picture`
///   within the file, or no `Picture` that fits the given constraints.
/// * `ErrorKind::IO(io::ErrorKind::NotFound)` is returned when the given
///   filename isn't found.
/// * `ErrorKind::IO(io::ErrorKind::InvalidData)` is returned when the data
///   within the file isn't valid FLAC data.
/// * Several different parser specific errors that are structured as
///   `ErrorKind::<parser_name>Parser`.
///
/// # Examples
///
/// Handling errors might look something like this:
///
/// ```
/// use flac::metadata;
/// use flac::metadata::PictureType;
///
/// let result = metadata::get_picture("path/to/file.flac",
///                                    Some(PictureType::FileIcon),
///                                    Some("image/gif"),
///                                    None, None, None,
///                                    None, None);
///
/// match result {
///   Ok(picture) => {
///     // Use the picture variable...
///   }
///   Err(error)  => println!("{:?}", error),
/// }
/// ```
///
/// Or just ignore the errors:
///
/// ```no_run
/// use flac::metadata;
/// use flac::metadata::PictureType;
///
/// let picture = metadata::get_picture("path/to/file.flac",
///                                     Some(PictureType::FileIcon),
///                                     Some("image/gif"),
///                                     None, None, None,
///                                     None, None).unwrap();
/// ```
pub fn get_picture(filename: &str,
                   picture_type: Option<PictureType>,
                   mime_type: Option<&str>,
                   description: Option<&str>,
                   max_width: Option<u32>,
                   max_height: Option<u32>,
                   max_depth: Option<u32>,
                   max_colors: Option<u32>)
                   -> Result<Picture, ErrorKind> {
  get_metadata(filename).and_then(|blocks| {
    let mut result = Err(ErrorKind::NotFound);

    let mut max_area_seen  = 0;
    let mut max_depth_seen = 0;

    let max_value      = u32::max_value();
    let max_width_num  = max_width.unwrap_or(max_value);
    let max_height_num = max_height.unwrap_or(max_value);
    let max_depth_num  = max_depth.unwrap_or(max_value);
    let max_colors_num = max_colors.unwrap_or(max_value);

    for block in blocks {
      if let Data::Picture(picture) = block.data {
        let area = (picture.width as u64) * (picture.height as u64);

        if optional_eq(picture_type, picture.picture_type) &&
           optional_eq(mime_type, &picture.mime_type) &&
           optional_eq(description, &picture.description) &&
           picture.width <= max_width_num &&
           picture.height <= max_height_num &&
           picture.depth <= max_depth_num &&
           picture.colors <= max_colors_num &&
           (area > max_area_seen || (area == max_area_seen &&
                                     picture.depth > max_depth_seen)) {
          max_area_seen  = area;
          max_depth_seen = picture.depth;
          result         = Ok(picture);
        }
      }
    }

    result
  })
}

#[cfg(test)]
mod tests {
  use super::*;
  use std::io;

  use utility::ErrorKind;

  #[test]
  #[should_panic]
  fn test_panic_optional_eq() {
    assert!(optional_eq(Some(1), 0));
  }

  #[test]
  fn test_optional_eq() {
    assert!(optional_eq(None, 0), "Should always return true when None");
    assert!(optional_eq(Some(0), 0), "Should return true (Some(0) == 0)");
  }

  #[test]
  fn test_get_metadata() {
    let not_found    = get_metadata("non-existent/file.txt");
    let invalid_data = get_metadata("README.md");
    let result       = get_metadata("tests/assets/input-SVAUP.flac");

    assert_eq!(not_found.unwrap_err(), ErrorKind::IO(
                                         io::ErrorKind::NotFound));
    assert_eq!(invalid_data.unwrap_err(), ErrorKind::IO(
                                            io::ErrorKind::InvalidData));
    assert!(result.is_ok());
  }
}
<|endoftext|>


<commit_before>use std::io;
use std::mem::size_of_val;
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use std::sync::Once;

use winapi::ctypes::c_int;
use winapi::shared::ws2def::SOCKADDR;
use winapi::um::winsock2::{
    ioctlsocket, socket, FIONBIO, INVALID_SOCKET, PF_INET, PF_INET6, SOCKET,
};

/// Initialise the network stack for Windows.
pub(crate) fn init() {
    static INIT: Once = Once::new();
    INIT.call_once(|| {
        // Let standard library call `WSAStartup` for us, we can't do it
        // ourselves because otherwise using any type in `std::net` would panic
        // when it tries to call `WSAStartup` a second time.
        drop(std::net::UdpSocket::bind("127.0.0.1:0"));
    });
}

/// Create a new non-blocking socket.
pub(crate) fn new_socket(addr: SocketAddr, socket_type: c_int) -> io::Result<SOCKET> {
    let domain = match addr {
        SocketAddr::V4(..) => PF_INET,
        SocketAddr::V6(..) => PF_INET6,
    };

    syscall!(
        socket(domain, socket_type, 0),
        PartialEq::eq,
        INVALID_SOCKET
    )
    .and_then(|socket| {
        syscall!(ioctlsocket(socket, FIONBIO, &mut 1), PartialEq::ne, 0).map(|_| socket as SOCKET)
    })
}

pub(crate) fn socket_addr(addr: &SocketAddr) -> (*const SOCKADDR, c_int) {
    match addr {
        SocketAddr::V4(ref addr) => (
            addr as *const _ as *const SOCKADDR,
            size_of_val(addr) as c_int,
        ),
        SocketAddr::V6(ref addr) => (
            addr as *const _ as *const SOCKADDR,
            size_of_val(addr) as c_int,
        ),
    }
}

#[cfg(all(feature = "os-poll", feature = "tcp"))]
pub(crate) fn inaddr_any(other: SocketAddr) -> SocketAddr {
    match other {
        SocketAddr::V4(..) => {
            let any = Ipv4Addr::new(0, 0, 0, 0);
            let addr = SocketAddrV4::new(any, 0);
            SocketAddr::V4(addr)
        }
        SocketAddr::V6(..) => {
            let any = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
            let addr = SocketAddrV6::new(any, 0, 0, 0);
            SocketAddr::V6(addr)
        }
    }
}
<commit_msg>Fix unused imports<commit_after>use std::io;
use std::mem::size_of_val;
use std::net::SocketAddr;
#[cfg(all(feature = "os-poll", feature = "tcp"))]
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
use std::sync::Once;

use winapi::ctypes::c_int;
use winapi::shared::ws2def::SOCKADDR;
use winapi::um::winsock2::{
    ioctlsocket, socket, FIONBIO, INVALID_SOCKET, PF_INET, PF_INET6, SOCKET,
};

/// Initialise the network stack for Windows.
pub(crate) fn init() {
    static INIT: Once = Once::new();
    INIT.call_once(|| {
        // Let standard library call `WSAStartup` for us, we can't do it
        // ourselves because otherwise using any type in `std::net` would panic
        // when it tries to call `WSAStartup` a second time.
        drop(std::net::UdpSocket::bind("127.0.0.1:0"));
    });
}

/// Create a new non-blocking socket.
pub(crate) fn new_socket(addr: SocketAddr, socket_type: c_int) -> io::Result<SOCKET> {
    let domain = match addr {
        SocketAddr::V4(..) => PF_INET,
        SocketAddr::V6(..) => PF_INET6,
    };

    syscall!(
        socket(domain, socket_type, 0),
        PartialEq::eq,
        INVALID_SOCKET
    )
    .and_then(|socket| {
        syscall!(ioctlsocket(socket, FIONBIO, &mut 1), PartialEq::ne, 0).map(|_| socket as SOCKET)
    })
}

pub(crate) fn socket_addr(addr: &SocketAddr) -> (*const SOCKADDR, c_int) {
    match addr {
        SocketAddr::V4(ref addr) => (
            addr as *const _ as *const SOCKADDR,
            size_of_val(addr) as c_int,
        ),
        SocketAddr::V6(ref addr) => (
            addr as *const _ as *const SOCKADDR,
            size_of_val(addr) as c_int,
        ),
    }
}

#[cfg(all(feature = "os-poll", feature = "tcp"))]
pub(crate) fn inaddr_any(other: SocketAddr) -> SocketAddr {
    match other {
        SocketAddr::V4(..) => {
            let any = Ipv4Addr::new(0, 0, 0, 0);
            let addr = SocketAddrV4::new(any, 0);
            SocketAddr::V4(addr)
        }
        SocketAddr::V6(..) => {
            let any = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
            let addr = SocketAddrV6::new(any, 0, 0, 0);
            SocketAddr::V6(addr)
        }
    }
}
<|endoftext|>


<commit_before>//! Tidy check to enforce rules about platform-specific code in std.
//!
//! This is intended to maintain existing standards of code
//! organization in hopes that the standard library will continue to
//! be refactored to isolate platform-specific bits, making porting
//! easier; where "standard library" roughly means "all the
//! dependencies of the std and test crates".
//!
//! This generally means placing restrictions on where `cfg(unix)`,
//! `cfg(windows)`, `cfg(target_os)` and `cfg(target_env)` may appear,
//! the basic objective being to isolate platform-specific code to the
//! platform-specific `std::sys` modules, and to the allocation,
//! unwinding, and libc crates.
//!
//! Following are the basic rules, though there are currently
//! exceptions:
//!
//! - core may not have platform-specific code.
//! - libpanic_abort may have platform-specific code.
//! - libpanic_unwind may have platform-specific code.
//! - libunwind may have platform-specific code.
//! - other crates in the std facade may not.
//! - std may have platform-specific code in the following places:
//!   - `sys/unix/`
//!   - `sys/windows/`
//!   - `os/`
//!
//! `std/sys_common` should _not_ contain platform-specific code.
//! Finally, because std contains tests with platform-specific
//! `ignore` attributes, once the parser encounters `mod tests`,
//! platform-specific cfgs are allowed. Not sure yet how to deal with
//! this in the long term.

use std::iter::Iterator;
use std::path::Path;

// Paths that may contain platform-specific code.
const EXCEPTION_PATHS: &[&str] = &[
    // std crates
    "src/libpanic_abort",
    "src/libpanic_unwind",
    "src/libunwind",
    // black_box implementation is LLVM-version specific and it uses
    // target_os to tell targets with different LLVM-versions appart
    // (e.g. `wasm32-unknown-emscripten` vs `wasm32-unknown-unknown`):
    "src/libcore/hint.rs",
    "src/libstd/sys/", // Platform-specific code for std lives here.
    // This has the trailing slash so that sys_common is not excepted.
    "src/libstd/os", // Platform-specific public interfaces
    "src/rtstartup", // Not sure what to do about this. magic stuff for mingw
    // temporary exceptions
    "src/libstd/lib.rs",
    "src/libstd/path.rs",
    "src/libstd/f32.rs",
    "src/libstd/f64.rs",
    // Integration test for platform-specific run-time feature detection:
    "src/libstd/tests/run-time-detect.rs",
    "src/libstd/net/test.rs",
    "src/libstd/sys_common/mod.rs",
    "src/libstd/sys_common/net.rs",
    "src/libstd/sys_common/backtrace.rs",
    "src/libterm", // Not sure how to make this crate portable, but test crate needs it.
    "src/libtest", // Probably should defer to unstable `std::sys` APIs.
    "src/libstd/sync/mpsc", // some tests are only run on non-emscripten
    // std testing crates, okay for now at least
    "src/libcore/tests",
    "src/liballoc/tests/lib.rs",
    // The `VaList` implementation must have platform specific code.
    // The Windows implementation of a `va_list` is always a character
    // pointer regardless of the target architecture. As a result,
    // we must use `#[cfg(windows)]` to conditionally compile the
    // correct `VaList` structure for windows.
    "src/libcore/ffi.rs",
    // non-std crates
    "src/test",
    "src/tools",
    "src/librustc",
    "src/librustdoc",
    "src/librustc_ast",
    "src/bootstrap",
];

pub fn check(path: &Path, bad: &mut bool) {
    // Sanity check that the complex parsing here works.
    let mut saw_target_arch = false;
    let mut saw_cfg_bang = false;
    super::walk(path, &mut super::filter_dirs, &mut |entry, contents| {
        let file = entry.path();
        let filestr = file.to_string_lossy().replace("\\", "/");
        if !filestr.ends_with(".rs") {
            return;
        }

        let is_exception_path = EXCEPTION_PATHS.iter().any(|s| filestr.contains(&**s));
        if is_exception_path {
            return;
        }

        check_cfgs(contents, &file, bad, &mut saw_target_arch, &mut saw_cfg_bang);
    });

    assert!(saw_target_arch);
    assert!(saw_cfg_bang);
}

fn check_cfgs(
    contents: &str,
    file: &Path,
    bad: &mut bool,
    saw_target_arch: &mut bool,
    saw_cfg_bang: &mut bool,
) {
    // For now it's ok to have platform-specific code after 'mod tests'.
    let mod_tests_idx = find_test_mod(contents);
    let contents = &contents[..mod_tests_idx];
    // Pull out all `cfg(...)` and `cfg!(...)` strings.
    let cfgs = parse_cfgs(contents);

    let mut line_numbers: Option<Vec<usize>> = None;
    let mut err = |idx: usize, cfg: &str| {
        if line_numbers.is_none() {
            line_numbers = Some(contents.match_indices('\n').map(|(i, _)| i).collect());
        }
        let line_numbers = line_numbers.as_ref().expect("");
        let line = match line_numbers.binary_search(&idx) {
            Ok(_) => unreachable!(),
            Err(i) => i + 1,
        };
        tidy_error!(bad, "{}:{}: platform-specific cfg: {}", file.display(), line, cfg);
    };

    for (idx, cfg) in cfgs {
        // Sanity check that the parsing here works.
        if !*saw_target_arch && cfg.contains("target_arch") {
            *saw_target_arch = true
        }
        if !*saw_cfg_bang && cfg.contains("cfg!") {
            *saw_cfg_bang = true
        }

        let contains_platform_specific_cfg = cfg.contains("target_os")
            || cfg.contains("target_env")
            || cfg.contains("target_vendor")
            || cfg.contains("unix")
            || cfg.contains("windows");

        if !contains_platform_specific_cfg {
            continue;
        }

        let preceeded_by_doc_comment = {
            let pre_contents = &contents[..idx];
            let pre_newline = pre_contents.rfind('\n');
            let pre_doc_comment = pre_contents.rfind("///");
            match (pre_newline, pre_doc_comment) {
                (Some(n), Some(c)) => n < c,
                (None, Some(_)) => true,
                (_, None) => false,
            }
        };

        if preceeded_by_doc_comment {
            continue;
        }

        err(idx, cfg);
    }
}

fn find_test_mod(contents: &str) -> usize {
    if let Some(mod_tests_idx) = contents.find("mod tests") {
        // Also capture a previous line indicating that "mod tests" is cfg'd out.
        let prev_newline_idx = contents[..mod_tests_idx].rfind('\n').unwrap_or(mod_tests_idx);
        let prev_newline_idx = contents[..prev_newline_idx].rfind('\n');
        if let Some(nl) = prev_newline_idx {
            let prev_line = &contents[nl + 1..mod_tests_idx];
            if prev_line.contains("cfg(all(test, not(target_os")
                || prev_line.contains("cfg(all(test, not(any(target_os")
            {
                nl
            } else {
                mod_tests_idx
            }
        } else {
            mod_tests_idx
        }
    } else {
        contents.len()
    }
}

fn parse_cfgs<'a>(contents: &'a str) -> Vec<(usize, &'a str)> {
    let candidate_cfgs = contents.match_indices("cfg");
    let candidate_cfg_idxs = candidate_cfgs.map(|(i, _)| i);
    // This is puling out the indexes of all "cfg" strings
    // that appear to be tokens followed by a parenthesis.
    let cfgs = candidate_cfg_idxs.filter(|i| {
        let pre_idx = i.saturating_sub(*i);
        let succeeds_non_ident = !contents
            .as_bytes()
            .get(pre_idx)
            .cloned()
            .map(char::from)
            .map(char::is_alphanumeric)
            .unwrap_or(false);
        let contents_after = &contents[*i..];
        let first_paren = contents_after.find('(');
        let paren_idx = first_paren.map(|ip| i + ip);
        let preceeds_whitespace_and_paren = paren_idx
            .map(|ip| {
                let maybe_space = &contents[*i + "cfg".len()..ip];
                maybe_space.chars().all(|c| char::is_whitespace(c) || c == '!')
            })
            .unwrap_or(false);

        succeeds_non_ident && preceeds_whitespace_and_paren
    });

    cfgs.flat_map(|i| {
        let mut depth = 0;
        let contents_from = &contents[i..];
        for (j, byte) in contents_from.bytes().enumerate() {
            match byte {
                b'(' => {
                    depth += 1;
                }
                b')' => {
                    depth -= 1;
                    if depth == 0 {
                        return Some((i, &contents_from[..=j]));
                    }
                }
                _ => {}
            }
        }

        // if the parentheses are unbalanced just ignore this cfg -- it'll be caught when attempting
        // to run the compiler, and there's no real reason to lint it separately here
        None
    })
    .collect()
}
<commit_msg>Ignore PAL lint for std::panicking<commit_after>//! Tidy check to enforce rules about platform-specific code in std.
//!
//! This is intended to maintain existing standards of code
//! organization in hopes that the standard library will continue to
//! be refactored to isolate platform-specific bits, making porting
//! easier; where "standard library" roughly means "all the
//! dependencies of the std and test crates".
//!
//! This generally means placing restrictions on where `cfg(unix)`,
//! `cfg(windows)`, `cfg(target_os)` and `cfg(target_env)` may appear,
//! the basic objective being to isolate platform-specific code to the
//! platform-specific `std::sys` modules, and to the allocation,
//! unwinding, and libc crates.
//!
//! Following are the basic rules, though there are currently
//! exceptions:
//!
//! - core may not have platform-specific code.
//! - libpanic_abort may have platform-specific code.
//! - libpanic_unwind may have platform-specific code.
//! - libunwind may have platform-specific code.
//! - other crates in the std facade may not.
//! - std may have platform-specific code in the following places:
//!   - `sys/unix/`
//!   - `sys/windows/`
//!   - `os/`
//!
//! `std/sys_common` should _not_ contain platform-specific code.
//! Finally, because std contains tests with platform-specific
//! `ignore` attributes, once the parser encounters `mod tests`,
//! platform-specific cfgs are allowed. Not sure yet how to deal with
//! this in the long term.

use std::iter::Iterator;
use std::path::Path;

// Paths that may contain platform-specific code.
const EXCEPTION_PATHS: &[&str] = &[
    // std crates
    "src/libpanic_abort",
    "src/libpanic_unwind",
    "src/libunwind",
    // black_box implementation is LLVM-version specific and it uses
    // target_os to tell targets with different LLVM-versions appart
    // (e.g. `wasm32-unknown-emscripten` vs `wasm32-unknown-unknown`):
    "src/libcore/hint.rs",
    "src/libstd/sys/", // Platform-specific code for std lives here.
    // This has the trailing slash so that sys_common is not excepted.
    "src/libstd/os", // Platform-specific public interfaces
    "src/rtstartup", // Not sure what to do about this. magic stuff for mingw
    // temporary exceptions
    "src/libstd/lib.rs",
    "src/libstd/path.rs",
    "src/libstd/f32.rs",
    "src/libstd/f64.rs",
    // Integration test for platform-specific run-time feature detection:
    "src/libstd/tests/run-time-detect.rs",
    "src/libstd/net/test.rs",
    "src/libstd/sys_common/mod.rs",
    "src/libstd/sys_common/net.rs",
    "src/libstd/sys_common/backtrace.rs",
    // panic_unwind shims
    "src/libstd/panicking.rs",
    "src/libterm", // Not sure how to make this crate portable, but test crate needs it.
    "src/libtest", // Probably should defer to unstable `std::sys` APIs.
    "src/libstd/sync/mpsc", // some tests are only run on non-emscripten
    // std testing crates, okay for now at least
    "src/libcore/tests",
    "src/liballoc/tests/lib.rs",
    // The `VaList` implementation must have platform specific code.
    // The Windows implementation of a `va_list` is always a character
    // pointer regardless of the target architecture. As a result,
    // we must use `#[cfg(windows)]` to conditionally compile the
    // correct `VaList` structure for windows.
    "src/libcore/ffi.rs",
    // non-std crates
    "src/test",
    "src/tools",
    "src/librustc",
    "src/librustdoc",
    "src/librustc_ast",
    "src/bootstrap",
];

pub fn check(path: &Path, bad: &mut bool) {
    // Sanity check that the complex parsing here works.
    let mut saw_target_arch = false;
    let mut saw_cfg_bang = false;
    super::walk(path, &mut super::filter_dirs, &mut |entry, contents| {
        let file = entry.path();
        let filestr = file.to_string_lossy().replace("\\", "/");
        if !filestr.ends_with(".rs") {
            return;
        }

        let is_exception_path = EXCEPTION_PATHS.iter().any(|s| filestr.contains(&**s));
        if is_exception_path {
            return;
        }

        check_cfgs(contents, &file, bad, &mut saw_target_arch, &mut saw_cfg_bang);
    });

    assert!(saw_target_arch);
    assert!(saw_cfg_bang);
}

fn check_cfgs(
    contents: &str,
    file: &Path,
    bad: &mut bool,
    saw_target_arch: &mut bool,
    saw_cfg_bang: &mut bool,
) {
    // For now it's ok to have platform-specific code after 'mod tests'.
    let mod_tests_idx = find_test_mod(contents);
    let contents = &contents[..mod_tests_idx];
    // Pull out all `cfg(...)` and `cfg!(...)` strings.
    let cfgs = parse_cfgs(contents);

    let mut line_numbers: Option<Vec<usize>> = None;
    let mut err = |idx: usize, cfg: &str| {
        if line_numbers.is_none() {
            line_numbers = Some(contents.match_indices('\n').map(|(i, _)| i).collect());
        }
        let line_numbers = line_numbers.as_ref().expect("");
        let line = match line_numbers.binary_search(&idx) {
            Ok(_) => unreachable!(),
            Err(i) => i + 1,
        };
        tidy_error!(bad, "{}:{}: platform-specific cfg: {}", file.display(), line, cfg);
    };

    for (idx, cfg) in cfgs {
        // Sanity check that the parsing here works.
        if !*saw_target_arch && cfg.contains("target_arch") {
            *saw_target_arch = true
        }
        if !*saw_cfg_bang && cfg.contains("cfg!") {
            *saw_cfg_bang = true
        }

        let contains_platform_specific_cfg = cfg.contains("target_os")
            || cfg.contains("target_env")
            || cfg.contains("target_vendor")
            || cfg.contains("unix")
            || cfg.contains("windows");

        if !contains_platform_specific_cfg {
            continue;
        }

        let preceeded_by_doc_comment = {
            let pre_contents = &contents[..idx];
            let pre_newline = pre_contents.rfind('\n');
            let pre_doc_comment = pre_contents.rfind("///");
            match (pre_newline, pre_doc_comment) {
                (Some(n), Some(c)) => n < c,
                (None, Some(_)) => true,
                (_, None) => false,
            }
        };

        if preceeded_by_doc_comment {
            continue;
        }

        err(idx, cfg);
    }
}

fn find_test_mod(contents: &str) -> usize {
    if let Some(mod_tests_idx) = contents.find("mod tests") {
        // Also capture a previous line indicating that "mod tests" is cfg'd out.
        let prev_newline_idx = contents[..mod_tests_idx].rfind('\n').unwrap_or(mod_tests_idx);
        let prev_newline_idx = contents[..prev_newline_idx].rfind('\n');
        if let Some(nl) = prev_newline_idx {
            let prev_line = &contents[nl + 1..mod_tests_idx];
            if prev_line.contains("cfg(all(test, not(target_os")
                || prev_line.contains("cfg(all(test, not(any(target_os")
            {
                nl
            } else {
                mod_tests_idx
            }
        } else {
            mod_tests_idx
        }
    } else {
        contents.len()
    }
}

fn parse_cfgs<'a>(contents: &'a str) -> Vec<(usize, &'a str)> {
    let candidate_cfgs = contents.match_indices("cfg");
    let candidate_cfg_idxs = candidate_cfgs.map(|(i, _)| i);
    // This is puling out the indexes of all "cfg" strings
    // that appear to be tokens followed by a parenthesis.
    let cfgs = candidate_cfg_idxs.filter(|i| {
        let pre_idx = i.saturating_sub(*i);
        let succeeds_non_ident = !contents
            .as_bytes()
            .get(pre_idx)
            .cloned()
            .map(char::from)
            .map(char::is_alphanumeric)
            .unwrap_or(false);
        let contents_after = &contents[*i..];
        let first_paren = contents_after.find('(');
        let paren_idx = first_paren.map(|ip| i + ip);
        let preceeds_whitespace_and_paren = paren_idx
            .map(|ip| {
                let maybe_space = &contents[*i + "cfg".len()..ip];
                maybe_space.chars().all(|c| char::is_whitespace(c) || c == '!')
            })
            .unwrap_or(false);

        succeeds_non_ident && preceeds_whitespace_and_paren
    });

    cfgs.flat_map(|i| {
        let mut depth = 0;
        let contents_from = &contents[i..];
        for (j, byte) in contents_from.bytes().enumerate() {
            match byte {
                b'(' => {
                    depth += 1;
                }
                b')' => {
                    depth -= 1;
                    if depth == 0 {
                        return Some((i, &contents_from[..=j]));
                    }
                }
                _ => {}
            }
        }

        // if the parentheses are unbalanced just ignore this cfg -- it'll be caught when attempting
        // to run the compiler, and there's no real reason to lint it separately here
        None
    })
    .collect()
}
<|endoftext|>