<commit_before>//! [From the man page](ftp://www.x.org/pub/X11R7.7/doc/man/man3/Xcursor.3.xhtml):
//! > Xcursor is a simple library designed to help locate and load cursors.
//! > Cursors can be loaded from files or memory. A library of common cursors
//! > exists which map to the standard X cursor names. Cursors can exist in
//! > several sizes and the library automatically picks the best size.

use std::{mem, ptr, slice};
use std::marker::PhantomData;
use std::time::Duration;

use libc::c_int;
use wlroots_sys::{wlr_xcursor, wlr_xcursor_frame, wlr_xcursor_image, wlr_xcursor_theme,
                  wlr_xcursor_theme_destroy, wlr_xcursor_theme_get_cursor, wlr_xcursor_theme_load};

use utils::{c_to_rust_string, safe_as_cstring};
#[cfg(feature = "unstable")]
pub use super::xcursor_manager::*;

/// Wrapper for an xcursor theme from the X11 xcursor library.
///
/// Xcursor (mostly) follows the freedesktop.org spec for theming icons.
#[derive(Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct Theme {
    theme: *mut wlr_xcursor_theme
}

/// Wrapper for an xcursor from the X11 xcursor library.
///
/// The cursor must live as long as the [`Theme`](struct.Theme.html)
/// that it comes from.
#[derive(Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct XCursor<'theme> {
    xcursor: *mut wlr_xcursor,
    phantom: PhantomData<&'theme Theme>
}

/// Contains the information necessary to render the cursor.
///
/// The hotspot of the image shows where the cursor will interact with the
/// environment. i.e. it refers to the location of the cursor "point".
/// Note that the coordinates could be inside the image.
#[derive(Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct Image<'cursor> {
    /// The width of the image in pixels.
    pub width: u32,
    /// The height of the image in pixels.
    pub height: u32,
    /// The x coordinate of the hotspot, which must be inside the image.
    pub hotspot_x: u32,
    /// The y coordinate of the hotspot, which must be inside the image.
    pub hotspot_y: u32,
    /// Animation delay to the next frame in milliseconds.
    pub delay: u32,
    /// The bytes in ARGB format.
    pub buffer: &'cursor [u8],
    /// A marker to indicate you can't send this type across threads.
    /// Also ensures you can't construct it outside of this library.
    #[doc(hidden)]
    pub(crate) _no_send: PhantomData<*mut wlr_xcursor_image>
}

impl Theme {
    /// Loads the named xcursor theme at the given cursor size (in pixels).
    ///
    /// This is useful if you need cursor images for your compositor to use when a
    /// client-side cursors is not available or you wish to override client-side
    /// cursors for a particular UI interaction (such as using a grab cursor when
    /// moving a window around)
    ///
    /// The default search path it uses is ~/.icons, /usr/share/icons, /usr/share/pixmaps.
    /// Within each of these directories, it searches for a directory using the theme name.
    /// Within the theme directory, it looks for cursor files in the 'cursors' subdirectory.
    /// It uses the first cursor file found along the path.
    ///
    /// If necessary, Xcursor also looks for a "index.theme" file in each theme directory
    /// to find inherited themes and searches along the path for those themes as well.
    ///
    /// If no name is given, defaults to "default".
    /// If no theme can be found `None` is returned.
    pub fn load_theme<T: Into<Option<String>>>(name: T, size: c_int) -> Option<Self> {
        unsafe {
            let name_str = name.into().map(safe_as_cstring);
            let name_ptr = name_str.map(|s| s.as_ptr()).unwrap_or(ptr::null_mut());
            let theme = wlr_xcursor_theme_load(name_ptr, size);
            if theme.is_null() {
                None
            } else {
                Some(Theme { theme })
            }
        }
    }

    /// Get the name of this theme.
    ///
    /// If the name returned by wlroots was malformed, or nonexistent,
    /// then the value will be None.
    pub fn name(&self) -> Option<String> {
        unsafe { c_to_rust_string((*self.theme).name) }
    }

    /// Get the size of the images.
    pub fn size(&self) -> c_int {
        unsafe { (*self.theme).size }
    }

    /// Get the number of cursors in this theme.
    pub fn cursor_count(&self) -> usize {
        unsafe { (*self.theme).cursor_count as usize }
    }

    /// Gets all the cursors from this theme.
    pub fn cursors<'theme>(&'theme mut self) -> Vec<XCursor<'theme>> {
        unsafe {
            let cursor_ptr = (*self.theme).cursors as *mut *mut wlr_xcursor;
            let length = self.cursor_count();
            let xcursors_slice: &'theme [*mut wlr_xcursor] =
                slice::from_raw_parts::<'theme, *mut wlr_xcursor>(cursor_ptr, length);
            xcursors_slice.into_iter()
                          .map(|&xcursor| XCursor::from_ptr(xcursor))
                          .collect()
        }
    }

    /// Get the cursor with the provided name (e.g. "left_ptr"), if it exists.
    pub fn get_cursor<'theme>(&'theme self, name: String) -> Option<XCursor<'theme>> {
        unsafe {
            let name_str = safe_as_cstring(name);
            let xcursor = wlr_xcursor_theme_get_cursor(self.theme, name_str.as_ptr());
            if xcursor.is_null() {
                None
            } else {
                Some(XCursor::from_ptr(xcursor))
            }
        }
    }

    /// Constructs a `Theme` from a raw pointer.
    ///
    /// # Unsafety
    /// Takes ownership of the cursor theme. When the `Theme` is dropped it
    /// calls `wlr_cursor_theme_destroy`.
    pub unsafe fn from_ptr(theme: *mut wlr_xcursor_theme) -> Theme {
        Theme { theme }
    }
}

impl Drop for Theme {
    fn drop(&mut self) {
        unsafe { wlr_xcursor_theme_destroy(self.theme) }
    }
}

impl<'theme> XCursor<'theme> {
    /// Get all the image frames associated with an xcursor.
    pub fn images<'cursor>(&'cursor self) -> Vec<Image<'cursor>> {
        unsafe {
            let cursors_slice = self.cursor_slice();
            let mut result = Vec::with_capacity(cursors_slice.len());
            for cursor in cursors_slice {
                result.push(Image::from_xcursor_image(*cursor))
            }
            result
        }
    }

    /// Get a specific frame of the image from the animation. Returns `None` if
    /// the index is out of bounds.
    ///
    /// We suggest paring this with `XCursor::frame` to avoid going out of bounds.
    pub fn image<'cursor>(&'cursor self, index: usize) -> Option<Image<'cursor>> {
        unsafe {
            let cursors_slice = self.cursor_slice();
            cursors_slice.get(index).map(|&cursor| Image::from_xcursor_image(cursor))
        }
    }

    unsafe fn cursor_slice(&self) -> &[*mut wlr_xcursor_image] {
        let image_ptr = (*self.xcursor).images as *mut *mut wlr_xcursor_image;
        slice::from_raw_parts::<'_, *mut wlr_xcursor_image>(image_ptr, self.image_len())
    }

    /// Returns the current frame number for an animated cursor give a
    /// monotonic time reference in milliseconds.
    ///
    /// e.g. if it's been animating for 500 milliseconds `duration`
    /// should be constructed from `Duration::from_millis(500)`.
    pub fn frame(&mut self, duration: Duration) -> usize {
        unsafe {
            wlr_xcursor_frame(self.xcursor, duration.subsec_millis()) as usize
        }
    }

    /// Get the number of animation frames for the cursor.
    pub fn image_len(&self) -> usize {
        unsafe { (*self.xcursor).image_count as _ }
    }

    /// Length of the animation in milliseconds.
    pub fn total_delay(&self) -> u32 {
        unsafe { (*self.xcursor).total_delay }
    }

    /// Constructs an `XCursor` from a raw pointer
    ///
    /// # Unsafety
    /// This lifetime is unbounded, but it must not outlive the
    /// xcursor manager that the pointer came from.
    pub unsafe fn from_ptr<'unbound>(xcursor: *mut wlr_xcursor) -> XCursor<'unbound> {
        XCursor { xcursor,
                  phantom: PhantomData }
    }
}

impl <'unbound> Image<'unbound> {
    unsafe fn from_xcursor_image(image: *mut wlr_xcursor_image) -> Self {
        Image {
            width: (*image).width,
            height: (*image).height,
            hotspot_x: (*image).hotspot_x,
            hotspot_y: (*image).hotspot_y,
            delay: (*image).delay,
            buffer: slice::from_raw_parts::<'_, u8>(
                (*image).buffer as *const u8,
                (*image).width as usize * (*image).height as usize
                    * mem::size_of::<u32>()
            ),
            _no_send: PhantomData
        }
    }
}
<commit_msg>Better xcursor module tagline<commit_after>//! Wrappers around the XCursor library used by wlroots.
//!
//! [From the man page](ftp://www.x.org/pub/X11R7.7/doc/man/man3/Xcursor.3.xhtml):
//! > Xcursor is a simple library designed to help locate and load cursors.
//! > Cursors can be loaded from files or memory. A library of common cursors
//! > exists which map to the standard X cursor names. Cursors can exist in
//! > several sizes and the library automatically picks the best size.

use std::{mem, ptr, slice};
use std::marker::PhantomData;
use std::time::Duration;

use libc::c_int;
use wlroots_sys::{wlr_xcursor, wlr_xcursor_frame, wlr_xcursor_image, wlr_xcursor_theme,
                  wlr_xcursor_theme_destroy, wlr_xcursor_theme_get_cursor, wlr_xcursor_theme_load};

use utils::{c_to_rust_string, safe_as_cstring};
#[cfg(feature = "unstable")]
pub use super::xcursor_manager::*;

/// Wrapper for an xcursor theme from the X11 xcursor library.
///
/// Xcursor (mostly) follows the freedesktop.org spec for theming icons.
#[derive(Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct Theme {
    theme: *mut wlr_xcursor_theme
}

/// Wrapper for an xcursor from the X11 xcursor library.
///
/// The cursor must live as long as the [`Theme`](struct.Theme.html)
/// that it comes from.
#[derive(Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct XCursor<'theme> {
    xcursor: *mut wlr_xcursor,
    phantom: PhantomData<&'theme Theme>
}

