<commit_before>//! Output advertising capabilities
//!
//! This module provides a type helping you to handle the advertising
//! of your compositor's output and their capabilities to your client,
//! as well as mapping your clients output request to your physical
//! outputs.
//!
//! # How to use it
//!
//! You need to instantiate an [`Output`]
//! for each output global you want to advertise to clients.
//!
//! Just add it to your Display using the [`Output::new(..)`](Output::new)
//! method. You can use the returned [`Output`] to change
//! the properties of your output (if the current resolution mode changes for example),
//! it'll automatically forward any changes to the clients.
//!
//! ```
//! # extern crate wayland_server;
//! # extern crate smithay;
//! use smithay::wayland::output::{Output, PhysicalProperties, Mode};
//! use wayland_server::protocol::wl_output;
//!
//! # let mut display = wayland_server::Display::new();
//! // Create the Output with given name and physical properties
//! let (output, _output_global) = Output::new(
//!     &mut display,      // the display
//!     "output-0".into(), // the name of this output,
//!     PhysicalProperties {
//!         size: (200, 150).into(),        // dimensions (width, height) in mm
//!         subpixel: wl_output::Subpixel::HorizontalRgb,  // subpixel information
//!         make: "Screens Inc".into(),     // make of the monitor
//!         model: "Monitor Ultra".into(),  // model of the monitor
//!     },
//!     None // insert a logger here
//! );
//! // Now you can configure it
//! output.change_current_state(
//!     Some(Mode { size: (1920, 1080).into(), refresh: 60000 }), // the resolution mode,
//!     Some(wl_output::Transform::Normal), // global screen transformation
//!     Some(1), // global screen scaling factor
//!     Some((0,0).into()) // output position
//! );
//! // set the preferred mode
//! output.set_preferred(Mode { size: (1920, 1080).into(), refresh: 60000 });
//! // add other supported modes
//! output.add_mode(Mode { size: (800, 600).into(), refresh: 60000 });
//! output.add_mode(Mode { size: (1024, 768).into(), refresh: 60000 });
//! ```

pub mod xdg;

use std::{
    ops::Deref as _,
    sync::{Arc, Mutex},
};

use wayland_server::protocol::{
    wl_output::{Subpixel, Transform},
    wl_surface,
};
use wayland_server::{
    protocol::wl_output::{Mode as WMode, WlOutput},
    Client, Display, Filter, Global, Main, UserDataMap,
};

use slog::{info, o, trace, warn};

use crate::utils::{Logical, Physical, Point, Raw, Size};

use self::xdg::XdgOutput;

/// An output mode
///
/// A possible combination of dimensions and refresh rate for an output.
///
/// This should only describe the characteristics of the video driver,
/// not taking into account any global scaling.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Mode {
    /// The size of the mode, in pixels
    pub size: Size<i32, Physical>,
    /// The refresh rate in millihertz
    ///
    /// `1000` is one fps (frame per second), `2000` is 2 fps, etc...
    pub refresh: i32,
}

/// The physical properties of an output
#[derive(Debug)]
pub struct PhysicalProperties {
    /// The size of the monitor, in millimeters
    pub size: Size<i32, Raw>,
    /// The subpixel geometry
    pub subpixel: Subpixel,
    /// Textual representation of the make
    pub make: String,
    /// Textual representation of the model
    pub model: String,
}

#[derive(Debug)]
pub(crate) struct Inner {
    name: String,
    pub(crate) log: ::slog::Logger,
    instances: Vec<WlOutput>,
    physical: PhysicalProperties,
    location: Point<i32, Logical>,
    transform: Transform,
    scale: i32,
    modes: Vec<Mode>,
    current_mode: Option<Mode>,
    preferred_mode: Option<Mode>,

    xdg_output: Option<XdgOutput>,
}

type InnerType = Arc<(Mutex<Inner>, UserDataMap)>;

impl Inner {
    fn new_global(&mut self, output: WlOutput) {
        trace!(self.log, "New global instantiated.");

        if self.modes.is_empty() {
            warn!(self.log, "Output is used with no modes set"; "name" => &self.name);
        }
        if self.current_mode.is_none() {
            warn!(self.log, "Output is used with no current mod set"; "name" => &self.name);
        }
        if self.preferred_mode.is_none() {
            warn!(self.log, "Output is used with not preferred mode set"; "name" => &self.name);
        }

        self.send_geometry(&output);
        for &mode in &self.modes {
            let mut flags = WMode::empty();
            if Some(mode) == self.current_mode {
                flags |= WMode::Current;
            }
            if Some(mode) == self.preferred_mode {
                flags |= WMode::Preferred;
            }
            output.mode(flags, mode.size.w, mode.size.h, mode.refresh);
        }
        if output.as_ref().version() >= 2 {
            output.scale(self.scale);
            output.done();
        }

        self.instances.push(output);
    }

    fn send_geometry(&self, output: &WlOutput) {
        output.geometry(
            self.location.x,
            self.location.y,
            self.physical.size.w,
            self.physical.size.h,
            self.physical.subpixel,
            self.physical.make.clone(),
            self.physical.model.clone(),
            self.transform,
        );
    }
}

/// An output as seen by the clients
///
/// This handle is stored in the event loop, and allows you to notify clients
/// about any change in the properties of this output.
#[derive(Debug, Clone)]
pub struct Output {
    pub(crate) inner: InnerType,
}

impl Output {
    /// Create a new output global with given name and physical properties
    ///
    /// The global is directly registered into the event loop, and this function
    /// returns the state token allowing you to access it, as well as the global handle,
    /// in case you wish to remove this global in the future.
    pub fn new<L>(
        display: &mut Display,
        name: String,
        physical: PhysicalProperties,
        logger: L,
    ) -> (Output, Global<WlOutput>)
    where
        L: Into<Option<::slog::Logger>>,
    {
        let log = crate::slog_or_fallback(logger).new(o!("smithay_module" => "output_handler"));

        info!(log, "Creating new wl_output"; "name" => &name);

        let inner = Arc::new((
            Mutex::new(Inner {
                name,
                log,
                instances: Vec::new(),
                physical,
                location: (0, 0).into(),
                transform: Transform::Normal,
                scale: 1,
                modes: Vec::new(),
                current_mode: None,
                preferred_mode: None,
                xdg_output: None,
            }),
            UserDataMap::default(),
        ));

        let output = Output { inner: inner.clone() };

        let global = display.create_global(
            3,
            Filter::new(move |(output, _version): (Main<WlOutput>, _), _, _| {
                output.assign_destructor(Filter::new(|output: WlOutput, _, _| {
                    let inner = output.as_ref().user_data().get::<InnerType>().unwrap();
                    inner
                        .0
                        .lock()
                        .unwrap()
                        .instances
                        .retain(|o| !o.as_ref().equals(output.as_ref()));
                }));
                output.as_ref().user_data().set_threadsafe({
                    let inner = inner.clone();
                    move || inner
                });
                inner.0.lock().unwrap().new_global(output.deref().clone());
            }),
        );

        (output, global)
    }

    /// Attempt to retrieve a [`Output`] from an existing resource
    pub fn from_resource(output: &WlOutput) -> Option<Output> {
        output
            .as_ref()
            .user_data()
            .get::<InnerType>()
            .cloned()
            .map(|inner| Output { inner })
    }

    /// Sets the preferred mode of this output
    ///
    /// If the provided mode was not previously known to this output, it is added to its
    /// internal list.
    pub fn set_preferred(&self, mode: Mode) {
        let mut inner = self.inner.0.lock().unwrap();
        inner.preferred_mode = Some(mode);
        if inner.modes.iter().all(|&m| m != mode) {
            inner.modes.push(mode);
        }
    }

    /// Adds a mode to the list of known modes to this output
    pub fn add_mode(&self, mode: Mode) {
        let mut inner = self.inner.0.lock().unwrap();
        if inner.modes.iter().all(|&m| m != mode) {
            inner.modes.push(mode);
        }
    }

    /// Returns the currently advertised mode of the output
    pub fn current_mode(&self) -> Option<Mode> {
        self.inner.0.lock().unwrap().current_mode
    }

    /// Returns the currently advertised transformation of the output
    pub fn current_transform(&self) -> Transform {
        self.inner.0.lock().unwrap().transform
    }

    /// Returns the currenly advertised scale of the output
    pub fn current_scale(&self) -> i32 {
        self.inner.0.lock().unwrap().scale
    }

    /// Returns the name of the output
    pub fn name(&self) -> String {
        self.inner.0.lock().unwrap().name.clone()
    }

    /// Removes a mode from the list of known modes
    ///
    /// It will not de-advertise it from existing clients (the protocol does not
    /// allow it), but it won't be advertised to now clients from now on.
    pub fn delete_mode(&self, mode: Mode) {
        let mut inner = self.inner.0.lock().unwrap();
        inner.modes.retain(|&m| m != mode);
        if inner.current_mode == Some(mode) {
            inner.current_mode = None;
        }
        if inner.preferred_mode == Some(mode) {
            inner.preferred_mode = None;
        }
    }

    /// Change the current state of this output
    ///
    /// You can changed the current mode, transform status, location or scale of this output. Providing
    /// `None` to any of these field means that the value does not change.
    ///
    /// If the provided mode was not previously known to this output, it is added to its
    /// internal list.
    ///
    /// By default, transform status is `Normal`, and scale is `1`.
    pub fn change_current_state(
        &self,
        new_mode: Option<Mode>,
        new_transform: Option<Transform>,
        new_scale: Option<i32>,
        new_location: Option<Point<i32, Logical>>,
    ) {
        let mut inner = self.inner.0.lock().unwrap();
        if let Some(mode) = new_mode {
            if inner.modes.iter().all(|&m| m != mode) {
                inner.modes.push(mode);
            }
            inner.current_mode = new_mode;
        }
        if let Some(transform) = new_transform {
            inner.transform = transform;
        }
        if let Some(scale) = new_scale {
            inner.scale = scale;
        }
        let mut flags = WMode::Current;
        if inner.preferred_mode == new_mode {
            flags |= WMode::Preferred;
        }
        if let Some(new_location) = new_location {
            inner.location = new_location;
        }

        // XdgOutput has to be updated before WlOutput
        // Because WlOutput::done() has to allways be called last
        if let Some(xdg_output) = inner.xdg_output.as_ref() {
            xdg_output.change_current_state(new_mode, new_scale, new_location);
        }

        for output in &inner.instances {
            if let Some(mode) = new_mode {
                output.mode(flags, mode.size.w, mode.size.h, mode.refresh);
            }
            if new_transform.is_some() || new_location.is_some() {
                inner.send_geometry(output);
            }
            if let Some(scale) = new_scale {
                if output.as_ref().version() >= 2 {
                    output.scale(scale);
                }
            }
            if output.as_ref().version() >= 2 {
                output.done();
            }
        }
    }

    /// Check is given [`wl_output`](WlOutput) instance is managed by this [`Output`].
    pub fn owns(&self, output: &WlOutput) -> bool {
        self.inner
            .0
            .lock()
            .unwrap()
            .instances
            .iter()
            .any(|o| o.as_ref().equals(output.as_ref()))
    }