/// Contains the information necessary to render the cursor.
///
/// The hotspot of the image shows where the cursor will interact with the
/// environment. i.e. it refers to the location of the cursor "point".
/// Note that the coordinates could be inside the image.
#[derive(Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct Image<'cursor> {
    /// The width of the image in pixels.
    pub width: u32,
    /// The height of the image in pixels.
    pub height: u32,
    /// The x coordinate of the hotspot, which must be inside the image.
    pub hotspot_x: u32,
    /// The y coordinate of the hotspot, which must be inside the image.
    pub hotspot_y: u32,
    /// Animation delay to the next frame in milliseconds.
    pub delay: u32,
    /// The bytes in ARGB format.
    pub buffer: &'cursor [u8],
    /// A marker to indicate you can't send this type across threads.
    /// Also ensures you can't construct it outside of this library.
    #[doc(hidden)]
    pub(crate) _no_send: PhantomData<*mut wlr_xcursor_image>
}

impl Theme {
    /// Loads the named xcursor theme at the given cursor size (in pixels).
    ///
    /// This is useful if you need cursor images for your compositor to use when a
    /// client-side cursors is not available or you wish to override client-side
    /// cursors for a particular UI interaction (such as using a grab cursor when
    /// moving a window around)
    ///
    /// The default search path it uses is ~/.icons, /usr/share/icons, /usr/share/pixmaps.
    /// Within each of these directories, it searches for a directory using the theme name.
    /// Within the theme directory, it looks for cursor files in the 'cursors' subdirectory.
    /// It uses the first cursor file found along the path.
    ///
    /// If necessary, Xcursor also looks for a "index.theme" file in each theme directory
    /// to find inherited themes and searches along the path for those themes as well.
    ///
    /// If no name is given, defaults to "default".
    /// If no theme can be found `None` is returned.
    pub fn load_theme<T: Into<Option<String>>>(name: T, size: c_int) -> Option<Self> {
        unsafe {
            let name_str = name.into().map(safe_as_cstring);
            let name_ptr = name_str.map(|s| s.as_ptr()).unwrap_or(ptr::null_mut());
            let theme = wlr_xcursor_theme_load(name_ptr, size);
            if theme.is_null() {
                None
            } else {
                Some(Theme { theme })
            }
        }
    }

    /// Get the name of this theme.
    ///
    /// If the name returned by wlroots was malformed, or nonexistent,
    /// then the value will be None.
    pub fn name(&self) -> Option<String> {
        unsafe { c_to_rust_string((*self.theme).name) }
    }

    /// Get the size of the images.
    pub fn size(&self) -> c_int {
        unsafe { (*self.theme).size }
    }

    /// Get the number of cursors in this theme.
    pub fn cursor_count(&self) -> usize {
        unsafe { (*self.theme).cursor_count as usize }
    }

    /// Gets all the cursors from this theme.
    pub fn cursors<'theme>(&'theme mut self) -> Vec<XCursor<'theme>> {
        unsafe {
            let cursor_ptr = (*self.theme).cursors as *mut *mut wlr_xcursor;
            let length = self.cursor_count();
            let xcursors_slice: &'theme [*mut wlr_xcursor] =
                slice::from_raw_parts::<'theme, *mut wlr_xcursor>(cursor_ptr, length);
            xcursors_slice.into_iter()
                          .map(|&xcursor| XCursor::from_ptr(xcursor))
                          .collect()
        }
    }

    /// Get the cursor with the provided name (e.g. "left_ptr"), if it exists.
    pub fn get_cursor<'theme>(&'theme self, name: String) -> Option<XCursor<'theme>> {
        unsafe {
            let name_str = safe_as_cstring(name);
            let xcursor = wlr_xcursor_theme_get_cursor(self.theme, name_str.as_ptr());
            if xcursor.is_null() {
                None
            } else {
                Some(XCursor::from_ptr(xcursor))
            }
        }
    }

    /// Constructs a `Theme` from a raw pointer.
    ///
    /// # Unsafety
    /// Takes ownership of the cursor theme. When the `Theme` is dropped it
    /// calls `wlr_cursor_theme_destroy`.
    pub unsafe fn from_ptr(theme: *mut wlr_xcursor_theme) -> Theme {
        Theme { theme }
    }
}

impl Drop for Theme {
    fn drop(&mut self) {
        unsafe { wlr_xcursor_theme_destroy(self.theme) }
    }
}

impl<'theme> XCursor<'theme> {
    /// Get all the image frames associated with an xcursor.
    pub fn images<'cursor>(&'cursor self) -> Vec<Image<'cursor>> {
        unsafe {
            let cursors_slice = self.cursor_slice();
            let mut result = Vec::with_capacity(cursors_slice.len());
            for cursor in cursors_slice {
                result.push(Image::from_xcursor_image(*cursor))
            }
            result
        }
    }

    /// Get a specific frame of the image from the animation. Returns `None` if
    /// the index is out of bounds.
    ///
    /// We suggest paring this with `XCursor::frame` to avoid going out of bounds.
    pub fn image<'cursor>(&'cursor self, index: usize) -> Option<Image<'cursor>> {
        unsafe {
            let cursors_slice = self.cursor_slice();
            cursors_slice.get(index).map(|&cursor| Image::from_xcursor_image(cursor))
        }
    }

    unsafe fn cursor_slice(&self) -> &[*mut wlr_xcursor_image] {
        let image_ptr = (*self.xcursor).images as *mut *mut wlr_xcursor_image;
        slice::from_raw_parts::<'_, *mut wlr_xcursor_image>(image_ptr, self.image_len())
    }

    /// Returns the current frame number for an animated cursor give a
    /// monotonic time reference in milliseconds.
    ///
    /// e.g. if it's been animating for 500 milliseconds `duration`
    /// should be constructed from `Duration::from_millis(500)`.
    pub fn frame(&mut self, duration: Duration) -> usize {
        unsafe {
            wlr_xcursor_frame(self.xcursor, duration.subsec_millis()) as usize
        }
    }

    /// Get the number of animation frames for the cursor.
    pub fn image_len(&self) -> usize {
        unsafe { (*self.xcursor).image_count as _ }
    }

    /// Length of the animation in milliseconds.
    pub fn total_delay(&self) -> u32 {
        unsafe { (*self.xcursor).total_delay }
    }

    /// Constructs an `XCursor` from a raw pointer
    ///
    /// # Unsafety
    /// This lifetime is unbounded, but it must not outlive the
    /// xcursor manager that the pointer came from.
    pub unsafe fn from_ptr<'unbound>(xcursor: *mut wlr_xcursor) -> XCursor<'unbound> {
        XCursor { xcursor,
                  phantom: PhantomData }
    }
}

impl <'unbound> Image<'unbound> {
    unsafe fn from_xcursor_image(image: *mut wlr_xcursor_image) -> Self {
        Image {
            width: (*image).width,
            height: (*image).height,
            hotspot_x: (*image).hotspot_x,
            hotspot_y: (*image).hotspot_y,
            delay: (*image).delay,
            buffer: slice::from_raw_parts::<'_, u8>(
                (*image).buffer as *const u8,
                (*image).width as usize * (*image).height as usize
                    * mem::size_of::<u32>()
            ),
            _no_send: PhantomData
        }
    }
}
<|endoftext|>


<commit_before>use wlroots_sys::wlr_drag_icon;
use std::cell::Cell;
use std::rc::{Rc, Weak};
use std::panic;
use errors::{HandleErr, HandleResult};
use DragIconHandler;

use {SurfaceHandle};

#[derive(Debug)]
pub struct DragIcon {
    liveliness: Rc<Cell<bool>>,
    drag_icon: *mut wlr_drag_icon
}

impl DragIcon {
    pub(crate) unsafe fn new(drag_icon: *mut wlr_drag_icon) -> Self {
        let liveliness = Rc::new(Cell::new(false));
        let state = Box::new(DragIconState {
            handle: Rc::downgrade(&liveliness),
            drag_icon
        });
        (*drag_icon).data = Box::into_raw(state) as *mut _;
        DragIcon {
            liveliness,
            drag_icon
        }
    }

    /// Get a handle for the surface associated with this drag icon
    pub fn surface(&mut self) -> SurfaceHandle {
        unsafe {
            let surface = (*self.drag_icon).surface;
			if surface.is_null() {
				panic!("drag icon had a null surface!");
			}
			SurfaceHandle::from_ptr(surface)
        }
    }

    /// The position to place the surface of the drag icon relative to the cursor position
	pub fn position(&mut self) -> (i32, i32) {
        unsafe { ((*self.drag_icon).sx, (*self.drag_icon).sy) }
	}

    /// Whether or not to display the drag icon
    pub fn mapped(&mut self) -> bool {
        unsafe { (*self.drag_icon).mapped }
    }

    /// Whether or not this drag icon comes from a pointer-drive dnd operation
    pub fn is_pointer(&mut self) -> bool {
        unsafe { (*self.drag_icon).is_pointer }
    }

    /// If this is a touch-driven dnd operation, the id of the touch point that started it
    pub fn touch_id(&mut self) -> i32 {
        unsafe { (*self.drag_icon).touch_id }
    }

    unsafe fn from_handle(handle: &DragIconHandle) -> HandleResult<Self> {
        let liveliness = handle.handle
                               .upgrade()
                               .ok_or_else(|| HandleErr::AlreadyDropped)?;
        Ok(DragIcon { liveliness,
                      drag_icon: handle.as_ptr() })
    }

    pub fn weak_reference(&self) -> DragIconHandle {
        DragIconHandle {
            handle: Rc::downgrade(&self.liveliness),
            drag_icon: self.drag_icon
        }
    }
}

pub struct DragIconHandle {
    handle: Weak<Cell<bool>>,
    drag_icon: *mut wlr_drag_icon
}

impl DragIconHandle {
    pub(crate) unsafe fn from_ptr(drag_icon: *mut wlr_drag_icon) -> Self {
        if drag_icon.is_null() {
            panic!("drag icon was null");
        }
        let data = (*drag_icon).data as *mut DragIconState;
        if data.is_null() {
            panic!("Cannot construct handle from drag icon that has not been set up!");
        }

        let handle = (*data).handle.clone();

        DragIconHandle {
            handle,
            drag_icon
        }
    }