    /// This function allows to run a [FnMut] on every
    /// [WlOutput] matching the same [Client] as provided
    pub fn with_client_outputs<F>(&self, client: Client, f: F)
    where
        F: FnMut(&WlOutput),
    {
        self.inner
            .0
            .lock()
            .unwrap()
            .instances
            .iter()
            .filter(|output| match output.as_ref().client() {
                Some(output_client) => output_client.equals(&client),
                None => false,
            })
            .for_each(f)
    }

    /// Sends `wl_surface.enter` for the provided surface
    /// with the matching client output
    pub fn enter(&self, surface: &wl_surface::WlSurface) {
        if let Some(client) = surface.as_ref().client() {
            self.with_client_outputs(client, |output| surface.enter(output))
        }
    }

    /// Sends `wl_surface.leave` for the provided surface
    /// with the matching client output
    pub fn leave(&self, surface: &wl_surface::WlSurface) {
        if let Some(client) = surface.as_ref().client() {
            self.with_client_outputs(client, |output| surface.leave(output))
        }
    }

    /// Returns the user data of this output
    pub fn user_data(&self) -> &UserDataMap {
        &self.inner.1
    }
}

impl PartialEq for Output {
    fn eq(&self, other: &Self) -> bool {
        Arc::ptr_eq(&self.inner, &other.inner)
    }
}

impl Eq for Output {}
<commit_msg>output: Make it possible to query physical properties<commit_after>//! Output advertising capabilities
//!
//! This module provides a type helping you to handle the advertising
//! of your compositor's output and their capabilities to your client,
//! as well as mapping your clients output request to your physical
//! outputs.
//!
//! # How to use it
//!
//! You need to instantiate an [`Output`]
//! for each output global you want to advertise to clients.
//!
//! Just add it to your Display using the [`Output::new(..)`](Output::new)
//! method. You can use the returned [`Output`] to change
//! the properties of your output (if the current resolution mode changes for example),
//! it'll automatically forward any changes to the clients.
//!
//! ```
//! # extern crate wayland_server;
//! # extern crate smithay;
//! use smithay::wayland::output::{Output, PhysicalProperties, Mode};
//! use wayland_server::protocol::wl_output;
//!
//! # let mut display = wayland_server::Display::new();
//! // Create the Output with given name and physical properties
//! let (output, _output_global) = Output::new(
//!     &mut display,      // the display
//!     "output-0".into(), // the name of this output,
//!     PhysicalProperties {
//!         size: (200, 150).into(),        // dimensions (width, height) in mm
//!         subpixel: wl_output::Subpixel::HorizontalRgb,  // subpixel information
//!         make: "Screens Inc".into(),     // make of the monitor
//!         model: "Monitor Ultra".into(),  // model of the monitor
//!     },
//!     None // insert a logger here
//! );
//! // Now you can configure it
//! output.change_current_state(
//!     Some(Mode { size: (1920, 1080).into(), refresh: 60000 }), // the resolution mode,
//!     Some(wl_output::Transform::Normal), // global screen transformation
//!     Some(1), // global screen scaling factor
//!     Some((0,0).into()) // output position
//! );
//! // set the preferred mode
//! output.set_preferred(Mode { size: (1920, 1080).into(), refresh: 60000 });
//! // add other supported modes
//! output.add_mode(Mode { size: (800, 600).into(), refresh: 60000 });
//! output.add_mode(Mode { size: (1024, 768).into(), refresh: 60000 });
//! ```

pub mod xdg;

use std::{
    ops::Deref as _,
    sync::{Arc, Mutex},
};

use wayland_server::protocol::{
    wl_output::{Subpixel, Transform},
    wl_surface,
};
use wayland_server::{
    protocol::wl_output::{Mode as WMode, WlOutput},
    Client, Display, Filter, Global, Main, UserDataMap,
};

use slog::{info, o, trace, warn};

use crate::utils::{Logical, Physical, Point, Raw, Size};

use self::xdg::XdgOutput;

/// An output mode
///
/// A possible combination of dimensions and refresh rate for an output.
///
/// This should only describe the characteristics of the video driver,
/// not taking into account any global scaling.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Mode {
    /// The size of the mode, in pixels
    pub size: Size<i32, Physical>,
    /// The refresh rate in millihertz
    ///
    /// `1000` is one fps (frame per second), `2000` is 2 fps, etc...
    pub refresh: i32,
}

/// The physical properties of an output
#[derive(Debug, Clone)]
pub struct PhysicalProperties {
    /// The size of the monitor, in millimeters
    pub size: Size<i32, Raw>,
    /// The subpixel geometry
    pub subpixel: Subpixel,
    /// Textual representation of the make
    pub make: String,
    /// Textual representation of the model
    pub model: String,
}

#[derive(Debug)]
pub(crate) struct Inner {
    name: String,
    pub(crate) log: ::slog::Logger,
    instances: Vec<WlOutput>,
    physical: PhysicalProperties,
    location: Point<i32, Logical>,
    transform: Transform,
    scale: i32,
    modes: Vec<Mode>,
    current_mode: Option<Mode>,
    preferred_mode: Option<Mode>,

    xdg_output: Option<XdgOutput>,
}

type InnerType = Arc<(Mutex<Inner>, UserDataMap)>;

impl Inner {
    fn new_global(&mut self, output: WlOutput) {
        trace!(self.log, "New global instantiated.");

        if self.modes.is_empty() {
            warn!(self.log, "Output is used with no modes set"; "name" => &self.name);
        }
        if self.current_mode.is_none() {
            warn!(self.log, "Output is used with no current mod set"; "name" => &self.name);
        }
        if self.preferred_mode.is_none() {
            warn!(self.log, "Output is used with not preferred mode set"; "name" => &self.name);
        }

        self.send_geometry(&output);
        for &mode in &self.modes {
            let mut flags = WMode::empty();
            if Some(mode) == self.current_mode {
                flags |= WMode::Current;
            }
            if Some(mode) == self.preferred_mode {
                flags |= WMode::Preferred;
            }
            output.mode(flags, mode.size.w, mode.size.h, mode.refresh);
        }
        if output.as_ref().version() >= 2 {
            output.scale(self.scale);
            output.done();
        }

        self.instances.push(output);
    }

    fn send_geometry(&self, output: &WlOutput) {
        output.geometry(
            self.location.x,
            self.location.y,
            self.physical.size.w,
            self.physical.size.h,
            self.physical.subpixel,
            self.physical.make.clone(),
            self.physical.model.clone(),
            self.transform,
        );
    }
}

/// An output as seen by the clients
///
/// This handle is stored in the event loop, and allows you to notify clients
/// about any change in the properties of this output.
#[derive(Debug, Clone)]
pub struct Output {
    pub(crate) inner: InnerType,
}

impl Output {
    /// Create a new output global with given name and physical properties
    ///
    /// The global is directly registered into the event loop, and this function
    /// returns the state token allowing you to access it, as well as the global handle,
    /// in case you wish to remove this global in the future.
    pub fn new<L>(
        display: &mut Display,
        name: String,
        physical: PhysicalProperties,
        logger: L,
    ) -> (Output, Global<WlOutput>)
    where
        L: Into<Option<::slog::Logger>>,
    {
        let log = crate::slog_or_fallback(logger).new(o!("smithay_module" => "output_handler"));

        info!(log, "Creating new wl_output"; "name" => &name);

        let inner = Arc::new((
            Mutex::new(Inner {
                name,
                log,
                instances: Vec::new(),
                physical,
                location: (0, 0).into(),
                transform: Transform::Normal,
                scale: 1,
                modes: Vec::new(),
                current_mode: None,
                preferred_mode: None,
                xdg_output: None,
            }),
            UserDataMap::default(),
        ));

        let output = Output { inner: inner.clone() };

        let global = display.create_global(
            3,
            Filter::new(move |(output, _version): (Main<WlOutput>, _), _, _| {
                output.assign_destructor(Filter::new(|output: WlOutput, _, _| {
                    let inner = output.as_ref().user_data().get::<InnerType>().unwrap();
                    inner
                        .0
                        .lock()
                        .unwrap()
                        .instances
                        .retain(|o| !o.as_ref().equals(output.as_ref()));
                }));
                output.as_ref().user_data().set_threadsafe({
                    let inner = inner.clone();
                    move || inner
                });
                inner.0.lock().unwrap().new_global(output.deref().clone());
            }),
        );

        (output, global)
    }

    /// Attempt to retrieve a [`Output`] from an existing resource
    pub fn from_resource(output: &WlOutput) -> Option<Output> {
        output
            .as_ref()
            .user_data()
            .get::<InnerType>()
            .cloned()
            .map(|inner| Output { inner })
    }

    /// Sets the preferred mode of this output
    ///
    /// If the provided mode was not previously known to this output, it is added to its
    /// internal list.
    pub fn set_preferred(&self, mode: Mode) {
        let mut inner = self.inner.0.lock().unwrap();
        inner.preferred_mode = Some(mode);
        if inner.modes.iter().all(|&m| m != mode) {
            inner.modes.push(mode);
        }
    }

    /// Adds a mode to the list of known modes to this output
    pub fn add_mode(&self, mode: Mode) {
        let mut inner = self.inner.0.lock().unwrap();
        if inner.modes.iter().all(|&m| m != mode) {
            inner.modes.push(mode);
        }
    }

    /// Returns the currently advertised mode of the output
    pub fn current_mode(&self) -> Option<Mode> {
        self.inner.0.lock().unwrap().current_mode
    }

    /// Returns the currently advertised transformation of the output
    pub fn current_transform(&self) -> Transform {
        self.inner.0.lock().unwrap().transform
    }

    /// Returns the currenly advertised scale of the output
    pub fn current_scale(&self) -> i32 {
        self.inner.0.lock().unwrap().scale
    }

    /// Returns the name of the output
    pub fn name(&self) -> String {
        self.inner.0.lock().unwrap().name.clone()
    }

    /// Returns the physical properties of the output
    pub fn physical_properties(&self) -> PhysicalProperties {
        self.inner.0.lock().unwrap().physical.clone()
    }

    /// Removes a mode from the list of known modes
    ///
    /// It will not de-advertise it from existing clients (the protocol does not
    /// allow it), but it won't be advertised to now clients from now on.
    pub fn delete_mode(&self, mode: Mode) {
        let mut inner = self.inner.0.lock().unwrap();
        inner.modes.retain(|&m| m != mode);
        if inner.current_mode == Some(mode) {
            inner.current_mode = None;
        }
        if inner.preferred_mode == Some(mode) {
            inner.preferred_mode = None;
        }
    }

    /// Change the current state of this output
    ///
    /// You can changed the current mode, transform status, location or scale of this output. Providing
    /// `None` to any of these field means that the value does not change.
    ///
    /// If the provided mode was not previously known to this output, it is added to its
    /// internal list.
    ///
    /// By default, transform status is `Normal`, and scale is `1`.
    pub fn change_current_state(
        &self,
        new_mode: Option<Mode>,
        new_transform: Option<Transform>,
        new_scale: Option<i32>,
        new_location: Option<Point<i32, Logical>>,
    ) {
        let mut inner = self.inner.0.lock().unwrap();
        if let Some(mode) = new_mode {
            if inner.modes.iter().all(|&m| m != mode) {
                inner.modes.push(mode);
            }
            inner.current_mode = new_mode;
        }
        if let Some(transform) = new_transform {
            inner.transform = transform;
        }
        if let Some(scale) = new_scale {
            inner.scale = scale;
        }
        let mut flags = WMode::Current;
        if inner.preferred_mode == new_mode {
            flags |= WMode::Preferred;
        }
        if let Some(new_location) = new_location {
            inner.location = new_location;
        }

        // XdgOutput has to be updated before WlOutput
        // Because WlOutput::done() has to allways be called last
        if let Some(xdg_output) = inner.xdg_output.as_ref() {
            xdg_output.change_current_state(new_mode, new_scale, new_location);
        }

        for output in &inner.instances {
            if let Some(mode) = new_mode {
                output.mode(flags, mode.size.w, mode.size.h, mode.refresh);
            }
            if new_transform.is_some() || new_location.is_some() {
                inner.send_geometry(output);
            }
            if let Some(scale) = new_scale {
                if output.as_ref().version() >= 2 {
                    output.scale(scale);
                }
            }
            if output.as_ref().version() >= 2 {
                output.done();
            }
        }
    }

    /// Check is given [`wl_output`](WlOutput) instance is managed by this [`Output`].
    pub fn owns(&self, output: &WlOutput) -> bool {
        self.inner
            .0
            .lock()
            .unwrap()
            .instances
            .iter()
            .any(|o| o.as_ref().equals(output.as_ref()))
    }

    /// This function allows to run a [FnMut] on every
    /// [WlOutput] matching the same [Client] as provided
    pub fn with_client_outputs<F>(&self, client: Client, f: F)
    where
        F: FnMut(&WlOutput),
    {
        self.inner
            .0
            .lock()
            .unwrap()
            .instances
            .iter()
            .filter(|output| match output.as_ref().client() {
                Some(output_client) => output_client.equals(&client),
                None => false,
            })
            .for_each(f)
    }

    /// Sends `wl_surface.enter` for the provided surface
    /// with the matching client output
    pub fn enter(&self, surface: &wl_surface::WlSurface) {
        if let Some(client) = surface.as_ref().client() {
            self.with_client_outputs(client, |output| surface.enter(output))
        }
    }

    /// Sends `wl_surface.leave` for the provided surface
    /// with the matching client output
    pub fn leave(&self, surface: &wl_surface::WlSurface) {
        if let Some(client) = surface.as_ref().client() {
            self.with_client_outputs(client, |output| surface.leave(output))
        }
    }

    /// Returns the user data of this output
    pub fn user_data(&self) -> &UserDataMap {
        &self.inner.1
    }
}

impl PartialEq for Output {
    fn eq(&self, other: &Self) -> bool {
        Arc::ptr_eq(&self.inner, &other.inner)
    }
}

impl Eq for Output {}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate regex;
extern crate xml;

pub mod parse;
pub mod serialize;

use clap::{App, Arg};
use std::fs::File;
use std::io::{BufReader, Read};

fn main() {
    let args = App::new("script-extractor")
                   .version(&crate_version!())
                   .about("Parse movie scripts into a structured format")
                   .after_help("Input has to be in the format generated by \
                                poppler's 'pdftohtml -xml'.\n\
                                Reads from stdin if no file or '-' is specified.")
                   .arg(Arg::with_name("input-file")
                            .help("input file in poppler's xml format")
                            .index(1)
                            .validator(check_file_exists))
                   .get_matches();

    let mut input: Box<Read> = if let Some(input_file) = args.value_of("input-file") {
        if input_file == "-" {
            Box::new(BufReader::new(std::io::stdin()))
        } else {
            let file_reader = File::open(input_file).expect("Cannot open input-file");
            Box::new(BufReader::new(file_reader))
        }
    } else {
        Box::new(BufReader::new(std::io::stdin()))
    };

    let scenes = parse::parse_script(&mut input);

    serialize::xml::format_script(&scenes, &mut std::io::stdout()).unwrap();
}

/// Check if the given file exists. Also considers "-" as a valid file.
fn check_file_exists(file_name: String) -> Result<(), String> {
    if file_name == "-" {
        Ok(())
    } else if let Ok(metadata) = std::fs::metadata(&file_name) {
        if metadata.is_file() && !metadata.permissions().readonly() {
            Ok(())
        } else {
            Err(format!("Cannot read file '{}'", file_name))
        }
    } else {
        Err(format!("File '{}' not found", file_name))
    }
}
<commit_msg>Add the possibility to filter the output by a page range<commit_after>#[macro_use]
extern crate clap;
extern crate regex;
extern crate xml;

pub mod parse;
pub mod serialize;

use clap::{App, Arg};
use parse::Script;
use regex::Regex;
use std::fs::File;
use std::io::{BufReader, Read};

fn main() {
    let args = App::new("script-extractor")
                   .version(&crate_version!())
                   .about("Parse movie scripts into a structured format")
                   .after_help("Input has to be in the format generated by \
                                poppler's 'pdftohtml -xml'.\n\
                                Reads from stdin if no file or '-' is specified.")
                   .arg(Arg::with_name("input-file")
                            .help("input file in poppler's xml format")
                            .index(1)
                            .validator(check_file_exists))
                   .arg(Arg::with_name("pages")
                            .help("Specify a single page or range of pages to extract")
                            .short("p")
                            .long("pages")
                            .takes_value(true)
                            .validator(|v| if extract_range(&v).is_some() {
                                               Ok(())
                                           } else {
                                               Err(format!("Invalid page range '{}'", v))
                                           }))
                   .get_matches();

    let mut input: Box<Read> = if let Some(input_file) = args.value_of("input-file") {
        if input_file == "-" {
            Box::new(BufReader::new(std::io::stdin()))
        } else {
            let file_reader = File::open(input_file).expect("Cannot open input-file");
            Box::new(BufReader::new(file_reader))
        }
    } else {
        Box::new(BufReader::new(std::io::stdin()))
    };

    let mut script = parse::parse_script(&mut input);

    // filter by pages if requested
    if let Some(range_string) = args.value_of("pages") {
        let range = extract_range(range_string).unwrap_or((0,u32::max_value()));
        script = filter_script(script, range);
    }

    serialize::xml::format_script(&script, &mut std::io::stdout()).unwrap();
}

/// Check if the given file exists. Also considers "-" as a valid file.
fn check_file_exists(file_name: String) -> Result<(), String> {
    if file_name == "-" {
        Ok(())
    } else if let Ok(metadata) = std::fs::metadata(&file_name) {
        if metadata.is_file() && !metadata.permissions().readonly() {
            Ok(())
        } else {
            Err(format!("Cannot read file '{}'", file_name))
        }
    } else {
        Err(format!("File '{}' not found", file_name))
    }
}

/// Parses the given string into a range.
///
/// This is used when parsing the "--pages" cli argument.
///
/// # Examples
///
/// ```
/// assert_eq!(extract_range("42"), (42, 42));
/// assert_eq!(extract_range("3-15"), (3, 15));
/// assert_eq!(extract_range("foo"), (0, u32::max_value()));
/// ```
pub fn extract_range(range_string: &str) -> Option<(u32, u32)> {
    let range_regex = Regex::new(r"^(?P<lower>\d+)-(?P<upper>\d+)$").unwrap();

    if let Ok(page) = range_string.parse() {
        return Some((page, page));
    } else if let Some(captures) = range_regex.captures(range_string) {
        if let (Ok(lower), Ok(upper)) = (captures.name("lower").unwrap().parse(),
                                         captures.name("upper").unwrap().parse()) {
            return Some((lower, upper));
        }
    }

    None
}

/// Filter the script using a range of pages.
///
/// The range is inclusive and empty scenes and locations are removed.
pub fn filter_script(script: Script, page_range: (u32, u32)) -> Script {
    use parse::{Scene, ScenePart};

    let (lower, upper) = page_range;

    script.into_iter().filter_map(|scene| {
        let filtered_scene: Scene = scene.into_iter().filter_map(|mut location| {
            let filtered_scene_parts: Vec<ScenePart> = location.parts.into_iter().filter_map(|scene_part| {
                let page = match scene_part {
                    ScenePart::Direction { page, .. } => page,
                    ScenePart::Dialog { page, .. } => page
                };

                // filter using the given range
                if lower <= page && page <= upper {
                    Some(scene_part)
                } else {
                    None
                }
            }).collect();

            // filter out locations with no scene parts
            if filtered_scene_parts.len() > 0 {
                location.parts = filtered_scene_parts;
                Some(location)
            } else {
                None
            }
        }).collect();

        // filter out scenes with no locations
        if filtered_scene.len() > 0 {
            Some(filtered_scene)
        } else {
            None
        }
    }).collect()
}
<|endoftext|>


<commit_before>extern crate xml;
extern crate regex;

use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::Read;
use std::io::BufReader;

use xml::reader::{EventReader, XmlEvent};
use xml::attribute::OwnedAttribute;

#[derive(Debug, Clone, Default)]
struct LineAttributes {
    top: i32,
    left: i32,
    height: i32,
}

fn read_attributes(attr_list: &Vec<OwnedAttribute>) -> LineAttributes {
    let mut attributes : LineAttributes = Default::default();

    for attr in attr_list {
        match attr.name.local_name.as_ref() {
            "top" => attributes.top = attr.value.parse().unwrap(),
            "left" => attributes.left = attr.value.parse().unwrap(),
            "height" => attributes.height = attr.value.parse().unwrap(),
            _ => {}
        }
    }

    attributes
}

#[derive(Debug, Clone, Default)]
struct ScriptProperties {
    direction_position: i32,
    dialog_position: i32,
    speaker_direction_position: i32,
    speaker_position: i32,
    intra_paragraph_line_height: i32,
}