    pub(crate) unsafe fn upgrade(&self) -> HandleResult<DragIcon> {
        self.handle.upgrade()
            .ok_or(HandleErr::AlreadyDropped)
            // NOTE
            // We drop the Rc here because having two would allow a dangling
            // pointer to exist!
            .and_then(|check| {
                let drag_icon = DragIcon::from_handle(self)?;
                if check.get() {
                    return Err(HandleErr::AlreadyBorrowed)
                }
                check.set(true);
                Ok(drag_icon)
            })
    }

    pub fn run<F, R>(&self, runner: F) -> HandleResult<R>
        where F: FnOnce(&mut DragIcon) -> R
    {
        let mut drag_icon = unsafe { self.upgrade()? };
        let res = panic::catch_unwind(panic::AssertUnwindSafe(|| runner(&mut drag_icon)));
        self.handle.upgrade().map(|check| {
                                      // Sanity check that it hasn't been tampered with.
                                      if !check.get() {
                                          wlr_log!(L_ERROR,
                                                   "After running DragIcon callback, \
                                                    mutable lock was false for: {:?}",
                                                   drag_icon);
                                          panic!("Lock in incorrect state!");
                                      }
                                      check.set(false);
                                  });
        match res {
            Ok(res) => Ok(res),
            Err(err) => panic::resume_unwind(err)
        }
    }

    unsafe fn as_ptr(&self) -> *mut wlr_drag_icon {
        self.drag_icon
    }
}

pub(crate) struct DragIconState {
    pub(crate) handle: Weak<Cell<bool>>,
    pub(crate) drag_icon: *mut wlr_drag_icon
}
<commit_msg>implement Hash and PartialEq for DragIconHandle<commit_after>use wlroots_sys::wlr_drag_icon;
use std::cell::Cell;
use std::rc::{Rc, Weak};
use std::hash::{Hash, Hasher};
use std::panic;
use errors::{HandleErr, HandleResult};
use DragIconHandler;

use {SurfaceHandle};

#[derive(Debug)]
pub struct DragIcon {
    liveliness: Rc<Cell<bool>>,
    drag_icon: *mut wlr_drag_icon
}

impl DragIcon {
    pub(crate) unsafe fn new(drag_icon: *mut wlr_drag_icon) -> Self {
        let liveliness = Rc::new(Cell::new(false));
        let state = Box::new(DragIconState {
            handle: Rc::downgrade(&liveliness),
            drag_icon
        });
        (*drag_icon).data = Box::into_raw(state) as *mut _;
        DragIcon {
            liveliness,
            drag_icon
        }
    }

    /// Get a handle for the surface associated with this drag icon
    pub fn surface(&mut self) -> SurfaceHandle {
        unsafe {
            let surface = (*self.drag_icon).surface;
			if surface.is_null() {
				panic!("drag icon had a null surface!");
			}
			SurfaceHandle::from_ptr(surface)
        }
    }

    /// The position to place the surface of the drag icon relative to the cursor position
	pub fn position(&mut self) -> (i32, i32) {
        unsafe { ((*self.drag_icon).sx, (*self.drag_icon).sy) }
	}

    /// Whether or not to display the drag icon
    pub fn mapped(&mut self) -> bool {
        unsafe { (*self.drag_icon).mapped }
    }

    /// Whether or not this drag icon comes from a pointer-drive dnd operation
    pub fn is_pointer(&mut self) -> bool {
        unsafe { (*self.drag_icon).is_pointer }
    }

    /// If this is a touch-driven dnd operation, the id of the touch point that started it
    pub fn touch_id(&mut self) -> i32 {
        unsafe { (*self.drag_icon).touch_id }
    }

    unsafe fn from_handle(handle: &DragIconHandle) -> HandleResult<Self> {
        let liveliness = handle.handle
                               .upgrade()
                               .ok_or_else(|| HandleErr::AlreadyDropped)?;
        Ok(DragIcon { liveliness,
                      drag_icon: handle.as_ptr() })
    }

    pub fn weak_reference(&self) -> DragIconHandle {
        DragIconHandle {
            handle: Rc::downgrade(&self.liveliness),
            drag_icon: self.drag_icon
        }
    }
}

#[derive(Debug, Clone)]
pub struct DragIconHandle {
    handle: Weak<Cell<bool>>,
    drag_icon: *mut wlr_drag_icon
}

impl Eq for DragIconHandle {}

impl PartialEq for DragIconHandle {
    fn eq(&self, rhs: &Self) -> bool {
        self.drag_icon == rhs.drag_icon
    }
}

impl Hash for DragIconHandle {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.drag_icon.hash(state);
    }
}

impl DragIconHandle {
    pub(crate) unsafe fn from_ptr(drag_icon: *mut wlr_drag_icon) -> Self {
        if drag_icon.is_null() {
            panic!("drag icon was null");
        }
        let data = (*drag_icon).data as *mut DragIconState;
        if data.is_null() {
            panic!("Cannot construct handle from drag icon that has not been set up!");
        }

        let handle = (*data).handle.clone();

        DragIconHandle {
            handle,
            drag_icon
        }
    }

    pub(crate) unsafe fn upgrade(&self) -> HandleResult<DragIcon> {
        self.handle.upgrade()
            .ok_or(HandleErr::AlreadyDropped)
            // NOTE
            // We drop the Rc here because having two would allow a dangling
            // pointer to exist!
            .and_then(|check| {
                let drag_icon = DragIcon::from_handle(self)?;
                if check.get() {
                    return Err(HandleErr::AlreadyBorrowed)
                }
                check.set(true);
                Ok(drag_icon)
            })
    }

    pub fn run<F, R>(&self, runner: F) -> HandleResult<R>
        where F: FnOnce(&mut DragIcon) -> R
    {
        let mut drag_icon = unsafe { self.upgrade()? };
        let res = panic::catch_unwind(panic::AssertUnwindSafe(|| runner(&mut drag_icon)));
        self.handle.upgrade().map(|check| {
                                      // Sanity check that it hasn't been tampered with.
                                      if !check.get() {
                                          wlr_log!(L_ERROR,
                                                   "After running DragIcon callback, \
                                                    mutable lock was false for: {:?}",
                                                   drag_icon);
                                          panic!("Lock in incorrect state!");
                                      }
                                      check.set(false);
                                  });
        match res {
            Ok(res) => Ok(res),
            Err(err) => panic::resume_unwind(err)
        }
    }

    unsafe fn as_ptr(&self) -> *mut wlr_drag_icon {
        self.drag_icon
    }
}

pub(crate) struct DragIconState {
    pub(crate) handle: Weak<Cell<bool>>,
    pub(crate) drag_icon: *mut wlr_drag_icon
}
<|endoftext|>


<commit_before>#![feature(phase)]

extern crate regex;
#[phase(plugin)] extern crate regex_macros;

use std::io::{BufferedReader, File, IoError, OtherIoError};
use std::os::{setenv, self_exe_path};

#[deriving(Show, Clone)]
pub struct ParseError {
	line: String
}

#[deriving(Show, Clone)]
pub enum DotenvError {
	Parse(ParseError),
	Io(IoError)
}

fn parse_line(line: String) -> Result<Option<(String, String)>, ParseError> {
	let line_regex = regex!(concat!(r"^(\s*(",
		r"#.*|", // A comment, or...
		r"(export\s+)?", // ...(optionally preceded by "export")...
		r"(?P<key>[A-Za-z_][A-Za-z0-9_]*)", // ...a key,...
		r"\s*=\s*", // ...then an equal sign,...
		r"(?P<value>.+?)", // ...and then its corresponding value.
	r")\s*)$"));

	line_regex.captures(line.as_slice()).map_or(
		Err(ParseError{line: line.clone()}),
		|captures| {
			let key = captures.name("key");
			let value = captures.name("value");

			if key == "" || value == "" {
				// If there's no key and value, but capturing did not fail,
				// then this means we're dealing with a comment.
				Ok(None)
			} else {
				Ok(Some((key.to_string(), value.to_string())))
			}
		}
	)
}

fn parse_line_iter<T: Iterator<String>>(lines: T) -> Result<Vec<(String, String)>, ParseError> {
	let mut parsed_lines = lines.map(parse_line);
	let failure = parsed_lines.find(|line| line.is_err());

	if failure.is_some() {
		return Err(failure.unwrap().err().unwrap());
	}

	Ok(parsed_lines.filter_map(|line| {
		line.clone().unwrap()
	}).collect())
}

fn lines_to_env(lines: Vec<(String, String)>) {
	for (key, value) in lines.into_iter() {
		setenv(key.as_slice(), value);
	}
}

fn from_file(file: File) -> Result<(), DotenvError> {
	let mut reader = BufferedReader::new(file);
	let lines = reader.lines();

	parse_line_iter(lines.filter_map(|result| {
		result.ok()
	})).map(lines_to_env).map_err(|err| {
		Parse(err)
	})
}

pub fn from_path(path: &Path) -> Result<(), DotenvError> {
	match File::open(path) {
		Ok(file) => from_file(file),
		Err(err) => Err(Io(err))
	}
}

pub fn from_filename(filename: &str) -> Result<(), DotenvError> {
	self_exe_path().as_mut().map(|path| {
		path.push(filename);
		from_path(path)
	}).unwrap_or(Err(Io(IoError{
		kind: OtherIoError,
		desc: "Could not fetch the path of this executable",
		detail: None
	})))
}

pub fn dotenv() -> Result<(), DotenvError> {
	from_filename(".env")
}

#[test]
fn test_parse_line_env() {
	let input_iter = vec![
		"THIS_IS_KEY=hi this is value",
		"   many_spaces  =   wow a  maze   ",
		"export   SHELL_LOVER=1"
	].into_iter().map(|input| input.to_string());
	let actual_iter = input_iter.map(|input| parse_line(input));

	let expected_iter = vec![
		("THIS_IS_KEY", "hi this is value"),
		("many_spaces", "wow a  maze"),
		("SHELL_LOVER", "1")
	].into_iter().map(|(key, value)| (key.to_string(), value.to_string()));

	for (expected, actual) in expected_iter.zip(actual_iter) {
		assert!(actual.is_ok());
		assert!(actual.clone().ok().unwrap().is_some())
		assert_eq!(expected, actual.ok().unwrap().unwrap())
	}
}