fn read_and_analyze_script(reader: Box<Read>) -> (ScriptProperties, Vec<(LineAttributes, String)>) {
    let mut script_properties: ScriptProperties = Default::default();
    let mut lines: Vec<(LineAttributes, String)> = Vec::new();

    // these maps are used to heuristically determine the ScriptProperties
    let mut position_uses: HashMap<i32, u32> = HashMap::new();
    let mut line_height_uses: HashMap<i32, u32> = HashMap::new();

    // states for the streaming xml parsing
    let mut current_line_attributes: LineAttributes = Default::default();
    let mut current_text_buffer = String::new();
    let mut last_line_height = 0;

    let parser = EventReader::new(reader);
    for event in parser {
        match event {
            Ok(XmlEvent::StartElement { name, attributes, .. }) => {
                match name.local_name.as_ref() {
                    "text" => {
                        current_line_attributes = read_attributes(&attributes);

                        current_text_buffer.clear();

                        // increase the count for the current left margin
                        *position_uses.entry(current_line_attributes.left)
                                      .or_insert(0) += 1;

                        // increase the count for the current top position diff
                        *line_height_uses.entry(current_line_attributes.top - last_line_height)
                                         .or_insert(0) += 1;
                        last_line_height = current_line_attributes.top;
                    }
                    "page" => {
                        last_line_height = 0;
                    }
                    _ => {}
                }
            }
            Ok(XmlEvent::Characters(text)) => {
                if current_text_buffer.len() > 0 {
                    current_text_buffer.push(' ');
                }
                current_text_buffer.push_str(text.trim());
            }
            Ok(XmlEvent::EndElement { name, .. }) => {
                if name.local_name == "text" {
                    lines.push((current_line_attributes.clone(), current_text_buffer.clone()));
                }
            }
            Ok(_) => {},
            Err(e) => panic!("Error parsing xml: {}", e),
        }
    }

    // the position_uses map should at least have 3 different entries
    if position_uses.len() < 3 {
        panic!("Script uses strange layout");
    }

    // copy the position uses map into a vector and sort by value desc
    let mut position_uses_vec = position_uses.into_iter().collect::<Vec<_>>();
    position_uses_vec.sort_by(|&a, &b| b.1.cmp(&a.1));

    // sort the top 3 entries by position again (right-exclusive range)
    position_uses_vec[0..3].sort_by(|&a, &b| b.0.cmp(&a.0));

    script_properties.speaker_position = position_uses_vec[0].0;
    script_properties.dialog_position = position_uses_vec[1].0;
    script_properties.direction_position = position_uses_vec[2].0;

    // take the next value between speaker and dialog as speaker_direction
    for &value in position_uses_vec.iter().skip(3) {
        let (position, _) = value;
        if position > script_properties.dialog_position &&
           position < script_properties.speaker_position {
            script_properties.speaker_direction_position = position;
            break;
        }
    }

    // use to most used line diff value to determine the specific sections
    let mut last_uses: u32 = 0;
    for (&line_height, &uses) in line_height_uses.iter() {
        if uses > last_uses {
            script_properties.intra_paragraph_line_height = line_height;
            last_uses = uses;
        }
    }

    (script_properties, lines)
}

#[derive(Debug, Clone)]
enum ScenePart {
    Direction {
        direction: String,
    },
    Dialog {
        speaker: String,
        direction: String,
        dialog: String,
    }
}

#[derive(Debug, Clone)]
enum ScriptPart {
    Separator,
    ScenePart(ScenePart),
    LocationChange(String),
    SceneChange,
    PageNumber(i32),
}

fn is_location_change(line: &str) -> bool {
    line.starts_with("INT.") || line.starts_with("EXT.")
}

fn is_scene_change(line: &str) -> bool {
    line.starts_with("CUT TO")
}

fn extract_script_parts(properties: ScriptProperties, lines: &Vec<(LineAttributes, String)>)
    -> Vec<ScriptPart> {
    let mut script_parts: Vec<ScriptPart> = Vec::new();
    let mut last_top_position = 0;
    let is_page_number = regex::Regex::new(r"^[0-9]+\s*\.*$").unwrap();

    for line in lines.iter() {
        let &(ref attributes, ref line) = line;

        if line.len() == 0 {
            continue;
        }

        // check if a new section starts
        if attributes.top - last_top_position > 18 ||
           attributes.top - last_top_position < 0 {
            // used to separate two consecutive script parts of the
            // same type. this is just a implementation detail of
            // the parsing. the Separator can be ignored later.
            script_parts.push(ScriptPart::Separator);
        }

        if attributes.left == properties.direction_position {
            if is_location_change(line) {
                script_parts.push(ScriptPart::LocationChange(line.clone()));
            } else {
                // ensure the last script part is a direction
                if let Some(&ScriptPart::ScenePart(ScenePart::Direction{..})) = script_parts.last() {
                } else {
                    script_parts.push(ScriptPart::ScenePart(
                        ScenePart::Direction{
                            direction: String::new()}));
                }

                if let Some(&mut ScriptPart::ScenePart(ScenePart::Direction{ref mut direction})) = script_parts.last_mut() {
                    if direction.len() > 0 {
                        direction.push(' ');
                    }
                    direction.push_str(line);
                }
            }
        } else if attributes.left == properties.speaker_position ||
                  attributes.left == properties.speaker_direction_position ||
                  attributes.left == properties.dialog_position {
            // Ensure the last script part is a dialog
            if let Some(&ScriptPart::ScenePart(ScenePart::Dialog{..})) = script_parts.last() {
            } else {
                script_parts.push(ScriptPart::ScenePart(
                    ScenePart::Dialog{
                        speaker: String::new(),
                        direction: String::new(),
                        dialog: String::new()}));
            }

            // get the dialog, should never fail (see above)
            if let Some(&mut ScriptPart::ScenePart(ScenePart::Dialog{ref mut speaker, ref mut direction, ref mut dialog})) = script_parts.last_mut() {
                if attributes.left == properties.speaker_position {
                    // there is only one speaker per dialog
                    speaker.push_str(line);
                } else if attributes.left == properties.speaker_direction_position {
                    if direction.len() > 0 {
                        direction.push(' ');
                    }
                    direction.push_str(line);
                } else if attributes.left == properties.dialog_position {
                    if dialog.len() > 0 {
                        dialog.push(' ');
                    }
                    dialog.push_str(line);
                }
            }
        } else {
            if is_scene_change(line) {
                script_parts.push(ScriptPart::SceneChange);
            } else if is_page_number.is_match(line) {
                // TODO: parse actual page number
                script_parts.push(ScriptPart::PageNumber(0));
            }
        }

        last_top_position = attributes.top;
    }

    script_parts
}

fn condense_script_demo(properties: ScriptProperties, lines: &Vec<(LineAttributes, String)>) {
    let mut last_left_position = 0;
    let mut last_top_position = 0;

    for line in lines.iter() {
        let &(ref attributes, ref line) = line;

        if line.len() == 0 {
            continue;
        }

        // start new section
        if last_left_position != attributes.left ||
           attributes.top - last_top_position > 18 ||
           attributes.top - last_top_position < 0 {
            if ! (last_left_position == properties.speaker_direction_position ||
                  last_left_position == properties.speaker_position) {
                // empty line after last section
                println!("");
            }
        }

        match attributes.left {
            k if (k == properties.direction_position)
                => println!("{}", line),
            k if (k == properties.dialog_position)
                => println!("          {}", line),
            k if (k == properties.speaker_direction_position)
                => println!("               {}", line),
            k if (k == properties.speaker_position)
                => println!("                    {}", line),
            _   => println!("------- {}", line),
        }

        last_left_position = attributes.left;
        last_top_position = attributes.top;
    }
}

fn main() {
    let xml_file_name = env::args().skip(1).next().expect("No input file given");

    let file_reader = File::open(xml_file_name).expect("Could not open file");
    let buffered_file_reader = Box::new(BufReader::new(file_reader));

    let (properties, lines) = read_and_analyze_script(buffered_file_reader);

    condense_script_demo(properties, &lines);
    //println!("{:?}", extract_script_parts(properties, &lines));
}
<commit_msg>Implement extraction of scenes<commit_after>extern crate xml;
extern crate regex;

use std::collections::HashMap;
use std::env;
use std::fs::File;
use std::io::Read;
use std::io::BufReader;

use xml::reader::{EventReader, XmlEvent};
use xml::attribute::OwnedAttribute;

#[derive(Debug, Clone, Default)]
struct LineAttributes {
    top: i32,
    left: i32,
    height: i32,
}

fn read_attributes(attr_list: &Vec<OwnedAttribute>) -> LineAttributes {
    let mut attributes : LineAttributes = Default::default();

    for attr in attr_list {
        match attr.name.local_name.as_ref() {
            "top" => attributes.top = attr.value.parse().unwrap(),
            "left" => attributes.left = attr.value.parse().unwrap(),
            "height" => attributes.height = attr.value.parse().unwrap(),
            _ => {}
        }
    }

    attributes
}

#[derive(Debug, Clone, Default)]
struct ScriptProperties {
    direction_position: i32,
    dialog_position: i32,
    speaker_direction_position: i32,
    speaker_position: i32,
    intra_paragraph_line_height: i32,
}

fn read_and_analyze_script(reader: Box<Read>) -> (ScriptProperties, Vec<(LineAttributes, String)>) {
    let mut script_properties: ScriptProperties = Default::default();
    let mut lines: Vec<(LineAttributes, String)> = Vec::new();

    // these maps are used to heuristically determine the ScriptProperties
    let mut position_uses: HashMap<i32, u32> = HashMap::new();
    let mut line_height_uses: HashMap<i32, u32> = HashMap::new();

    // states for the streaming xml parsing
    let mut current_line_attributes: LineAttributes = Default::default();
    let mut current_text_buffer = String::new();
    let mut last_line_height = 0;

    let parser = EventReader::new(reader);
    for event in parser {
        match event {
            Ok(XmlEvent::StartElement { name, attributes, .. }) => {
                match name.local_name.as_ref() {
                    "text" => {
                        current_line_attributes = read_attributes(&attributes);

                        current_text_buffer.clear();

                        // increase the count for the current left margin
                        *position_uses.entry(current_line_attributes.left)
                                      .or_insert(0) += 1;

                        // increase the count for the current top position diff
                        *line_height_uses.entry(current_line_attributes.top - last_line_height)
                                         .or_insert(0) += 1;
                        last_line_height = current_line_attributes.top;
                    }
                    "page" => {
                        last_line_height = 0;
                    }
                    _ => {}
                }
            }
            Ok(XmlEvent::Characters(text)) => {
                if current_text_buffer.len() > 0 {
                    current_text_buffer.push(' ');
                }
                current_text_buffer.push_str(text.trim());
            }
            Ok(XmlEvent::EndElement { name, .. }) => {
                if name.local_name == "text" {
                    lines.push((current_line_attributes.clone(), current_text_buffer.clone()));
                }
            }
            Ok(_) => {},
            Err(e) => panic!("Error parsing xml: {}", e),
        }
    }

    // the position_uses map should at least have 3 different entries
    if position_uses.len() < 3 {
        panic!("Script uses strange layout");
    }

    // copy the position uses map into a vector and sort by value desc
    let mut position_uses_vec = position_uses.into_iter().collect::<Vec<_>>();
    position_uses_vec.sort_by(|&a, &b| b.1.cmp(&a.1));

    // sort the top 3 entries by position again (right-exclusive range)
    position_uses_vec[0..3].sort_by(|&a, &b| b.0.cmp(&a.0));

    script_properties.speaker_position = position_uses_vec[0].0;
    script_properties.dialog_position = position_uses_vec[1].0;
    script_properties.direction_position = position_uses_vec[2].0;

    // take the next value between speaker and dialog as speaker_direction
    for &value in position_uses_vec.iter().skip(3) {
        let (position, _) = value;
        if position > script_properties.dialog_position &&
           position < script_properties.speaker_position {
            script_properties.speaker_direction_position = position;
            break;
        }
    }

    // use to most used line diff value to determine the specific sections
    let mut last_uses: u32 = 0;
    for (&line_height, &uses) in line_height_uses.iter() {
        if uses > last_uses {
            script_properties.intra_paragraph_line_height = line_height;
            last_uses = uses;
        }
    }

    (script_properties, lines)
}

#[derive(Debug, Clone)]
enum ScenePart {
    Direction {
        direction: String,
    },
    Dialog {
        speaker: String,
        direction: String,
        dialog: String,
    }
}

#[derive(Debug, Clone)]
enum ScriptPart {
    Separator,
    ScenePart(ScenePart),
    LocationChange(String),
    SceneChange,
    PageNumber(i32),
}

fn is_location_change(line: &str) -> bool {
    line.starts_with("INT.") || line.starts_with("EXT.")
}

fn is_scene_change(line: &str) -> bool {
    line.starts_with("CUT TO")
}

fn extract_script_parts(properties: ScriptProperties, lines: &Vec<(LineAttributes, String)>)
    -> Vec<ScriptPart> {
    let mut script_parts: Vec<ScriptPart> = Vec::new();
    let mut last_top_position = 0;
    let is_page_number = regex::Regex::new(r"^[0-9]+\s*\.*$").unwrap();

    for line in lines.iter() {
        let &(ref attributes, ref line) = line;

        if line.len() == 0 {
            continue;
        }

        // check if a new section starts
        if attributes.top - last_top_position > 18 ||
           attributes.top - last_top_position < 0 {
            // used to separate two consecutive script parts of the
            // same type. this is just a implementation detail of
            // the parsing. the Separator can be ignored later.
            script_parts.push(ScriptPart::Separator);
        }

        if attributes.left == properties.direction_position {
            if is_location_change(line) {
                script_parts.push(ScriptPart::LocationChange(line.clone()));
            } else {
                // ensure the last script part is a direction
                if let Some(&ScriptPart::ScenePart(ScenePart::Direction{..})) = script_parts.last() {
                } else {
                    script_parts.push(ScriptPart::ScenePart(
                        ScenePart::Direction{
                            direction: String::new()}));
                }

                if let Some(&mut ScriptPart::ScenePart(ScenePart::Direction{ref mut direction})) = script_parts.last_mut() {
                    if direction.len() > 0 {
                        direction.push(' ');
                    }
                    direction.push_str(line);
                }
            }
        } else if attributes.left == properties.speaker_position ||
                  attributes.left == properties.speaker_direction_position ||
                  attributes.left == properties.dialog_position {
            // Ensure the last script part is a dialog
            if let Some(&ScriptPart::ScenePart(ScenePart::Dialog{..})) = script_parts.last() {
            } else {
                script_parts.push(ScriptPart::ScenePart(
                    ScenePart::Dialog{
                        speaker: String::new(),
                        direction: String::new(),
                        dialog: String::new()}));
            }

            // get the dialog, should never fail (see above)
            if let Some(&mut ScriptPart::ScenePart(ScenePart::Dialog{ref mut speaker, ref mut direction, ref mut dialog})) = script_parts.last_mut() {
                if attributes.left == properties.speaker_position {
                    // there is only one speaker per dialog
                    speaker.push_str(line);
                } else if attributes.left == properties.speaker_direction_position {
                    if direction.len() > 0 {
                        direction.push(' ');
                    }
                    direction.push_str(line);
                } else if attributes.left == properties.dialog_position {
                    if dialog.len() > 0 {
                        dialog.push(' ');
                    }
                    dialog.push_str(line);
                }
            }
        } else {
            if is_scene_change(line) {
                script_parts.push(ScriptPart::SceneChange);
            } else if is_page_number.is_match(line) {
                // TODO: parse actual page number
                script_parts.push(ScriptPart::PageNumber(0));
            }
        }

        last_top_position = attributes.top;
    }

    script_parts
}

#[derive(Default, Clone, Debug)]
struct Location {
	internal: bool,
	name: String,
	parts: Vec<ScenePart>,
}

type Scene = Vec<Location>;

fn extract_scenes(script_parts: &Vec<ScriptPart>) -> Vec<Scene> {
	let mut scenes = Vec::new();
	let mut current_page_numer = 0;

    // scene with default (empty) location
	let default_scene: Scene = vec![Default::default()];
	scenes.push(default_scene.clone());

	for script_part in script_parts.iter() {
		use ScriptPart::*;
		match script_part {
			&PageNumber(page_number) => current_page_numer = page_number,
			&SceneChange => {
				// unwrap is save, see default_scene
				if scenes.last().unwrap().len() > 0 {
					scenes.push(default_scene.clone());
				}
			}
			&LocationChange(ref location) => {
				// unwraps are safe, see default_scene
				let mut current_scene = scenes.last_mut().unwrap();

				if current_scene.last().unwrap().parts.len() == 0 {
					current_scene.pop();
				}
                // TODO: Parse INT./EXT.
				current_scene.push(Location {name: location.clone(), ..Default::default()})
			}
            &ScenePart(ref scene_part) => {
                let scene_parts = &mut scenes.last_mut().unwrap().last_mut().unwrap().parts;
                scene_parts.push(scene_part.clone());
            }
			&Separator => {} //ignore
		}
	}

	scenes
}

#[allow(dead_code)]
fn condense_script_demo(properties: ScriptProperties, lines: &Vec<(LineAttributes, String)>) {
    let mut last_left_position = 0;
    let mut last_top_position = 0;

    for line in lines.iter() {
        let &(ref attributes, ref line) = line;

        if line.len() == 0 {
            continue;
        }

        // start new section
        if last_left_position != attributes.left ||
           attributes.top - last_top_position > 18 ||
           attributes.top - last_top_position < 0 {
            if ! (last_left_position == properties.speaker_direction_position ||
                  last_left_position == properties.speaker_position) {
                // empty line after last section
                println!("");
            }
        }

        match attributes.left {
            k if (k == properties.direction_position)
                => println!("{}", line),
            k if (k == properties.dialog_position)
                => println!("          {}", line),
            k if (k == properties.speaker_direction_position)
                => println!("               {}", line),
            k if (k == properties.speaker_position)
                => println!("                    {}", line),
            _   => println!("------- {}", line),
        }

        last_left_position = attributes.left;
        last_top_position = attributes.top;
    }
}

fn main() {
    let xml_file_name = env::args().skip(1).next().expect("No input file given");

    let file_reader = File::open(xml_file_name).expect("Could not open file");
    let buffered_file_reader = Box::new(BufReader::new(file_reader));

    let (properties, lines) = read_and_analyze_script(buffered_file_reader);

    //condense_script_demo(properties, &lines);
    println!("{:#?}", extract_scenes(&extract_script_parts(properties, &lines)));
}
<|endoftext|>