#[test]
fn test_parse_line_comment() {
	let input_iter = vec![
		"# foo=bar",
		"    #    "
	].into_iter().map(|input| input.to_string());
	let mut actual_iter = input_iter.map(|input| parse_line(input));

	for actual in actual_iter {
		assert!(actual.is_ok());
		assert!(actual.ok().unwrap().is_none());
	}
}

#[test]
fn test_parse_line_invalid() {
	let input_iter = vec![
		"  invalid    ",
		"very bacon = yes indeed",
		"key=",
		"=value"
	].into_iter().map(|input| input.to_string());
	let mut actual_iter = input_iter.map(|input| parse_line(input));

	for actual in actual_iter {
		assert!(actual.is_err());
	}
}

#[test]
fn test_from_line_iter_valid() {
	let input = vec![
		"test_env_one=1",
		"# a comment",
		"test_env_two=2"
	].into_iter().map(|line| line.to_string());
	let actual = parse_line_iter(input);

	assert!(actual.is_ok())
}

#[test]
fn test_from_line_iter_invalid() {
	let input = vec![
		"test_env_one=1",
		"# a comment",
		"not valid"
	].into_iter().map(|line| line.to_string());
	let actual = parse_line_iter(input);

	assert!(actual.is_err())
}
<commit_msg>Don't override existing environment variables!<commit_after>#![feature(phase)]

extern crate regex;
#[phase(plugin)] extern crate regex_macros;

use std::io::{BufferedReader, File, IoError, OtherIoError};
use std::os::{getenv, setenv, self_exe_path};

#[deriving(Show, Clone)]
pub struct ParseError {
	line: String
}

#[deriving(Show, Clone)]
pub enum DotenvError {
	Parse(ParseError),
	Io(IoError)
}

fn parse_line(line: String) -> Result<Option<(String, String)>, ParseError> {
	let line_regex = regex!(concat!(r"^(\s*(",
		r"#.*|", // A comment, or...
		r"(export\s+)?", // ...(optionally preceded by "export")...
		r"(?P<key>[A-Za-z_][A-Za-z0-9_]*)", // ...a key,...
		r"\s*=\s*", // ...then an equal sign,...
		r"(?P<value>.+?)", // ...and then its corresponding value.
	r")\s*)$"));

	line_regex.captures(line.as_slice()).map_or(
		Err(ParseError{line: line.clone()}),
		|captures| {
			let key = captures.name("key");
			let value = captures.name("value");

			if key == "" || value == "" {
				// If there's no key and value, but capturing did not fail,
				// then this means we're dealing with a comment.
				Ok(None)
			} else {
				Ok(Some((key.to_string(), value.to_string())))
			}
		}
	)
}

fn parse_line_iter<T: Iterator<String>>(lines: T) -> Result<Vec<(String, String)>, ParseError> {
	let mut parsed_lines = lines.map(parse_line);
	let failure = parsed_lines.find(|line| line.is_err());

	if failure.is_some() {
		return Err(failure.unwrap().err().unwrap());
	}

	Ok(parsed_lines.filter_map(|line| {
		line.clone().unwrap()
	}).collect())
}

fn lines_to_env(lines: Vec<(String, String)>) {
	for (key, value) in lines.into_iter() {
		if getenv(key.as_slice()).is_none() {
			setenv(key.as_slice(), value);
		}
	}
}

fn from_file(file: File) -> Result<(), DotenvError> {
	let mut reader = BufferedReader::new(file);
	let lines = reader.lines();

	parse_line_iter(lines.filter_map(|result| {
		result.ok()
	})).map(lines_to_env).map_err(|err| {
		Parse(err)
	})
}

pub fn from_path(path: &Path) -> Result<(), DotenvError> {
	match File::open(path) {
		Ok(file) => from_file(file),
		Err(err) => Err(Io(err))
	}
}

pub fn from_filename(filename: &str) -> Result<(), DotenvError> {
	self_exe_path().as_mut().map(|path| {
		path.push(filename);
		from_path(path)
	}).unwrap_or(Err(Io(IoError{
		kind: OtherIoError,
		desc: "Could not fetch the path of this executable",
		detail: None
	})))
}

pub fn dotenv() -> Result<(), DotenvError> {
	from_filename(".env")
}

#[test]
fn test_parse_line_env() {
	let input_iter = vec![
		"THIS_IS_KEY=hi this is value",
		"   many_spaces  =   wow a  maze   ",
		"export   SHELL_LOVER=1"
	].into_iter().map(|input| input.to_string());
	let actual_iter = input_iter.map(|input| parse_line(input));

	let expected_iter = vec![
		("THIS_IS_KEY", "hi this is value"),
		("many_spaces", "wow a  maze"),
		("SHELL_LOVER", "1")
	].into_iter().map(|(key, value)| (key.to_string(), value.to_string()));

	for (expected, actual) in expected_iter.zip(actual_iter) {
		assert!(actual.is_ok());
		assert!(actual.clone().ok().unwrap().is_some())
		assert_eq!(expected, actual.ok().unwrap().unwrap())
	}
}

#[test]
fn test_parse_line_comment() {
	let input_iter = vec![
		"# foo=bar",
		"    #    "
	].into_iter().map(|input| input.to_string());
	let mut actual_iter = input_iter.map(|input| parse_line(input));

	for actual in actual_iter {
		assert!(actual.is_ok());
		assert!(actual.ok().unwrap().is_none());
	}
}

#[test]
fn test_parse_line_invalid() {
	let input_iter = vec![
		"  invalid    ",
		"very bacon = yes indeed",
		"key=",
		"=value"
	].into_iter().map(|input| input.to_string());
	let mut actual_iter = input_iter.map(|input| parse_line(input));

	for actual in actual_iter {
		assert!(actual.is_err());
	}
}

#[test]
fn test_from_line_iter_valid() {
	let input = vec![
		"test_env_one=1",
		"# a comment",
		"test_env_two=2"
	].into_iter().map(|line| line.to_string());
	let actual = parse_line_iter(input);

	assert!(actual.is_ok())
}

#[test]
fn test_from_line_iter_invalid() {
	let input = vec![
		"test_env_one=1",
		"# a comment",
		"not valid"
	].into_iter().map(|line| line.to_string());
	let actual = parse_line_iter(input);

	assert!(actual.is_err())
}
<|endoftext|>


<commit_before>use parser::{Cond, Expr, Node, Target, WS};
use std::str;
use std::collections::HashSet;
use syn;
use quote::{Tokens, ToTokens};

fn path_as_identifier(s: &str) -> String {
    let mut res = String::new();
    for c in s.chars() {
        if c.is_alphanumeric() {
            res.push(c);
        } else {
            res.push_str(&format!("{:x}", c as u32));
        }
    }
    res
}

struct Generator<'a> {
    buf: String,
    indent: u8,
    start: bool,
    locals: HashSet<String>,
    next_ws: Option<&'a str>,
    skip_ws: bool,
}

impl<'a> Generator<'a> {

    fn new() -> Generator<'a> {
        Generator {
            buf: String::new(),
            indent: 0,
            start: true,
            locals: HashSet::new(),
            next_ws: None,
            skip_ws: false,
        }
    }

    fn indent(&mut self) {
        self.indent += 1;
    }

    fn dedent(&mut self) {
        self.indent -= 1;
    }

    fn write(&mut self, s: &str) {
        if self.start {
            for _ in 0..(self.indent * 4) {
                self.buf.push(' ');
            }
            self.start = false;
        }
        self.buf.push_str(s);
    }

    fn writeln(&mut self, s: &str) {
        if s.is_empty() {
            return;
        }
        if s == "}" {
            self.dedent();
        }
        self.write(s);
        if s.ends_with('{') {
            self.indent();
        }
        self.buf.push('\n');
        self.start = true;
    }

    fn flush_ws(&mut self, ws: &WS) {
        if self.next_ws.is_some() && !ws.0 {
            let val = self.next_ws.unwrap();
            if !val.is_empty() {
                self.writeln(&format!("writer.write_str({:#?}).unwrap();",
                                      val));
            }
        }
        self.next_ws = None;
    }

    fn prepare_ws(&mut self, ws: &WS) {
        self.skip_ws = ws.1;
    }

    fn handle_ws(&mut self, ws: &WS) {
        self.flush_ws(ws);
        self.prepare_ws(ws);
    }

    fn visit_num_lit(&mut self, s: &str) {
        self.write(s);
    }

    fn visit_str_lit(&mut self, s: &str) {
        self.write(&format!("\"{}\"", s));
    }

    fn visit_var(&mut self, s: &str) {
        if self.locals.contains(s) {
            self.write(s);
        } else {
            self.write(&format!("self.{}", s));
        }
    }

    fn visit_attr(&mut self, obj: &Expr, attr: &str) {
        if let Expr::Var(name) = *obj {
            if name == "loop" {
                self.write("_loop_index");
                if attr == "index" {
                    self.write(" + 1");
                    return;
                } else if attr == "index0" {
                    return;
                } else {
                    panic!("unknown loop variable");
                }
            }
        }
        self.visit_expr(obj);
        self.write(&format!(".{}", attr));
    }

    fn visit_filter(&mut self, name: &str, args: &[Expr]) {
        if name == "format" {
            self.write("format!(");
        } else {
            self.write(&format!("askama::filters::{}(&", name));
        }

        for (i, arg) in args.iter().enumerate() {
            if i > 0 {
                self.write(", &");
            }
            self.visit_expr(arg);
        }
        self.write(")");
    }

    fn visit_binop(&mut self, op: &str, left: &Expr, right: &Expr) {
        self.visit_expr(left);
        self.write(&format!(" {} ", op));
        self.visit_expr(right);
    }

    fn visit_group(&mut self, inner: &Expr) {
        self.write("(");
        self.visit_expr(inner);
        self.write(")");
    }

    fn visit_call(&mut self, obj: &Expr, method: &str, args: &[Expr]) {
        self.visit_expr(obj);
        self.write(&format!(".{}(", method));
        for (i, arg) in args.iter().enumerate() {
            if i > 0 {
                self.write(", ");
            }
            self.visit_expr(arg);
        }
        self.write(")");
    }

    fn visit_expr(&mut self, expr: &Expr) {
        match *expr {
            Expr::NumLit(s) => self.visit_num_lit(s),
            Expr::StrLit(s) => self.visit_str_lit(s),
            Expr::Var(s) => self.visit_var(s),
            Expr::Attr(ref obj, name) => self.visit_attr(obj, name),
            Expr::Filter(name, ref args) => self.visit_filter(name, args),
            Expr::BinOp(op, ref left, ref right) =>
                self.visit_binop(op, left, right),
            Expr::Group(ref inner) => self.visit_group(inner),
            Expr::Call(ref obj, method, ref args) =>
                self.visit_call(obj, method, args),
        }
    }

    fn visit_target_single(&mut self, name: &str) -> Vec<String> {
        vec![name.to_string()]
    }

    fn visit_target(&mut self, target: &Target) -> Vec<String> {
        match *target {
            Target::Name(s) => { self.visit_target_single(s) },
        }
    }

    fn write_lit(&mut self, lws: &'a str, val: &str, rws: &'a str) {
        assert!(self.next_ws.is_none());
        if !lws.is_empty() {
            if self.skip_ws {
                self.skip_ws = false;
            } else if val.is_empty() {
                assert!(rws.is_empty());
                self.next_ws = Some(lws);
            } else {
                self.writeln(&format!("writer.write_str({:#?}).unwrap();",
                                      lws));
            }
        }
        if !val.is_empty() {
            self.writeln(&format!("writer.write_str({:#?}).unwrap();", val));
        }
        if !rws.is_empty() {
            self.next_ws = Some(rws);
        }
    }

    fn write_expr(&mut self, ws: &WS, s: &Expr) {
        self.handle_ws(ws);
        self.write("writer.write_fmt(format_args!(\"{}\", ");
        self.visit_expr(s);
        self.writeln(")).unwrap();");
    }

    fn write_cond(&mut self, conds: &'a [Cond], ws: &WS) {
        for (i, &(ref cws, ref cond, ref nodes)) in conds.iter().enumerate() {
            self.handle_ws(cws);
            match *cond {
                Some(ref expr) => {
                    if i == 0 {
                        self.write("if ");
                    } else {
                        self.dedent();
                        self.write("} else if ");
                    }
                    self.visit_expr(expr);
                },
                None => {
                    self.dedent();
                    self.write("} else");
                },
            }
            self.writeln(" {");
            self.handle(nodes);
        }
        self.handle_ws(ws);
        self.writeln("}");
    }

    fn write_loop(&mut self, ws1: &WS, var: &Target, iter: &Expr,
                  body: &'a [Node], ws2: &WS) {
        self.handle_ws(ws1);
        self.write("for (_loop_index, ");
        let targets = self.visit_target(var);
        for name in &targets {
            self.locals.insert(name.clone());
            self.write(name);
        }
        self.write(") in (&");
        self.visit_expr(iter);
        self.writeln(").into_iter().enumerate() {");

        self.handle(body);
        self.handle_ws(ws2);
        self.writeln("}");
        for name in &targets {
            self.locals.remove(name);
        }
    }

    fn write_block(&mut self, ws1: &WS, name: &str, ws2: &WS) {
        self.flush_ws(ws1);
        self.writeln(&format!("timpl.render_block_{}_to(writer);", name));
        self.prepare_ws(ws2);
    }

    fn write_block_def(&mut self, ws1: &WS, name: &str, nodes: &'a [Node],
                       ws2: &WS) {
        self.writeln("#[allow(unused_variables)]");
        self.writeln(&format!(
            "fn render_block_{}_to(&self, writer: &mut std::fmt::Write) {{",
            name));
        self.prepare_ws(ws1);
        self.handle(nodes);
        self.flush_ws(ws2);
        self.writeln("}");
    }

    fn handle(&mut self, nodes: &'a [Node]) {
        for n in nodes {
            match *n {
                Node::Lit(lws, val, rws) => { self.write_lit(lws, val, rws); }
                Node::Comment() => {},
                Node::Expr(ref ws, ref val) => { self.write_expr(ws, val); },
                Node::Cond(ref conds, ref ws) => {
                    self.write_cond(conds, ws);
                },
                Node::Loop(ref ws1, ref var, ref iter, ref body, ref ws2) => {
                    self.write_loop(ws1, var, iter, body, ws2);
                },
                Node::Block(ref ws1, name, ref ws2) => {
                    self.write_block(ws1, name, ws2);
                },
                Node::BlockDef(ref ws1, name, ref block_nodes, ref ws2) => {
                    self.write_block_def(ws1, name, block_nodes, ws2);
                }
                Node::Extends(_) => {
                    panic!("no extends or block definition allowed in content");
                },
            }
        }
    }

    fn write_header(&mut self, ast: &syn::DeriveInput,
                    trait_suffix: Option<&str>) {
        let mut full_anno = Tokens::new();
        let mut orig_anno = Tokens::new();
        let need_anno = ast.generics.lifetimes.len() > 0 ||
                        ast.generics.ty_params.len() > 0;
        if need_anno {
            full_anno.append("<");
            orig_anno.append("<");
        }

        let mut sep = false;
        for lt in &ast.generics.lifetimes {
            if sep {
                full_anno.append(",");
                orig_anno.append(",");
            }
            lt.to_tokens(&mut full_anno);
            lt.to_tokens(&mut orig_anno);
            sep = true;
        }

        for param in &ast.generics.ty_params {
            if sep {
                full_anno.append(",");
                orig_anno.append(",");
            }
            let mut impl_param = param.clone();
            impl_param.default = None;
            impl_param.to_tokens(&mut full_anno);
            param.ident.to_tokens(&mut orig_anno);
            sep = true;
        }

        if need_anno {
            full_anno.append(">");
            orig_anno.append(">");
        }

        let mut where_clause = Tokens::new();
        ast.generics.where_clause.to_tokens(&mut where_clause);
        let name = if let Some(suffix) = trait_suffix {
            format!("TraitFrom{}", suffix)
        } else {
            "askama::Template".to_string()
        };
        self.writeln(&format!("impl{} {} for {}{}{} {{",
                              full_anno.as_str(), &name, ast.ident.as_ref(),
                              orig_anno.as_str(), where_clause.as_str()));
    }

    fn impl_template(&mut self, ast: &syn::DeriveInput, nodes: &'a [Node]) {
        self.write_header(ast, None);
        self.writeln("fn render_to(&self, writer: &mut std::fmt::Write) {");
        self.handle(nodes);
        self.flush_ws(&WS(false, false));
        self.writeln("}");
        self.writeln("}");
    }

    fn impl_trait(&mut self, ast: &syn::DeriveInput, base: &str,
                  blocks: &'a [Node], nodes: Option<&'a [Node]>) {
        self.write_header(ast, Some(&path_as_identifier(base)));
        self.handle(blocks);

        self.writeln("#[allow(unused_variables)]");
        let trait_name = format!("TraitFrom{}", path_as_identifier(base));
        self.writeln(&format!(
            "fn render_trait_to(&self, timpl: &{}, writer: &mut std::fmt::Write) {{",
            trait_name));

        if let Some(nodes) = nodes {
            self.handle(nodes);
            self.flush_ws(&WS(false, false));
        } else {
            self.writeln("self._parent.render_trait_to(self, writer);");
        }

        self.writeln("}");
        self.flush_ws(&WS(false, false));
        self.writeln("}");
    }

    fn impl_template_for_trait(&mut self, ast: &syn::DeriveInput, derived: bool) {
        self.write_header(ast, None);
        self.writeln("fn render_to(&self, writer: &mut std::fmt::Write) {");
        if derived {
            self.writeln("self._parent.render_trait_to(self, writer);");
        } else {
            self.writeln("self.render_trait_to(self, writer);");
        }
        self.writeln("}");
        self.writeln("}");
    }

    fn define_trait(&mut self, path: &str, block_names: &[&str]) {
        let trait_name = format!("TraitFrom{}", path_as_identifier(path));
        self.writeln(&format!("trait {} {{", &trait_name));

        for bname in block_names {
            self.writeln(&format!(
                "fn render_block_{}_to(&self, writer: &mut std::fmt::Write);",
                bname));
        }
        self.writeln(&format!(
            "fn render_trait_to(&self, timpl: &{}, writer: &mut std::fmt::Write);",
            trait_name));

        self.writeln("}");
    }

    fn result(self) -> String {
        self.buf
    }

}

pub fn generate(ast: &syn::DeriveInput, path: &str, mut nodes: Vec<Node>) -> String {
    let mut base: Option<Expr> = None;
    let mut blocks = Vec::new();
    let mut block_names = Vec::new();
    let mut content = Vec::new();
    for n in nodes.drain(..) {
        match n {
            Node::Extends(path) => {
                match base {
                    Some(_) => panic!("multiple extend blocks found"),
                    None => { base = Some(path); },
                }
            },
            Node::BlockDef(ws1, name, _, ws2) => {
                blocks.push(n);
                block_names.push(name);
                content.push(Node::Block(ws1, name, ws2));
            },
            _ => { content.push(n); },
        }
    }

    let mut gen = Generator::new();
    if !blocks.is_empty() {
        if base.is_none() {
            gen.define_trait(path, &block_names);
        }
        let tmpl_path = match base {
            Some(Expr::StrLit(base_path)) => { base_path },
            _ => { path },
        };
        let trait_nodes = if base.is_none() { Some(&content[..]) } else { None };
        gen.impl_trait(ast, tmpl_path, &blocks, trait_nodes);
        gen.impl_template_for_trait(ast, base.is_some());
    } else {
        gen.impl_template(ast, &content);
    }
    gen.result()
}
<commit_msg>Reference askama and std from the global scope (fixes #17)<commit_after>use parser::{Cond, Expr, Node, Target, WS};
use std::str;
use std::collections::HashSet;
use syn;
use quote::{Tokens, ToTokens};

fn path_as_identifier(s: &str) -> String {
    let mut res = String::new();
    for c in s.chars() {
        if c.is_alphanumeric() {
            res.push(c);
        } else {
            res.push_str(&format!("{:x}", c as u32));
        }
    }
    res
}

struct Generator<'a> {
    buf: String,
    indent: u8,
    start: bool,
    locals: HashSet<String>,
    next_ws: Option<&'a str>,
    skip_ws: bool,
}