<commit_before>/*
 * Copyright 2017 Sreejith Krishnan R
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

use std::any::Any;
use std::f32;

use super::super::{Layout, ConcreteLayout, PresentationLayout};
use ::platform::Context;
use ::draw::{Drawable, AbsoluteLayout, AbsoluteLayoutParams, Wrapper, MeasureMode, Symbol, Line, LineParam};
use ::props::{Directionality, MathVariant};
use ::paint::Point;

pub struct MrootLayout {
    pub(crate) child: Box<Layout>,
    pub(crate) degree: Box<Layout>,

    pub(crate) dir: Directionality,
    pub(crate) base_size: f32,

    pub(crate) presentation_element: PresentationLayout,
}

impl Layout for MrootLayout {
    fn layout<'a>(&'a self, context: &Context) -> Box<Drawable + 'a> {
        Box::new(ConcreteLayout::layout(self, context))
    }

    fn as_any(&self) -> &Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut Any {
        self
    }
}

impl<'a> ConcreteLayout<'a, Wrapper<'a, PresentationLayout, AbsoluteLayout<'a>>> for MrootLayout {
    fn layout(&'a self, context: &Context) -> Wrapper<'a, PresentationLayout, AbsoluteLayout<'a>> {
        let mut ll = AbsoluteLayout::new();
        ll.should_calculate_child_bounds(false);

        let ruler = context.platform().get_math_ruler(self.base_size);

        let vertical_gap = if self.presentation_element.display_style {
            ruler.radical_display_style_vertical_gap()
        } else {
            ruler.radical_vertical_gap()
        };

        let degree_bottom_raise_pc = ruler.radical_degree_bottom_raise_percent();
        let extra_ascender = ruler.radical_extra_ascender();
        let rule_thickness = ruler.radical_rule_thickness();
        let kern_before_degree = ruler.radical_kern_before_degree();
        let kern_after_degree = ruler.radical_kern_after_degree();

        let degree_layout = self.degree.layout(context);
        let child_layout = self.child.layout(context);

        let root_height = child_layout.bounding_box().height() + vertical_gap;

        let mut surd = Symbol::new(
            self,
            |_| "√",
            |_| &MathVariant::Normal,
            |_| false,
            |e| e.base_size,
            |_| f32::INFINITY,
            |e| e.base_size,
            |e| &e.dir,
            |e| &e.presentation_element.math_color
        );
        surd.calculate(context, &MeasureMode::Wrap, &MeasureMode::UpTo(root_height));

        let root_height = surd.bounding_box().height().max(root_height);

        let degree_pos_x = kern_before_degree;
        let degree_pos_y = ((root_height*degree_bottom_raise_pc)-degree_layout.bounding_box().height())
            .max(vertical_gap);

        let surd_pos_x = degree_pos_x + degree_layout.bounding_box().width() + kern_after_degree;
        let surd_pos_y = degree_pos_y +
            degree_layout.bounding_box().height() +
            (root_height * degree_bottom_raise_pc) -
            root_height;

        let rule_pos_x = surd_pos_x + surd.bounding_box().width();
        let rule_pos_y = surd_pos_y;

        let child_pos_x = rule_pos_x;
        let child_pos_y = rule_pos_y + rule_thickness + extra_ascender;

        let mut rule_line = Line::new(
            LineParam::Horizontal { y: rule_thickness/2.0 },
            self,
            rule_thickness,
            |e| &e.presentation_element.math_color
        );

        rule_line.calculate(
            context,
            &MeasureMode::UpTo(child_layout.bounding_box().width()),
            &MeasureMode::Wrap
        );

        let height = surd_pos_y+surd.bounding_box().height();
        ll.set_baseline(Some(height-(child_pos_y+child_layout.bounding_box().baseline_pos())));
        ll.set_axis(Some(height-(child_pos_y+child_layout.bounding_box().axis_pos())));

        ll.add_child(degree_layout, AbsoluteLayoutParams::new(Point::new(degree_pos_x, degree_pos_y)));
        ll.add_child(Box::new(surd), AbsoluteLayoutParams::new(Point::new(surd_pos_x, surd_pos_y)));
        ll.add_child(Box::new(rule_line), AbsoluteLayoutParams::new(Point::new(rule_pos_x, rule_pos_y)));
        ll.add_child(child_layout, AbsoluteLayoutParams::new(Point::new(child_pos_x, child_pos_y)));

        let mut wrapper = self.presentation_element.layout(context);
        wrapper.wrap(ll);
        wrapper.calculate(context, &MeasureMode::Wrap, &MeasureMode::Wrap);

        wrapper
    }
}

impl MrootLayout {
}<commit_msg>Fix trimming of surd when root without degree is used<commit_after>/*
 * Copyright 2017 Sreejith Krishnan R
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

use std::any::Any;
use std::f32;

use super::super::{Layout, ConcreteLayout, PresentationLayout};
use ::platform::Context;
use ::draw::{Drawable, AbsoluteLayout, AbsoluteLayoutParams, Wrapper, MeasureMode, Symbol, Line, LineParam};
use ::props::{Directionality, MathVariant};
use ::paint::Point;

pub struct MrootLayout {
    pub(crate) child: Box<Layout>,
    pub(crate) degree: Box<Layout>,

    pub(crate) dir: Directionality,
    pub(crate) base_size: f32,

    pub(crate) presentation_element: PresentationLayout,
}

impl Layout for MrootLayout {
    fn layout<'a>(&'a self, context: &Context) -> Box<Drawable + 'a> {
        Box::new(ConcreteLayout::layout(self, context))
    }

    fn as_any(&self) -> &Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut Any {
        self
    }
}

impl<'a> ConcreteLayout<'a, Wrapper<'a, PresentationLayout, AbsoluteLayout<'a>>> for MrootLayout {
    fn layout(&'a self, context: &Context) -> Wrapper<'a, PresentationLayout, AbsoluteLayout<'a>> {
        let mut ll = AbsoluteLayout::new();
        ll.should_calculate_child_bounds(false);

        let ruler = context.platform().get_math_ruler(self.base_size);

        let vertical_gap = if self.presentation_element.display_style {
            ruler.radical_display_style_vertical_gap()
        } else {
            ruler.radical_vertical_gap()
        };

        let degree_bottom_raise_pc = ruler.radical_degree_bottom_raise_percent();
        let extra_ascender = ruler.radical_extra_ascender();
        let rule_thickness = ruler.radical_rule_thickness();
        let kern_before_degree = ruler.radical_kern_before_degree();
        let kern_after_degree = ruler.radical_kern_after_degree();

        let degree_layout = self.degree.layout(context);
        let child_layout = self.child.layout(context);

        let root_height = child_layout.bounding_box().height() + vertical_gap;

        let mut surd = Symbol::new(
            self,
            |_| "√",
            |_| &MathVariant::Normal,
            |_| false,
            |e| e.base_size,
            |_| f32::INFINITY,
            |e| e.base_size,
            |e| &e.dir,
            |e| &e.presentation_element.math_color
        );
        surd.calculate(context, &MeasureMode::Wrap, &MeasureMode::UpTo(root_height));

        let root_height = surd.bounding_box().height().max(root_height);

        let degree_pos_x = kern_before_degree;
        let degree_pos_y = ((root_height*degree_bottom_raise_pc)-degree_layout.bounding_box().height())
            .max(vertical_gap);

        let surd_pos_x = (degree_pos_x + degree_layout.bounding_box().width() + kern_after_degree).max(0f32);
        let surd_pos_y = degree_pos_y +
            degree_layout.bounding_box().height() +
            (root_height * degree_bottom_raise_pc) -
            root_height;

        let rule_pos_x = surd_pos_x + surd.bounding_box().width();
        let rule_pos_y = surd_pos_y;

        let child_pos_x = rule_pos_x;
        let child_pos_y = rule_pos_y + rule_thickness + extra_ascender;

        let mut rule_line = Line::new(
            LineParam::Horizontal { y: rule_thickness/2.0 },
            self,
            rule_thickness,
            |e| &e.presentation_element.math_color
        );

        rule_line.calculate(
            context,
            &MeasureMode::UpTo(child_layout.bounding_box().width()),
            &MeasureMode::Wrap
        );

        let height = surd_pos_y+surd.bounding_box().height();
        ll.set_baseline(Some(height-(child_pos_y+child_layout.bounding_box().baseline_pos())));
        ll.set_axis(Some(height-(child_pos_y+child_layout.bounding_box().axis_pos())));

        ll.add_child(degree_layout, AbsoluteLayoutParams::new(Point::new(degree_pos_x, degree_pos_y)));
        ll.add_child(Box::new(surd), AbsoluteLayoutParams::new(Point::new(surd_pos_x, surd_pos_y)));
        ll.add_child(Box::new(rule_line), AbsoluteLayoutParams::new(Point::new(rule_pos_x, rule_pos_y)));
        ll.add_child(child_layout, AbsoluteLayoutParams::new(Point::new(child_pos_x, child_pos_y)));

        let mut wrapper = self.presentation_element.layout(context);
        wrapper.wrap(ll);
        wrapper.calculate(context, &MeasureMode::Wrap, &MeasureMode::Wrap);

        wrapper
    }
}

impl MrootLayout {
}<|endoftext|>


<commit_before>use if_chain::if_chain;
use rustc::hir::{Expr, ExprKind};
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};
use syntax_pos::Span;

use crate::consts::{constant, Constant};
use crate::utils::{in_macro_or_desugar, is_direct_expn_of, span_help_and_lint};

declare_clippy_lint! {
    /// **What it does:** Checks for `assert!(true)` and `assert!(false)` calls.
    ///
    /// **Why is this bad?** Will be optimized out by the compiler or should probably be replaced by a
    /// panic!() or unreachable!()
    ///
    /// **Known problems:** None
    ///
    /// **Example:**
    /// ```rust,ignore
    /// assert!(false)
    /// // or
    /// assert!(true)
    /// // or
    /// const B: bool = false;
    /// assert!(B)
    /// ```
    pub ASSERTIONS_ON_CONSTANTS,
    style,
    "`assert!(true)` / `assert!(false)` will be optimized out by the compiler, and should probably be replaced by a `panic!()` or `unreachable!()`"
}

declare_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]);

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssertionsOnConstants {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
        let mut is_debug_assert = false;
        let debug_assert_not_in_macro_or_desugar = |span: Span| {
            is_debug_assert = true;
            // Check that `debug_assert!` itself is not inside a macro
            !in_macro_or_desugar(span)
        };
        if_chain! {
            if let Some(assert_span) = is_direct_expn_of(e.span, "assert");
            if !in_macro_or_desugar(assert_span)
                || is_direct_expn_of(assert_span, "debug_assert")
                    .map_or(false, debug_assert_not_in_macro_or_desugar);
            if let ExprKind::Unary(_, ref lit) = e.node;
            if let Some(bool_const) = constant(cx, cx.tables, lit);
            then {
                match bool_const.0 {
                    Constant::Bool(true) => {
                        span_help_and_lint(
                            cx,
                            ASSERTIONS_ON_CONSTANTS,
                            e.span,
                            "`assert!(true)` will be optimized out by the compiler",
                            "remove it"
                        );
                    },
                    Constant::Bool(false) if !is_debug_assert => {
                        span_help_and_lint(
                            cx,
                            ASSERTIONS_ON_CONSTANTS,
                            e.span,
                            "`assert!(false)` should probably be replaced",
                            "use `panic!()` or `unreachable!()`"
                        );
                    },
                    _ => (),
                }
            }
        }
    }
}
<commit_msg>Make code cleaner and more readable<commit_after>use if_chain::if_chain;
use rustc::hir::{Expr, ExprKind};
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};
use syntax_pos::Span;

use crate::consts::{constant, Constant};
use crate::utils::{in_macro_or_desugar, is_direct_expn_of, span_help_and_lint};

declare_clippy_lint! {
    /// **What it does:** Checks for `assert!(true)` and `assert!(false)` calls.
    ///
    /// **Why is this bad?** Will be optimized out by the compiler or should probably be replaced by a
    /// panic!() or unreachable!()
    ///
    /// **Known problems:** None
    ///
    /// **Example:**
    /// ```rust,ignore
    /// assert!(false)
    /// // or
    /// assert!(true)
    /// // or
    /// const B: bool = false;
    /// assert!(B)
    /// ```
    pub ASSERTIONS_ON_CONSTANTS,
    style,
    "`assert!(true)` / `assert!(false)` will be optimized out by the compiler, and should probably be replaced by a `panic!()` or `unreachable!()`"
}

declare_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]);

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for AssertionsOnConstants {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
        let mut is_debug_assert = false;
        if let Some(assert_span) = is_direct_expn_of(e.span, "assert") {
            if in_macro_or_desugar(assert_span) {
                return;
            }
            if let Some(debug_assert_span) = is_direct_expn_of(assert_span, "debug_assert") {
                if in_macro_or_desugar(debug_assert_span) {
                    return;
                }
                is_debug_assert = true;
            }
            if let ExprKind::Unary(_, ref lit) = e.node {
                if let Some((bool_const, _)) = constant(cx, cx.tables, lit) {
                    if let Constant::Bool(is_true) bool_const {
                        if is_true {
                            span_help_and_lint(
                                cx,
                                ASSERTIONS_ON_CONSTANTS,
                                e.span,
                                "`assert!(true)` will be optimized out by the compiler",
                                "remove it"
                            );
                        } else if !is_debug_assert {
                            span_help_and_lint(
                                cx,
                                ASSERTIONS_ON_CONSTANTS,
                                e.span,
                                "`assert!(false)` should probably be replaced",
                                "use `panic!()` or `unreachable!()`"
                            );
                        }
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use super::Pass;
use crate::clean::*;
use crate::core::DocContext;
use crate::fold::DocFolder;
use crate::html::markdown::main_body_opts;
use core::ops::Range;
use pulldown_cmark::{Event, Parser, Tag};
use std::iter::Peekable;
use std::str::CharIndices;

crate const CHECK_INVALID_HTML_TAGS: Pass = Pass {
    name: "check-invalid-html-tags",
    run: check_invalid_html_tags,
    description: "detects invalid HTML tags in doc comments",
};

struct InvalidHtmlTagsLinter<'a, 'tcx> {
    cx: &'a mut DocContext<'tcx>,
}

crate fn check_invalid_html_tags(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
    if !cx.tcx.sess.is_nightly_build() {
        krate
    } else {
        let mut coll = InvalidHtmlTagsLinter { cx };

        coll.fold_crate(krate)
    }
}

const ALLOWED_UNCLOSED: &[&str] = &[
    "area", "base", "br", "col", "embed", "hr", "img", "input", "keygen", "link", "meta", "param",
    "source", "track", "wbr",
];

fn drop_tag(
    tags: &mut Vec<(String, Range<usize>)>,
    tag_name: String,
    range: Range<usize>,
    f: &impl Fn(&str, &Range<usize>),
) {
    let tag_name_low = tag_name.to_lowercase();
    if let Some(pos) = tags.iter().rposition(|(t, _)| t.to_lowercase() == tag_name_low) {
        // If the tag is nested inside a "
` (the `h2` tag isn't required
        // but it helps for the visualization).
        f(&format!("unopened HTML tag `{}`", tag_name), &range);
    }
}

fn extract_html_tag(
    tags: &mut Vec<(String, Range<usize>)>,
    text: &str,
    range: &Range<usize>,
    start_pos: usize,
    iter: &mut Peekable<CharIndices<'_>>,
    f: &impl Fn(&str, &Range<usize>),
) {
    let mut tag_name = String::new();
    let mut is_closing = false;
    let mut prev_pos = start_pos;

    loop {
        let (pos, c) = match iter.peek() {
            Some((pos, c)) => (*pos, *c),
            // In case we reached the of the doc comment, we want to check that it's an
            // unclosed HTML tag. For example "///
 (prev_pos, '\0'),
        };
        prev_pos = pos;
        // Checking if this is a closing tag (like `</a>` for `<a>`).
        if c == '/' && tag_name.is_empty() {
            is_closing = true;
        } else if c.is_ascii_alphanumeric() {
            tag_name.push(c);
        } else {
            if !tag_name.is_empty() {
                let mut r = Range { start: range.start + start_pos, end: range.start + pos };
                if c == '>' {
                    // In case we have a tag without attribute, we can consider the span to
                    // refer to it fully.
                    r.end += 1;
                }
                if is_closing {
                    // In case we have "
" or even "
".
                    if c != '>' {
                        if !c.is_whitespace() {
                            // It seems like it's not a valid HTML tag.
                            break;
                        }
                        let mut found = false;
                        for (new_pos, c) in text[pos..].char_indices() {
                            if !c.is_whitespace() {
                                if c == '>' {
                                    r.end = range.start + new_pos + 1;
                                    found = true;
                                }
                                break;
                            }
                        }
                        if !found {
                            break;
                        }
                    }
                    drop_tag(tags, tag_name, r, f);
                } else {
                    tags.push((tag_name, r));
                }
            }
            break;
        }
        iter.next();
    }
}

fn extract_tags(
    tags: &mut Vec<(String, Range<usize>)>,
    text: &str,
    range: Range<usize>,
    is_in_comment: &mut Option<Range<usize>>,
    f: &impl Fn(&str, &Range<usize>),
) {
    let mut iter = text.char_indices().peekable();

    while let Some((start_pos, c)) = iter.next() {
        if is_in_comment.is_some() {
            if text[start_pos..].starts_with("-->") {
                *is_in_comment = None;
            }
        } else if c == '<' {
            if text[start_pos..].starts_with("") {
                *is_in_comment = None;
            }
        } else if c == '<' {
            if text[start_pos..].starts_with("


<commit_before>// Copyright 2012-2013 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 ast;
use ast::{MetaItem, Item, Expr};
use codemap::Span;
use ext::base::ExtCtxt;
use ext::build::{AstBuilder};
use ext::deriving::generic::*;
use ext::deriving::generic::ty::*;
use ptr::P;

pub fn expand_deriving_rand<F>(cx: &mut ExtCtxt,
                               span: Span,
                               mitem: &MetaItem,
                               item: &Item,
                               push: F) where
    F: FnOnce(P<Item>),
{
    let trait_def = TraitDef {
        span: span,
        attributes: Vec::new(),
        path: Path::new(vec!("std", "rand", "Rand")),
        additional_bounds: Vec::new(),
        generics: LifetimeBounds::empty(),
        methods: vec!(
            MethodDef {
                name: "rand",
                generics: LifetimeBounds {
                    lifetimes: Vec::new(),
                    bounds: vec!(("R",
                                  vec!( Path::new(vec!("std", "rand", "Rng")) )))
                },
                explicit_self: None,
                args: vec!(
                    Ptr(box Literal(Path::new_local("R")),
                        Borrowed(None, ast::MutMutable))
                ),
                ret_ty: Self,
                attributes: Vec::new(),
                combine_substructure: combine_substructure(box |a, b, c| {
                    rand_substructure(a, b, c)
                })
            }
        ),
        associated_types: Vec::new(),
    };
    trait_def.expand(cx, mitem, item, push)
}

fn rand_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> {
    let rng = match substr.nonself_args {
        [ref rng] => rng,
        _ => cx.bug("Incorrect number of arguments to `rand` in `derive(Rand)`")
    };
    let rand_ident = vec!(
        cx.ident_of("std"),
        cx.ident_of("rand"),
        cx.ident_of("Rand"),
        cx.ident_of("rand")
    );
    let mut rand_call = |&mut: cx: &mut ExtCtxt, span| {
        cx.expr_call_global(span,
                            rand_ident.clone(),
                            vec!(rng.clone()))
    };

    return match *substr.fields {
        StaticStruct(_, ref summary) => {
            let path = cx.path_ident(trait_span, substr.type_ident);
            rand_thing(cx, trait_span, path, summary, rand_call)
        }
        StaticEnum(_, ref variants) => {
            if variants.is_empty() {
                cx.span_err(trait_span, "`Rand` cannot be derived for enums with no variants");
                // let compilation continue
                return cx.expr_usize(trait_span, 0);
            }

            let variant_count = cx.expr_usize(trait_span, variants.len());

            let rand_name = cx.path_all(trait_span,
                                        true,
                                        rand_ident.clone(),
                                        Vec::new(),
                                        Vec::new(),
                                        Vec::new());
            let rand_name = cx.expr_path(rand_name);

            // ::rand::Rand::rand(rng)
            let rv_call = cx.expr_call(trait_span,
                                       rand_name,
                                       vec!(rng.clone()));

            // need to specify the usize-ness of the random number
            let usize_ty = cx.ty_ident(trait_span, cx.ident_of("usize"));
            let value_ident = cx.ident_of("__value");
            let let_statement = cx.stmt_let_typed(trait_span,
                                                  false,
                                                  value_ident,
                                                  usize_ty,
                                                  rv_call);

            // rand() % variants.len()
            let value_ref = cx.expr_ident(trait_span, value_ident);
            let rand_variant = cx.expr_binary(trait_span,
                                              ast::BiRem,
                                              value_ref,
                                              variant_count);

            let mut arms = variants.iter().enumerate().map(|(i, &(ident, v_span, ref summary))| {
                let i_expr = cx.expr_usize(v_span, i);
                let pat = cx.pat_lit(v_span, i_expr);

                let path = cx.path(v_span, vec![substr.type_ident, ident]);
                let thing = rand_thing(cx, v_span, path, summary, |cx, sp| rand_call(cx, sp));
                cx.arm(v_span, vec!( pat ), thing)
            }).collect::<Vec<ast::Arm> >();

            // _ => {} at the end. Should never occur
            arms.push(cx.arm_unreachable(trait_span));

            let match_expr = cx.expr_match(trait_span, rand_variant, arms);

            let block = cx.block(trait_span, vec!( let_statement ), Some(match_expr));
            cx.expr_block(block)
        }
        _ => cx.bug("Non-static method in `derive(Rand)`")
    };

    fn rand_thing<F>(cx: &mut ExtCtxt,
                     trait_span: Span,
                     ctor_path: ast::Path,
                     summary: &StaticFields,
                     mut rand_call: F)
                     -> P<Expr> where
        F: FnMut(&mut ExtCtxt, Span) -> P<Expr>,
    {
        let path = cx.expr_path(ctor_path.clone());
        match *summary {
            Unnamed(ref fields) => {
                if fields.is_empty() {
                    path
                } else {
                    let exprs = fields.iter().map(|span| rand_call(cx, *span)).collect();
                    cx.expr_call(trait_span, path, exprs)
                }
            }
            Named(ref fields) => {
                let rand_fields = fields.iter().map(|&(ident, span)| {
                    let e = rand_call(cx, span);
                    cx.field_imm(span, ident, e)
                }).collect();
                cx.expr_struct(trait_span, ctor_path, rand_fields)
            }
        }
    }
}
<commit_msg>Correct one case where the inference was detecting a looser result than the explicit annotation, leading to "extra `mut` declaration" lint errors.<commit_after>// Copyright 2012-2013 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 ast;
use ast::{MetaItem, Item, Expr};
use codemap::Span;
use ext::base::ExtCtxt;
use ext::build::{AstBuilder};
use ext::deriving::generic::*;
use ext::deriving::generic::ty::*;
use ptr::P;

pub fn expand_deriving_rand<F>(cx: &mut ExtCtxt,
                               span: Span,
                               mitem: &MetaItem,
                               item: &Item,
                               push: F) where
    F: FnOnce(P<Item>),
{
    let trait_def = TraitDef {
        span: span,
        attributes: Vec::new(),
        path: Path::new(vec!("std", "rand", "Rand")),
        additional_bounds: Vec::new(),
        generics: LifetimeBounds::empty(),
        methods: vec!(
            MethodDef {
                name: "rand",
                generics: LifetimeBounds {
                    lifetimes: Vec::new(),
                    bounds: vec!(("R",
                                  vec!( Path::new(vec!("std", "rand", "Rng")) )))
                },
                explicit_self: None,
                args: vec!(
                    Ptr(box Literal(Path::new_local("R")),
                        Borrowed(None, ast::MutMutable))
                ),
                ret_ty: Self,
                attributes: Vec::new(),
                combine_substructure: combine_substructure(box |a, b, c| {
                    rand_substructure(a, b, c)
                })
            }
        ),
        associated_types: Vec::new(),
    };
    trait_def.expand(cx, mitem, item, push)
}

fn rand_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> {
    let rng = match substr.nonself_args {
        [ref rng] => rng,
        _ => cx.bug("Incorrect number of arguments to `rand` in `derive(Rand)`")
    };
    let rand_ident = vec!(
        cx.ident_of("std"),
        cx.ident_of("rand"),
        cx.ident_of("Rand"),
        cx.ident_of("rand")
    );
    let rand_call = |&: cx: &mut ExtCtxt, span| {
        cx.expr_call_global(span,
                            rand_ident.clone(),
                            vec!(rng.clone()))
    };

    return match *substr.fields {
        StaticStruct(_, ref summary) => {
            let path = cx.path_ident(trait_span, substr.type_ident);
            rand_thing(cx, trait_span, path, summary, rand_call)
        }
        StaticEnum(_, ref variants) => {
            if variants.is_empty() {
                cx.span_err(trait_span, "`Rand` cannot be derived for enums with no variants");
                // let compilation continue
                return cx.expr_usize(trait_span, 0);
            }

            let variant_count = cx.expr_usize(trait_span, variants.len());

            let rand_name = cx.path_all(trait_span,
                                        true,
                                        rand_ident.clone(),
                                        Vec::new(),
                                        Vec::new(),
                                        Vec::new());
            let rand_name = cx.expr_path(rand_name);

            // ::rand::Rand::rand(rng)
            let rv_call = cx.expr_call(trait_span,
                                       rand_name,
                                       vec!(rng.clone()));

            // need to specify the usize-ness of the random number
            let usize_ty = cx.ty_ident(trait_span, cx.ident_of("usize"));
            let value_ident = cx.ident_of("__value");
            let let_statement = cx.stmt_let_typed(trait_span,
                                                  false,
                                                  value_ident,
                                                  usize_ty,
                                                  rv_call);

            // rand() % variants.len()
            let value_ref = cx.expr_ident(trait_span, value_ident);
            let rand_variant = cx.expr_binary(trait_span,
                                              ast::BiRem,
                                              value_ref,
                                              variant_count);

            let mut arms = variants.iter().enumerate().map(|(i, &(ident, v_span, ref summary))| {
                let i_expr = cx.expr_usize(v_span, i);
                let pat = cx.pat_lit(v_span, i_expr);

                let path = cx.path(v_span, vec![substr.type_ident, ident]);
                let thing = rand_thing(cx, v_span, path, summary, |cx, sp| rand_call(cx, sp));
                cx.arm(v_span, vec!( pat ), thing)
            }).collect::<Vec<ast::Arm> >();

            // _ => {} at the end. Should never occur
            arms.push(cx.arm_unreachable(trait_span));

            let match_expr = cx.expr_match(trait_span, rand_variant, arms);

            let block = cx.block(trait_span, vec!( let_statement ), Some(match_expr));
            cx.expr_block(block)
        }
        _ => cx.bug("Non-static method in `derive(Rand)`")
    };

    fn rand_thing<F>(cx: &mut ExtCtxt,
                     trait_span: Span,
                     ctor_path: ast::Path,
                     summary: &StaticFields,
                     mut rand_call: F)
                     -> P<Expr> where
        F: FnMut(&mut ExtCtxt, Span) -> P<Expr>,
    {
        let path = cx.expr_path(ctor_path.clone());
        match *summary {
            Unnamed(ref fields) => {
                if fields.is_empty() {
                    path
                } else {
                    let exprs = fields.iter().map(|span| rand_call(cx, *span)).collect();
                    cx.expr_call(trait_span, path, exprs)
                }
            }
            Named(ref fields) => {
                let rand_fields = fields.iter().map(|&(ident, span)| {
                    let e = rand_call(cx, span);
                    cx.field_imm(span, ident, e)
                }).collect();
                cx.expr_struct(trait_span, ctor_path, rand_fields)
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::sync::atomic::{compiler_fence, fence, AtomicBool, AtomicIsize, AtomicU64, Ordering::*};

fn main() {
    atomic_bool();
    atomic_isize();
    atomic_u64();
    atomic_fences();
    weak_sometimes_fails();
}

fn atomic_bool() {
    static mut ATOMIC: AtomicBool = AtomicBool::new(false);

    unsafe {
        assert_eq!(*ATOMIC.get_mut(), false);
        ATOMIC.store(true, SeqCst);
        assert_eq!(*ATOMIC.get_mut(), true);
        ATOMIC.fetch_or(false, SeqCst);
        assert_eq!(*ATOMIC.get_mut(), true);
        ATOMIC.fetch_and(false, SeqCst);
        assert_eq!(*ATOMIC.get_mut(), false);
        ATOMIC.fetch_nand(true, SeqCst);
        assert_eq!(*ATOMIC.get_mut(), true);
        ATOMIC.fetch_xor(true, SeqCst);
        assert_eq!(*ATOMIC.get_mut(), false);
    }
}
// There isn't a trait to use to make this generic, so just use a macro
macro_rules! compare_exchange_weak_loop {
    ($atom:expr, $from:expr, $to:expr, $succ_order:expr, $fail_order:expr) => {
        loop {
            match $atom.compare_exchange_weak($from, $to, $succ_order, $fail_order) {
                Ok(n) => {
                    assert_eq!(n, $from);
                    break;
                }
                Err(n) => assert_eq!(n, $from),
            }
        }
    };
}
fn atomic_isize() {
    static ATOMIC: AtomicIsize = AtomicIsize::new(0);

    // Make sure trans can emit all the intrinsics correctly
    assert_eq!(ATOMIC.compare_exchange(0, 1, Relaxed, Relaxed), Ok(0));
    assert_eq!(ATOMIC.compare_exchange(0, 2, Acquire, Relaxed), Err(1));
    assert_eq!(ATOMIC.compare_exchange(0, 1, Release, Relaxed), Err(1));
    assert_eq!(ATOMIC.compare_exchange(1, 0, AcqRel, Relaxed), Ok(1));
    ATOMIC.compare_exchange(0, 1, SeqCst, Relaxed).ok();
    ATOMIC.compare_exchange(0, 1, Acquire, Acquire).ok();
    ATOMIC.compare_exchange(0, 1, AcqRel, Acquire).ok();
    ATOMIC.compare_exchange(0, 1, SeqCst, Acquire).ok();
    ATOMIC.compare_exchange(0, 1, SeqCst, SeqCst).ok();

    ATOMIC.store(0, SeqCst);
    compare_exchange_weak_loop!(ATOMIC, 0, 1, Relaxed, Relaxed);
    assert_eq!(ATOMIC.compare_exchange_weak(0, 2, Acquire, Relaxed), Err(1));
    assert_eq!(ATOMIC.compare_exchange_weak(0, 1, Release, Relaxed), Err(1));
    compare_exchange_weak_loop!(ATOMIC, 1, 0, AcqRel, Relaxed);
    assert_eq!(ATOMIC.load(Relaxed), 0);
    ATOMIC.compare_exchange_weak(0, 1, AcqRel, Relaxed).ok();
    ATOMIC.compare_exchange_weak(0, 1, SeqCst, Relaxed).ok();
    ATOMIC.compare_exchange_weak(0, 1, Acquire, Acquire).ok();
    ATOMIC.compare_exchange_weak(0, 1, AcqRel, Acquire).ok();
    ATOMIC.compare_exchange_weak(0, 1, SeqCst, Acquire).ok();
    ATOMIC.compare_exchange_weak(0, 1, SeqCst, SeqCst).ok();
}

fn atomic_u64() {
    static ATOMIC: AtomicU64 = AtomicU64::new(0);

    ATOMIC.store(1, SeqCst);
    assert_eq!(ATOMIC.compare_exchange(0, 0x100, AcqRel, Acquire), Err(1));
    compare_exchange_weak_loop!(ATOMIC, 1, 0x100, AcqRel, Acquire);
    assert_eq!(ATOMIC.load(Relaxed), 0x100);
}

fn atomic_fences() {
    fence(SeqCst);
    fence(Release);
    fence(Acquire);
    fence(AcqRel);
    compiler_fence(SeqCst);
    compiler_fence(Release);
    compiler_fence(Acquire);
    compiler_fence(AcqRel);
}

fn weak_sometimes_fails() {
    static ATOMIC: AtomicBool = AtomicBool::new(false);
    let tries = 20;
    for _ in 0..tries {
        let cur = ATOMIC.load(Relaxed);
        // Try (weakly) to flip the flag.
        if ATOMIC.compare_exchange_weak(cur, !cur, Relaxed, Relaxed).is_err() {
            // We succeeded, so return and skip the panic.
            return;
        }
    }
    panic!("compare_exchange_weak succeeded {} tries in a row", tries);
}
<commit_msg>review nits<commit_after>use std::sync::atomic::{compiler_fence, fence, AtomicBool, AtomicIsize, AtomicU64, Ordering::*};

fn main() {
    atomic_bool();
    atomic_isize();
    atomic_u64();
    atomic_fences();
    weak_sometimes_fails();
}

fn atomic_bool() {
    static mut ATOMIC: AtomicBool = AtomicBool::new(false);

    unsafe {
        assert_eq!(*ATOMIC.get_mut(), false);
        ATOMIC.store(true, SeqCst);
        assert_eq!(*ATOMIC.get_mut(), true);
        ATOMIC.fetch_or(false, SeqCst);
        assert_eq!(*ATOMIC.get_mut(), true);
        ATOMIC.fetch_and(false, SeqCst);
        assert_eq!(*ATOMIC.get_mut(), false);
        ATOMIC.fetch_nand(true, SeqCst);
        assert_eq!(*ATOMIC.get_mut(), true);
        ATOMIC.fetch_xor(true, SeqCst);
        assert_eq!(*ATOMIC.get_mut(), false);
    }
}
// There isn't a trait to use to make this generic, so just use a macro
macro_rules! compare_exchange_weak_loop {
    ($atom:expr, $from:expr, $to:expr, $succ_order:expr, $fail_order:expr) => {
        loop {
            match $atom.compare_exchange_weak($from, $to, $succ_order, $fail_order) {
                Ok(n) => {
                    assert_eq!(n, $from);
                    break;
                }
                Err(n) => assert_eq!(n, $from),
            }
        }
    };
}
fn atomic_isize() {
    static ATOMIC: AtomicIsize = AtomicIsize::new(0);

    // Make sure trans can emit all the intrinsics correctly
    assert_eq!(ATOMIC.compare_exchange(0, 1, Relaxed, Relaxed), Ok(0));
    assert_eq!(ATOMIC.compare_exchange(0, 2, Acquire, Relaxed), Err(1));
    assert_eq!(ATOMIC.compare_exchange(0, 1, Release, Relaxed), Err(1));
    assert_eq!(ATOMIC.compare_exchange(1, 0, AcqRel, Relaxed), Ok(1));
    ATOMIC.compare_exchange(0, 1, SeqCst, Relaxed).ok();
    ATOMIC.compare_exchange(0, 1, Acquire, Acquire).ok();
    ATOMIC.compare_exchange(0, 1, AcqRel, Acquire).ok();
    ATOMIC.compare_exchange(0, 1, SeqCst, Acquire).ok();
    ATOMIC.compare_exchange(0, 1, SeqCst, SeqCst).ok();

    ATOMIC.store(0, SeqCst);
    compare_exchange_weak_loop!(ATOMIC, 0, 1, Relaxed, Relaxed);
    assert_eq!(ATOMIC.compare_exchange_weak(0, 2, Acquire, Relaxed), Err(1));
    assert_eq!(ATOMIC.compare_exchange_weak(0, 1, Release, Relaxed), Err(1));
    compare_exchange_weak_loop!(ATOMIC, 1, 0, AcqRel, Relaxed);
    assert_eq!(ATOMIC.load(Relaxed), 0);
    ATOMIC.compare_exchange_weak(0, 1, AcqRel, Relaxed).ok();
    ATOMIC.compare_exchange_weak(0, 1, SeqCst, Relaxed).ok();
    ATOMIC.compare_exchange_weak(0, 1, Acquire, Acquire).ok();
    ATOMIC.compare_exchange_weak(0, 1, AcqRel, Acquire).ok();
    ATOMIC.compare_exchange_weak(0, 1, SeqCst, Acquire).ok();
    ATOMIC.compare_exchange_weak(0, 1, SeqCst, SeqCst).ok();
}

fn atomic_u64() {
    static ATOMIC: AtomicU64 = AtomicU64::new(0);

    ATOMIC.store(1, SeqCst);
    assert_eq!(ATOMIC.compare_exchange(0, 0x100, AcqRel, Acquire), Err(1));
    compare_exchange_weak_loop!(ATOMIC, 1, 0x100, AcqRel, Acquire);
    assert_eq!(ATOMIC.load(Relaxed), 0x100);
}

fn atomic_fences() {
    fence(SeqCst);
    fence(Release);
    fence(Acquire);
    fence(AcqRel);
    compiler_fence(SeqCst);
    compiler_fence(Release);
    compiler_fence(Acquire);
    compiler_fence(AcqRel);
}

fn weak_sometimes_fails() {
    let atomic = AtomicBool::new(false);
    let tries = 20;
    for _ in 0..tries {
        let cur = atomic.load(Relaxed);
        // Try (weakly) to flip the flag.
        if atomic.compare_exchange_weak(cur, !cur, Relaxed, Relaxed).is_err() {
            // We failed, so return and skip the panic.
            return;
        }
    }
    panic!("compare_exchange_weak succeeded {} tries in a row", tries);
}
<|endoftext|>