impl<'a> Generator<'a> {

    fn new() -> Generator<'a> {
        Generator {
            buf: String::new(),
            indent: 0,
            start: true,
            locals: HashSet::new(),
            next_ws: None,
            skip_ws: false,
        }
    }

    fn indent(&mut self) {
        self.indent += 1;
    }

    fn dedent(&mut self) {
        self.indent -= 1;
    }

    fn write(&mut self, s: &str) {
        if self.start {
            for _ in 0..(self.indent * 4) {
                self.buf.push(' ');
            }
            self.start = false;
        }
        self.buf.push_str(s);
    }

    fn writeln(&mut self, s: &str) {
        if s.is_empty() {
            return;
        }
        if s == "}" {
            self.dedent();
        }
        self.write(s);
        if s.ends_with('{') {
            self.indent();
        }
        self.buf.push('\n');
        self.start = true;
    }

    fn flush_ws(&mut self, ws: &WS) {
        if self.next_ws.is_some() && !ws.0 {
            let val = self.next_ws.unwrap();
            if !val.is_empty() {
                self.writeln(&format!("writer.write_str({:#?}).unwrap();",
                                      val));
            }
        }
        self.next_ws = None;
    }

    fn prepare_ws(&mut self, ws: &WS) {
        self.skip_ws = ws.1;
    }

    fn handle_ws(&mut self, ws: &WS) {
        self.flush_ws(ws);
        self.prepare_ws(ws);
    }

    fn visit_num_lit(&mut self, s: &str) {
        self.write(s);
    }

    fn visit_str_lit(&mut self, s: &str) {
        self.write(&format!("\"{}\"", s));
    }

    fn visit_var(&mut self, s: &str) {
        if self.locals.contains(s) {
            self.write(s);
        } else {
            self.write(&format!("self.{}", s));
        }
    }

    fn visit_attr(&mut self, obj: &Expr, attr: &str) {
        if let Expr::Var(name) = *obj {
            if name == "loop" {
                self.write("_loop_index");
                if attr == "index" {
                    self.write(" + 1");
                    return;
                } else if attr == "index0" {
                    return;
                } else {
                    panic!("unknown loop variable");
                }
            }
        }
        self.visit_expr(obj);
        self.write(&format!(".{}", attr));
    }

    fn visit_filter(&mut self, name: &str, args: &[Expr]) {
        if name == "format" {
            self.write("format!(");
        } else {
            self.write(&format!("::askama::filters::{}(&", name));
        }

        for (i, arg) in args.iter().enumerate() {
            if i > 0 {
                self.write(", &");
            }
            self.visit_expr(arg);
        }
        self.write(")");
    }

    fn visit_binop(&mut self, op: &str, left: &Expr, right: &Expr) {
        self.visit_expr(left);
        self.write(&format!(" {} ", op));
        self.visit_expr(right);
    }

    fn visit_group(&mut self, inner: &Expr) {
        self.write("(");
        self.visit_expr(inner);
        self.write(")");
    }

    fn visit_call(&mut self, obj: &Expr, method: &str, args: &[Expr]) {
        self.visit_expr(obj);
        self.write(&format!(".{}(", method));
        for (i, arg) in args.iter().enumerate() {
            if i > 0 {
                self.write(", ");
            }
            self.visit_expr(arg);
        }
        self.write(")");
    }

    fn visit_expr(&mut self, expr: &Expr) {
        match *expr {
            Expr::NumLit(s) => self.visit_num_lit(s),
            Expr::StrLit(s) => self.visit_str_lit(s),
            Expr::Var(s) => self.visit_var(s),
            Expr::Attr(ref obj, name) => self.visit_attr(obj, name),
            Expr::Filter(name, ref args) => self.visit_filter(name, args),
            Expr::BinOp(op, ref left, ref right) =>
                self.visit_binop(op, left, right),
            Expr::Group(ref inner) => self.visit_group(inner),
            Expr::Call(ref obj, method, ref args) =>
                self.visit_call(obj, method, args),
        }
    }

    fn visit_target_single(&mut self, name: &str) -> Vec<String> {
        vec![name.to_string()]
    }

    fn visit_target(&mut self, target: &Target) -> Vec<String> {
        match *target {
            Target::Name(s) => { self.visit_target_single(s) },
        }
    }

    fn write_lit(&mut self, lws: &'a str, val: &str, rws: &'a str) {
        assert!(self.next_ws.is_none());
        if !lws.is_empty() {
            if self.skip_ws {
                self.skip_ws = false;
            } else if val.is_empty() {
                assert!(rws.is_empty());
                self.next_ws = Some(lws);
            } else {
                self.writeln(&format!("writer.write_str({:#?}).unwrap();",
                                      lws));
            }
        }
        if !val.is_empty() {
            self.writeln(&format!("writer.write_str({:#?}).unwrap();", val));
        }
        if !rws.is_empty() {
            self.next_ws = Some(rws);
        }
    }

    fn write_expr(&mut self, ws: &WS, s: &Expr) {
        self.handle_ws(ws);
        self.write("writer.write_fmt(format_args!(\"{}\", ");
        self.visit_expr(s);
        self.writeln(")).unwrap();");
    }

    fn write_cond(&mut self, conds: &'a [Cond], ws: &WS) {
        for (i, &(ref cws, ref cond, ref nodes)) in conds.iter().enumerate() {
            self.handle_ws(cws);
            match *cond {
                Some(ref expr) => {
                    if i == 0 {
                        self.write("if ");
                    } else {
                        self.dedent();
                        self.write("} else if ");
                    }
                    self.visit_expr(expr);
                },
                None => {
                    self.dedent();
                    self.write("} else");
                },
            }
            self.writeln(" {");
            self.handle(nodes);
        }
        self.handle_ws(ws);
        self.writeln("}");
    }

    fn write_loop(&mut self, ws1: &WS, var: &Target, iter: &Expr,
                  body: &'a [Node], ws2: &WS) {
        self.handle_ws(ws1);
        self.write("for (_loop_index, ");
        let targets = self.visit_target(var);
        for name in &targets {
            self.locals.insert(name.clone());
            self.write(name);
        }
        self.write(") in (&");
        self.visit_expr(iter);
        self.writeln(").into_iter().enumerate() {");

        self.handle(body);
        self.handle_ws(ws2);
        self.writeln("}");
        for name in &targets {
            self.locals.remove(name);
        }
    }

    fn write_block(&mut self, ws1: &WS, name: &str, ws2: &WS) {
        self.flush_ws(ws1);
        self.writeln(&format!("timpl.render_block_{}_to(writer);", name));
        self.prepare_ws(ws2);
    }

    fn write_block_def(&mut self, ws1: &WS, name: &str, nodes: &'a [Node],
                       ws2: &WS) {
        self.writeln("#[allow(unused_variables)]");
        self.writeln(&format!(
            "fn render_block_{}_to(&self, writer: &mut ::std::fmt::Write) {{",
            name));
        self.prepare_ws(ws1);
        self.handle(nodes);
        self.flush_ws(ws2);
        self.writeln("}");
    }

    fn handle(&mut self, nodes: &'a [Node]) {
        for n in nodes {
            match *n {
                Node::Lit(lws, val, rws) => { self.write_lit(lws, val, rws); }
                Node::Comment() => {},
                Node::Expr(ref ws, ref val) => { self.write_expr(ws, val); },
                Node::Cond(ref conds, ref ws) => {
                    self.write_cond(conds, ws);
                },
                Node::Loop(ref ws1, ref var, ref iter, ref body, ref ws2) => {
                    self.write_loop(ws1, var, iter, body, ws2);
                },
                Node::Block(ref ws1, name, ref ws2) => {
                    self.write_block(ws1, name, ws2);
                },
                Node::BlockDef(ref ws1, name, ref block_nodes, ref ws2) => {
                    self.write_block_def(ws1, name, block_nodes, ws2);
                }
                Node::Extends(_) => {
                    panic!("no extends or block definition allowed in content");
                },
            }
        }
    }

    fn write_header(&mut self, ast: &syn::DeriveInput,
                    trait_suffix: Option<&str>) {
        let mut full_anno = Tokens::new();
        let mut orig_anno = Tokens::new();
        let need_anno = ast.generics.lifetimes.len() > 0 ||
                        ast.generics.ty_params.len() > 0;
        if need_anno {
            full_anno.append("<");
            orig_anno.append("<");
        }

        let mut sep = false;
        for lt in &ast.generics.lifetimes {
            if sep {
                full_anno.append(",");
                orig_anno.append(",");
            }
            lt.to_tokens(&mut full_anno);
            lt.to_tokens(&mut orig_anno);
            sep = true;
        }

        for param in &ast.generics.ty_params {
            if sep {
                full_anno.append(",");
                orig_anno.append(",");
            }
            let mut impl_param = param.clone();
            impl_param.default = None;
            impl_param.to_tokens(&mut full_anno);
            param.ident.to_tokens(&mut orig_anno);
            sep = true;
        }

        if need_anno {
            full_anno.append(">");
            orig_anno.append(">");
        }

        let mut where_clause = Tokens::new();
        ast.generics.where_clause.to_tokens(&mut where_clause);
        let name = if let Some(suffix) = trait_suffix {
            format!("TraitFrom{}", suffix)
        } else {
            "::askama::Template".to_string()
        };
        self.writeln(&format!("impl{} {} for {}{}{} {{",
                              full_anno.as_str(), &name, ast.ident.as_ref(),
                              orig_anno.as_str(), where_clause.as_str()));
    }

    fn impl_template(&mut self, ast: &syn::DeriveInput, nodes: &'a [Node]) {
        self.write_header(ast, None);
        self.writeln("fn render_to(&self, writer: &mut ::std::fmt::Write) {");
        self.handle(nodes);
        self.flush_ws(&WS(false, false));
        self.writeln("}");
        self.writeln("}");
    }

    fn impl_trait(&mut self, ast: &syn::DeriveInput, base: &str,
                  blocks: &'a [Node], nodes: Option<&'a [Node]>) {
        self.write_header(ast, Some(&path_as_identifier(base)));
        self.handle(blocks);

        self.writeln("#[allow(unused_variables)]");
        let trait_name = format!("TraitFrom{}", path_as_identifier(base));
        self.writeln(&format!(
            "fn render_trait_to(&self, timpl: &{}, writer: &mut ::std::fmt::Write) {{",
            trait_name));

        if let Some(nodes) = nodes {
            self.handle(nodes);
            self.flush_ws(&WS(false, false));
        } else {
            self.writeln("self._parent.render_trait_to(self, writer);");
        }

        self.writeln("}");
        self.flush_ws(&WS(false, false));
        self.writeln("}");
    }

    fn impl_template_for_trait(&mut self, ast: &syn::DeriveInput, derived: bool) {
        self.write_header(ast, None);
        self.writeln("fn render_to(&self, writer: &mut ::std::fmt::Write) {");
        if derived {
            self.writeln("self._parent.render_trait_to(self, writer);");
        } else {
            self.writeln("self.render_trait_to(self, writer);");
        }
        self.writeln("}");
        self.writeln("}");
    }

    fn define_trait(&mut self, path: &str, block_names: &[&str]) {
        let trait_name = format!("TraitFrom{}", path_as_identifier(path));
        self.writeln(&format!("trait {} {{", &trait_name));

        for bname in block_names {
            self.writeln(&format!(
                "fn render_block_{}_to(&self, writer: &mut ::std::fmt::Write);",
                bname));
        }
        self.writeln(&format!(
            "fn render_trait_to(&self, timpl: &{}, writer: &mut ::std::fmt::Write);",
            trait_name));

        self.writeln("}");
    }

    fn result(self) -> String {
        self.buf
    }

}

pub fn generate(ast: &syn::DeriveInput, path: &str, mut nodes: Vec<Node>) -> String {
    let mut base: Option<Expr> = None;
    let mut blocks = Vec::new();
    let mut block_names = Vec::new();
    let mut content = Vec::new();
    for n in nodes.drain(..) {
        match n {
            Node::Extends(path) => {
                match base {
                    Some(_) => panic!("multiple extend blocks found"),
                    None => { base = Some(path); },
                }
            },
            Node::BlockDef(ws1, name, _, ws2) => {
                blocks.push(n);
                block_names.push(name);
                content.push(Node::Block(ws1, name, ws2));
            },
            _ => { content.push(n); },
        }
    }

    let mut gen = Generator::new();
    if !blocks.is_empty() {
        if base.is_none() {
            gen.define_trait(path, &block_names);
        }
        let tmpl_path = match base {
            Some(Expr::StrLit(base_path)) => { base_path },
            _ => { path },
        };
        let trait_nodes = if base.is_none() { Some(&content[..]) } else { None };
        gen.impl_trait(ast, tmpl_path, &blocks, trait_nodes);
        gen.impl_template_for_trait(ast, base.is_some());
    } else {
        gen.impl_template(ast, &content);
    }
    gen.result()
}
<|endoftext|>


<commit_before>use ::GetSlice;

use alloc::boxed::Box;

use collections::string::{String, ToString};
use collections::vec::Vec;

use common::debug;
use common::parse_path::parse_path;

use graphics::bmp::BmpFile;

use schemes::Url;

/// A package (_REDOX content serialized)
pub struct Package {
    /// The URL
    pub url: Url,
    /// The ID of the package
    pub id: String,
    /// The name of the package
    pub name: String,
    /// The binary for the package
    pub binary: Url,
    /// The icon for the package
    pub icon: BmpFile,
    /// The accepted extensions
    pub accepts: Vec<String>,
    /// The author(s) of the package
    pub authors: Vec<String>,
    /// The description of the package
    pub descriptions: Vec<String>,
}

impl Package {
    /// Create package from URL
    pub fn from_url(url: &Url) -> Box<Self> {
        let mut package = box Package {
            url: url.clone(),
            id: String::new(),
            name: String::new(),
            binary: Url::new(),
            icon: BmpFile::new(),
            accepts: Vec::new(),
            authors: Vec::new(),
            descriptions: Vec::new(),
        };

        let path_parts = parse_path(url.reference());

        if !path_parts.is_empty() {
            if let Some(part) = path_parts.get(path_parts.len() - 1) {
                package.id = part.clone();
                package.binary = Url::from_string(url.to_string() + part + ".bin");
            }
        }

        let mut info = String::new();

        if let Some(mut resource) = Url::from_string(url.to_string() + "_REDOX").open() {
            resource.read_to_end(unsafe { info.as_mut_vec() });
        }

        for line in info.lines_any() {
            if line.starts_with("name=") {
                package.name = line.get_slice(Some(5), None).to_string();
            } else if line.starts_with("binary=") {
                package.binary = Url::from_string(url.to_string() + line.get_slice(Some(7), None));
            } else if line.starts_with("icon=") {
                if let Some(mut resource) = Url::from_string(line.get_slice(Some(5), None).to_string()).open() {
                    let mut vec: Vec<u8> = Vec::new();
                    resource.read_to_end(&mut vec);
                    package.icon = BmpFile::from_data(&vec);
                }
            } else if line.starts_with("accept=") {
                package.accepts.push(line.get_slice(Some(7), None).to_string());
            } else if line.starts_with("author=") {
                package.authors.push(line.get_slice(Some(7), None).to_string());
            } else if line.starts_with("description=") {
                package.descriptions.push(line.get_slice(Some(12), None).to_string());
            } else {
                debug::d("Unknown package info: ");
                debug::d(&line);
                debug::dl();
            }
        }

        package
    }
}
<commit_msg>Use Lines, since LinesAny is deprecated in favor for it`<commit_after>use ::GetSlice;

use alloc::boxed::Box;

use collections::string::{String, ToString};
use collections::vec::Vec;

use common::debug;
use common::parse_path::parse_path;

use graphics::bmp::BmpFile;

use schemes::Url;

/// A package (_REDOX content serialized)
pub struct Package {
    /// The URL
    pub url: Url,
    /// The ID of the package
    pub id: String,
    /// The name of the package
    pub name: String,
    /// The binary for the package
    pub binary: Url,
    /// The icon for the package
    pub icon: BmpFile,
    /// The accepted extensions
    pub accepts: Vec<String>,
    /// The author(s) of the package
    pub authors: Vec<String>,
    /// The description of the package
    pub descriptions: Vec<String>,
}

impl Package {
    /// Create package from URL
    pub fn from_url(url: &Url) -> Box<Self> {
        let mut package = box Package {
            url: url.clone(),
            id: String::new(),
            name: String::new(),
            binary: Url::new(),
            icon: BmpFile::new(),
            accepts: Vec::new(),
            authors: Vec::new(),
            descriptions: Vec::new(),
        };

        let path_parts = parse_path(url.reference());

        if !path_parts.is_empty() {
            if let Some(part) = path_parts.get(path_parts.len() - 1) {
                package.id = part.clone();
                package.binary = Url::from_string(url.to_string() + part + ".bin");
            }
        }

        let mut info = String::new();

        if let Some(mut resource) = Url::from_string(url.to_string() + "_REDOX").open() {
            resource.read_to_end(unsafe { info.as_mut_vec() });
        }

        for line in info.lines() {
            if line.starts_with("name=") {
                package.name = line.get_slice(Some(5), None).to_string();
            } else if line.starts_with("binary=") {
                package.binary = Url::from_string(url.to_string() + line.get_slice(Some(7), None));
            } else if line.starts_with("icon=") {
                if let Some(mut resource) = Url::from_string(line.get_slice(Some(5), None).to_string()).open() {
                    let mut vec: Vec<u8> = Vec::new();
                    resource.read_to_end(&mut vec);
                    package.icon = BmpFile::from_data(&vec);
                }
            } else if line.starts_with("accept=") {
                package.accepts.push(line.get_slice(Some(7), None).to_string());
            } else if line.starts_with("author=") {
                package.authors.push(line.get_slice(Some(7), None).to_string());
            } else if line.starts_with("description=") {
                package.descriptions.push(line.get_slice(Some(12), None).to_string());
            } else {
                debug::d("Unknown package info: ");
                debug::d(&line);
                debug::dl();
            }
        }

        package
    }
}
<|endoftext|>


<commit_before>use pos::{BytePos, Column, Line, Location};

#[derive(Clone, Debug)]
pub struct Source<'a> {
    src: &'a str,
    /// The starting byte position of each line in `src`
    lines: Vec<BytePos>,
}

impl<'a> Source<'a> {
    pub fn new(src: &str) -> Source {
        use std::iter;

        let lines = {
            let input_indices = src.as_bytes()
                .iter()
                .enumerate()
                .filter(|&(_, &b)| b == b'\n')
                .map(|(i, _)| BytePos::from(i + 1)); // index of first char in the line

            iter::once(BytePos::from(0))
                .chain(input_indices)
                .collect()
        };

        Source {
            src: src,
            lines: lines,
        }
    }

    pub fn src(&self) -> &str {
        &self.src
    }

    pub fn line(&self, line_number: Line) -> Option<(BytePos, &str)> {
        let line_number = line_number.to_usize();

        self.lines.get(line_number).map(|&start| {
            let line = match self.lines.get(line_number + 1) {
                Some(end) => &self.src[start.to_usize()..end.to_usize() - 1], // Skip '\n'
                None => &self.src[start.to_usize()..],
            };

            (start, line)
        })
    }

    pub fn line_at_byte(&self, byte: BytePos) -> Option<(BytePos, &str)> {
        let line_number = self.line_number_at_byte(byte);
        self.line(line_number)
    }

    pub fn line_number_at_byte(&self, byte: BytePos) -> Line {
        let num_lines = self.lines.len();

        Line::from((0..num_lines)
            .filter(|&i| self.lines[i] > byte)
            .map(|i| i - 1)
            .next()
            .unwrap_or(num_lines - 1))
    }

    pub fn location(&self, byte: BytePos) -> Option<Location> {
        let line_index = self.line_number_at_byte(byte);

        self.line(line_index).and_then(|(line_byte, line)| {
            let mut column_index = Column::from(0);

            for (i, (next_byte, _)) in line.char_indices().enumerate() {
                column_index = Column::from(i);

                let curr_byte = line_byte + BytePos::from(next_byte);

                if curr_byte == byte {
                    return Some(Location {
                        line: line_index,
                        column: column_index,
                        absolute: byte,
                    });
                }
            }

            // Handle the case where `byte` is equal to the source's length
            if byte == line_byte + BytePos::from(line.len()) {
                Some(Location {
                    line: line_index,
                    column: column_index,
                    absolute: byte,
                })
            } else {
                None
            }
        })
    }
}

#[cfg(test)]
mod tests {
    use pos::{BytePos, Column, Line, Location};

    use super::Source;

    fn test_source() -> Source<'static> {
        Source::new("hello!\nhowdy\n\nhi萤\nbloop\n")
    }

    #[test]
    fn source_line() {
        let source = test_source();

        assert_eq!(source.line(Line::from(0)),
                   Some((BytePos::from(0), "hello!")));
        assert_eq!(source.line(Line::from(1)),
                   Some((BytePos::from(7), "howdy")));
        assert_eq!(source.line(Line::from(2)), Some((BytePos::from(13), "")));
        assert_eq!(source.line(Line::from(3)),
                   Some((BytePos::from(14), "hi萤")));
        assert_eq!(source.line(Line::from(4)),
                   Some((BytePos::from(20), "bloop")));
        assert_eq!(source.line(Line::from(5)), Some((BytePos::from(26), "")));
        assert_eq!(source.line(Line::from(6)), None);
    }

    #[test]
    fn source_line_number_at_byte() {
        let source = test_source();

        assert_eq!(source.line_number_at_byte(BytePos::from(0)), Line::from(0));
        assert_eq!(source.line_number_at_byte(BytePos::from(6)), Line::from(0));
        assert_eq!(source.line_number_at_byte(BytePos::from(7)), Line::from(1));
        assert_eq!(source.line_number_at_byte(BytePos::from(8)), Line::from(1));

        assert_eq!(source.line_number_at_byte(BytePos::from(12)), Line::from(1));
        assert_eq!(source.line_number_at_byte(BytePos::from(13)), Line::from(2));
        assert_eq!(source.line_number_at_byte(BytePos::from(14)), Line::from(3));
        assert_eq!(source.line_number_at_byte(BytePos::from(15)), Line::from(3));

        assert_eq!(source.line_number_at_byte(BytePos::from(18)), Line::from(3));
        assert_eq!(source.line_number_at_byte(BytePos::from(19)), Line::from(3));
        assert_eq!(source.line_number_at_byte(BytePos::from(20)), Line::from(4));

        assert_eq!(source.line_number_at_byte(BytePos::from(400)),
                   Line::from(5));
    }

    #[test]
    fn source_location() {
        let source = test_source();

        assert_eq!(source.location(BytePos::from(0)),
                   Some(Location {
                       line: Line::from(0),
                       column: Column::from(0),
                       absolute: BytePos::from(0),
                   }));

        assert_eq!(source.location(BytePos::from(3)),
                   Some(Location {
                       line: Line::from(0),
                       column: Column::from(3),
                       absolute: BytePos::from(3),
                   }));

        assert_eq!(source.location(BytePos::from(16)),
                   Some(Location {
                       line: Line::from(3),
                       column: Column::from(2),
                       absolute: BytePos::from(16),
                   }));

        assert_eq!(source.location(BytePos::from(400)), None);
    }

    #[test]
    fn source_location_end_of_source() {
        let source = test_source();
        assert_eq!(source.location(BytePos::from(source.src.len())),
                   Some(Location {
                       line: Line::from(5),
                       column: Column::from(0),
                       absolute: BytePos::from(source.src.len()),
                   }));
    }
}
<commit_msg>refactor: Allow location-byte mappings to be done without a stored source file<commit_after>use pos::{BytePos, Column, Line, Location};

#[derive(Clone, Debug)]
pub struct Lines {
    lines: Vec<BytePos>,
    end: usize,
}

impl Lines {
    pub fn new(src: &str) -> Lines {
        use std::iter;

        let input_indices = src.as_bytes()
            .iter()
            .enumerate()
            .filter(|&(_, &b)| b == b'\n')
            .map(|(i, _)| BytePos::from(i + 1)); // index of first char in the line

        let lines = iter::once(BytePos::from(0))
            .chain(input_indices)
            .collect();
        Lines {
            lines: lines,
            end: src.len(),
        }
    }

    pub fn line(&self, line_number: Line) -> Option<BytePos> {
        let line_number = line_number.to_usize();
        self.lines.get(line_number).cloned()
    }

    pub fn location(&self, byte: BytePos) -> Option<Location> {
        if byte.to_usize() <= self.end {
            let line_index = self.line_number_at_byte(byte);

            self.line(line_index).map(|line_byte| {
                Location {
                    line: line_index,
                    column: Column::from((byte - line_byte).to_usize()),
                    absolute: byte,
                }
            })
        } else {
            None
        }
    }

    pub fn line_number_at_byte(&self, byte: BytePos) -> Line {
        let num_lines = self.lines.len();

        Line::from((0..num_lines)
            .filter(|&i| self.lines[i] > byte)
            .map(|i| i - 1)
            .next()
            .unwrap_or(num_lines - 1))
    }
}

#[derive(Clone, Debug)]
pub struct Source<'a> {
    src: &'a str,
    /// The starting byte position of each line in `src`
    lines: Lines,
}

impl<'a> Source<'a> {
    pub fn new(src: &str) -> Source {
        Source {
            src: src,
            lines: Lines::new(src),
        }
    }

    pub fn src(&self) -> &str {
        &self.src
    }

    pub fn line(&self, line_number: Line) -> Option<(BytePos, &str)> {
        self.lines.line(line_number).map(|start| {
            let line = match self.lines.lines.get(line_number.to_usize() + 1) {
                Some(end) => &self.src[start.to_usize()..end.to_usize() - 1], // Skip '\n'
                None => &self.src[start.to_usize()..],
            };

            (start, line)
        })
    }

    pub fn line_at_byte(&self, byte: BytePos) -> Option<(BytePos, &str)> {
        let line_number = self.line_number_at_byte(byte);
        self.line(line_number)
    }

    pub fn line_number_at_byte(&self, byte: BytePos) -> Line {
        self.lines.line_number_at_byte(byte)
    }

    pub fn location(&self, byte: BytePos) -> Option<Location> {
        self.lines.location(byte)
    }
}

#[cfg(test)]
mod tests {
    use pos::{BytePos, Column, Line, Location};

    use super::Source;

    fn test_source() -> Source<'static> {
        Source::new("hello!\nhowdy\n\nhi萤\nbloop\n")
    }

    #[test]
    fn source_line() {
        let source = test_source();

        assert_eq!(source.line(Line::from(0)),
                   Some((BytePos::from(0), "hello!")));
        assert_eq!(source.line(Line::from(1)),
                   Some((BytePos::from(7), "howdy")));
        assert_eq!(source.line(Line::from(2)), Some((BytePos::from(13), "")));
        assert_eq!(source.line(Line::from(3)),
                   Some((BytePos::from(14), "hi萤")));
        assert_eq!(source.line(Line::from(4)),
                   Some((BytePos::from(20), "bloop")));
        assert_eq!(source.line(Line::from(5)), Some((BytePos::from(26), "")));
        assert_eq!(source.line(Line::from(6)), None);
    }

    #[test]
    fn source_line_number_at_byte() {
        let source = test_source();

        assert_eq!(source.line_number_at_byte(BytePos::from(0)), Line::from(0));
        assert_eq!(source.line_number_at_byte(BytePos::from(6)), Line::from(0));
        assert_eq!(source.line_number_at_byte(BytePos::from(7)), Line::from(1));
        assert_eq!(source.line_number_at_byte(BytePos::from(8)), Line::from(1));

        assert_eq!(source.line_number_at_byte(BytePos::from(12)), Line::from(1));
        assert_eq!(source.line_number_at_byte(BytePos::from(13)), Line::from(2));
        assert_eq!(source.line_number_at_byte(BytePos::from(14)), Line::from(3));
        assert_eq!(source.line_number_at_byte(BytePos::from(15)), Line::from(3));

        assert_eq!(source.line_number_at_byte(BytePos::from(18)), Line::from(3));
        assert_eq!(source.line_number_at_byte(BytePos::from(19)), Line::from(3));
        assert_eq!(source.line_number_at_byte(BytePos::from(20)), Line::from(4));

        assert_eq!(source.line_number_at_byte(BytePos::from(400)),
                   Line::from(5));
    }

    #[test]
    fn source_location() {
        let source = test_source();

        assert_eq!(source.location(BytePos::from(0)),
                   Some(Location {
                       line: Line::from(0),
                       column: Column::from(0),
                       absolute: BytePos::from(0),
                   }));

        assert_eq!(source.location(BytePos::from(3)),
                   Some(Location {
                       line: Line::from(0),
                       column: Column::from(3),
                       absolute: BytePos::from(3),
                   }));

        assert_eq!(source.location(BytePos::from(16)),
                   Some(Location {
                       line: Line::from(3),
                       column: Column::from(2),
                       absolute: BytePos::from(16),
                   }));

        assert_eq!(source.location(BytePos::from(400)), None);
    }

    #[test]
    fn source_location_end_of_source() {
        let source = test_source();
        assert_eq!(source.location(BytePos::from(source.src.len())),
                   Some(Location {
                       line: Line::from(5),
                       column: Column::from(0),
                       absolute: BytePos::from(source.src.len()),
                   }));
    }
}
<|endoftext|>