<commit_before>//! TODO Documentation

use std::panic;
use std::rc::{Rc, Weak};
use std::sync::atomic::{AtomicBool, Ordering};
use std::marker::PhantomData;

use wlroots_sys::{wlr_cursor_attach_output_layout, wlr_output_effective_resolution,
                  wlr_output_layout, wlr_output_layout_add, wlr_output_layout_add_auto,
                  wlr_output_layout_create, wlr_output_layout_destroy, wlr_output_layout_move,
                  wlr_output_layout_output, wlr_output_layout_remove};

use errors::{UpgradeHandleErr, UpgradeHandleResult};

use {Cursor, CursorBuilder, Origin, Output, OutputHandle};

#[derive(Debug)]
pub struct OutputLayout {
    /// The structure that ensures weak handles to this structure are still alive.
    ///
    /// They contain weak handles, and will safely not use dead memory when this
    /// is freed by wlroots.
    ///
    /// If this is `None`, then this is from an upgraded `OutputHandle`, and
    /// the operations are **unchecked**.
    /// This is means safe operations might fail, but only if you use the unsafe
    /// marked function `upgrade` on a `OutputHandle`.
    liveliness: Option<Rc<AtomicBool>>,
    /// The output_layout ptr that refers to this `OutputLayout`
    layout: *mut wlr_output_layout,
    cursors: Vec<Cursor>
}

/// A handle to an `OutputLayout`.
///
/// Used internally by `Output` to gain access to the `OutputLayout`.
#[derive(Debug, Clone)]
pub(crate) struct OutputLayoutHandle {
    /// The Rc that ensures that this handle is still alive.
    ///
    /// When wlroots deallocates the pointer associated with this handle,
    /// this can no longer be used.
    handle: Weak<AtomicBool>,
    /// The output_layout ptr that refers to this `OutputLayout`
    layout: *mut wlr_output_layout
}

/// The coordinate information of an `Output` within an `OutputLayout`.
#[derive(Debug)]
// TODO Remove pub?
pub struct OutputLayoutOutput<'output> {
    layout_output: *mut wlr_output_layout_output,
    phantom: PhantomData<&'output OutputLayout>
}

impl OutputLayout {
    /// Construct a new OuputLayout.
    pub fn new() -> Option<Self> {
        unsafe {
            let layout = wlr_output_layout_create();
            if layout.is_null() {
                None
            } else {
                Some(OutputLayout { liveliness: Some(Rc::new(AtomicBool::new(false))),
                                    layout,
                                    cursors: Vec::new() })
            }
        }
    }

    /// Get the outputs associated with this OutputLayout.
    ///
    /// Also returns their absolute position within the layout.
    pub fn outputs(&mut self) -> Vec<(OutputHandle, Origin)> {
        unsafe {
            let mut result = vec![];
            wl_list_for_each!((*self.layout).outputs, link, (pos: wlr_output_layout_output) => {
                result.push((OutputHandle::from_ptr((*pos).output),
                             Origin::new((*pos).x, (*pos).y)))
            });
            result
        }
    }

    /// Get the Outputs in the OutputLayout coupled with their output information.
    ///
    /// For a version that isn't bound by lifetimes, see `outputs`.
    pub fn outputs_layouts<'output>(&'output mut self) -> Vec<OutputLayoutOutput<'output>> {
        unsafe {
            let mut result = vec![];
            wl_list_for_each!((*self.layout).outputs, link,
                              (layout_output: wlr_output_layout_output) => {
                                  result.push(OutputLayoutOutput { layout_output,
                                                                   phantom: PhantomData
                                  })
                              });
            result
        }
    }

    pub fn cursors(&mut self) -> &mut [Cursor] {
        self.cursors.as_mut_slice()
    }

    /// Attach a cursor to this OutputLayout.
    pub fn attach_cursor(&mut self, cursor: CursorBuilder) {
        unsafe {
            let cursor = cursor.build(self.weak_reference());
            wlr_cursor_attach_output_layout(cursor.as_ptr(), self.layout);
            self.cursors.push(cursor);
        }
    }

    /// Adds an output to the layout at the given coordinates.
    pub fn add(&mut self, output: &mut Output, origin: Origin) {
        let (x, y) = (origin.x, origin.y);
        unsafe { wlr_output_layout_add(self.layout, output.as_ptr(), x, y) }
    }

    /// Adds an output to the layout, automatically positioning it with
    /// the others that are already there.
    pub fn add_auto(&mut self, output: &mut Output) {
        unsafe {
            let layout_handle = self.weak_reference();
            output.set_output_layout(Some(layout_handle));
            wlr_output_layout_add_auto(self.layout, output.as_ptr());
            wlr_log!(L_DEBUG, "Added {:?} to {:?}", output, self);
        }
    }

    /// Moves the output to the given coordinates.
    ///
    /// If the output is not part of this layout this does nothing.
    pub fn move_output(&mut self, output: &mut Output, origin: Origin) {
        let (x, y) = (origin.x, origin.y);
        unsafe { wlr_output_layout_move(self.layout, output.as_ptr(), x, y) }
    }

    /// Remove an output from this layout.
    ///
    /// If the output was not in the layout, does nothing.
    pub fn remove(&mut self, output: &mut Output) {
        wlr_log!(L_DEBUG, "Removing {:?} from {:?}", output, self);
        unsafe {
            output.clear_output_layout_data();
            wlr_output_layout_remove(self.layout, output.as_ptr());
        };
    }

    /// Creates a weak reference to an `OutputLayout`.
    ///
    /// # Panics
    /// If this `OutputLayout` is a previously upgraded `OutputLayoutHandle`,
    /// then this function will panic.
    pub(crate) fn weak_reference(&self) -> OutputLayoutHandle {
        let arc = self.liveliness.as_ref()
                      .expect("Cannot downgrade a previously upgraded OutputLayoutHandle");
        OutputLayoutHandle { handle: Rc::downgrade(arc),
                             layout: self.layout }
    }

    unsafe fn from_handle(handle: &OutputLayoutHandle) -> Self {
        OutputLayout { liveliness: None,
                       layout: handle.as_ptr(),
                       cursors: Vec::new() }
    }
}

impl Drop for OutputLayout {
    fn drop(&mut self) {
        match self.liveliness {
            None => {}
            Some(ref liveliness) => {
                if Rc::strong_count(liveliness) == 1 {
                    unsafe { wlr_output_layout_destroy(self.layout) }
                    wlr_log!(L_DEBUG, "Dropped {:?}", self);
                    let weak_count = Rc::weak_count(liveliness);
                    if weak_count > 0 {
                        wlr_log!(L_DEBUG,
                                 "Still {} weak pointers to OutputLayout {:?}",
                                 weak_count,
                                 self.layout);
                    }
                }
            }
        }
    }
}

impl OutputLayoutHandle {
    /// Upgrades the `OutputLayoutHandle` to a reference
    /// to the backing `OutputLayout`.
    ///
    /// # Unsafety
    /// This function is unsafe, because it creates an unbound `OutputLayout`
    /// which may live forever..
    /// But the actual lifetime of `OutputLayout` is determined by the user.
    pub(crate) unsafe fn upgrade(&self) -> UpgradeHandleResult<OutputLayout> {
        self.handle.upgrade()
            .ok_or(UpgradeHandleErr::AlreadyDropped)
            // NOTE
            // We drop the Rc here because having two would allow a dangling
            // pointer to exist!
            .and_then(|check| {
                let output_layout = OutputLayout::from_handle(self);
                if check.load(Ordering::Acquire) {
                    return Err(UpgradeHandleErr::AlreadyBorrowed)
                }
                check.store(true, Ordering::Release);
                Ok(output_layout)
            })
    }

    /// Run a function on the referenced OutputLayout, if it still exists
    ///
    /// Returns the result of the function, if successful.
    ///
    /// # Safety
    /// By enforcing a rather harsh limit on the lifetime of the OutputLayout
    /// to a short lived scope of an anonymous function,
    /// this function ensures the OutputLayout does not live longer
    /// than it exists (because the lifetime is controlled by the user).
    pub fn run<F, R>(&mut self, runner: F) -> UpgradeHandleResult<R>
        where F: FnOnce(&mut OutputLayout) -> R
    {
        let mut output_layout = unsafe { self.upgrade()? };
        let res = panic::catch_unwind(panic::AssertUnwindSafe(|| runner(&mut output_layout)));
        self.handle.upgrade().map(|check| {
                                      // Sanity check that it hasn't been tampered with.
                                      if !check.load(Ordering::Acquire) {
                                          wlr_log!(L_ERROR,
                                                   "After running OutputLayout callback, mutable \
                                                    lock was false for: {:?}",
                                                   output_layout);
                                          panic!("Lock in incorrect state!");
                                      }
                                      check.store(false, Ordering::Release);
                                  });
        match res {
            Ok(res) => Ok(res),
            Err(err) => panic::resume_unwind(err)
        }
    }

    unsafe fn as_ptr(&self) -> *mut wlr_output_layout {
        self.layout
    }
}

impl <'output> OutputLayoutOutput<'output> {
    /// Get the absolute top left edge coordinate of this output in the output
    /// layout.
    pub fn top_left_edge(&self) -> Origin {
        unsafe { Origin::new((*self.layout_output).x, (*self.layout_output).y) }
    }

    /// Get the absolute top right edge coordinate of this output in the output
    /// layout.
    pub fn top_right_edge(&self) -> Origin {
        unsafe {
            let (mut width, mut _height) = (0, 0);
            wlr_output_effective_resolution((*self.layout_output).output, &mut width, &mut _height);
            let (x, y) = ((*self.layout_output).x, (*self.layout_output).y);
            Origin::new(x + width, y)
        }
    }

    pub fn bottom_left_edge(&self) -> Origin {
        unsafe {
            let (mut _width, mut height) = (0, 0);
            wlr_output_effective_resolution((*self.layout_output).output, &mut _width, &mut height);
            let (x, y) = ((*self.layout_output).x, (*self.layout_output).y);
            Origin::new(x, y + height)
        }
    }

    pub fn bottom_right_edge(&self) -> Origin {
        unsafe {
            let (mut width, mut height) = (0, 0);
            wlr_output_effective_resolution((*self.layout_output).output, &mut width, &mut height);
            let (x, y) = ((*self.layout_output).x, (*self.layout_output).y);
            Origin::new(x + height, y + height)
        }
    }
}

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

impl Eq for OutputLayoutHandle {}
<commit_msg>Removed old todo<commit_after>//! TODO Documentation

use std::panic;
use std::rc::{Rc, Weak};
use std::sync::atomic::{AtomicBool, Ordering};
use std::marker::PhantomData;

use wlroots_sys::{wlr_cursor_attach_output_layout, wlr_output_effective_resolution,
                  wlr_output_layout, wlr_output_layout_add, wlr_output_layout_add_auto,
                  wlr_output_layout_create, wlr_output_layout_destroy, wlr_output_layout_move,
                  wlr_output_layout_output, wlr_output_layout_remove};

use errors::{UpgradeHandleErr, UpgradeHandleResult};

use {Cursor, CursorBuilder, Origin, Output, OutputHandle};

#[derive(Debug)]
pub struct OutputLayout {
    /// The structure that ensures weak handles to this structure are still alive.
    ///
    /// They contain weak handles, and will safely not use dead memory when this
    /// is freed by wlroots.
    ///
    /// If this is `None`, then this is from an upgraded `OutputHandle`, and
    /// the operations are **unchecked**.
    /// This is means safe operations might fail, but only if you use the unsafe
    /// marked function `upgrade` on a `OutputHandle`.
    liveliness: Option<Rc<AtomicBool>>,
    /// The output_layout ptr that refers to this `OutputLayout`
    layout: *mut wlr_output_layout,
    cursors: Vec<Cursor>
}

/// A handle to an `OutputLayout`.
///
/// Used internally by `Output` to gain access to the `OutputLayout`.
#[derive(Debug, Clone)]
pub(crate) struct OutputLayoutHandle {
    /// The Rc that ensures that this handle is still alive.
    ///
    /// When wlroots deallocates the pointer associated with this handle,
    /// this can no longer be used.
    handle: Weak<AtomicBool>,
    /// The output_layout ptr that refers to this `OutputLayout`
    layout: *mut wlr_output_layout
}

/// The coordinate information of an `Output` within an `OutputLayout`.
#[derive(Debug)]
pub struct OutputLayoutOutput<'output> {
    layout_output: *mut wlr_output_layout_output,
    phantom: PhantomData<&'output OutputLayout>
}

impl OutputLayout {
    /// Construct a new OuputLayout.
    pub fn new() -> Option<Self> {
        unsafe {
            let layout = wlr_output_layout_create();
            if layout.is_null() {
                None
            } else {
                Some(OutputLayout { liveliness: Some(Rc::new(AtomicBool::new(false))),
                                    layout,
                                    cursors: Vec::new() })
            }
        }
    }

    /// Get the outputs associated with this OutputLayout.
    ///
    /// Also returns their absolute position within the layout.
    pub fn outputs(&mut self) -> Vec<(OutputHandle, Origin)> {
        unsafe {
            let mut result = vec![];
            wl_list_for_each!((*self.layout).outputs, link, (pos: wlr_output_layout_output) => {
                result.push((OutputHandle::from_ptr((*pos).output),
                             Origin::new((*pos).x, (*pos).y)))
            });
            result
        }
    }

    /// Get the Outputs in the OutputLayout coupled with their output information.
    ///
    /// For a version that isn't bound by lifetimes, see `outputs`.
    pub fn outputs_layouts<'output>(&'output mut self) -> Vec<OutputLayoutOutput<'output>> {
        unsafe {
            let mut result = vec![];
            wl_list_for_each!((*self.layout).outputs, link,
                              (layout_output: wlr_output_layout_output) => {
                                  result.push(OutputLayoutOutput { layout_output,
                                                                   phantom: PhantomData
                                  })
                              });
            result
        }
    }

    pub fn cursors(&mut self) -> &mut [Cursor] {
        self.cursors.as_mut_slice()
    }

    /// Attach a cursor to this OutputLayout.
    pub fn attach_cursor(&mut self, cursor: CursorBuilder) {
        unsafe {
            let cursor = cursor.build(self.weak_reference());
            wlr_cursor_attach_output_layout(cursor.as_ptr(), self.layout);
            self.cursors.push(cursor);
        }
    }

    /// Adds an output to the layout at the given coordinates.
    pub fn add(&mut self, output: &mut Output, origin: Origin) {
        let (x, y) = (origin.x, origin.y);
        unsafe { wlr_output_layout_add(self.layout, output.as_ptr(), x, y) }
    }

    /// Adds an output to the layout, automatically positioning it with
    /// the others that are already there.
    pub fn add_auto(&mut self, output: &mut Output) {
        unsafe {
            let layout_handle = self.weak_reference();
            output.set_output_layout(Some(layout_handle));
            wlr_output_layout_add_auto(self.layout, output.as_ptr());
            wlr_log!(L_DEBUG, "Added {:?} to {:?}", output, self);
        }
    }

    /// Moves the output to the given coordinates.
    ///
    /// If the output is not part of this layout this does nothing.
    pub fn move_output(&mut self, output: &mut Output, origin: Origin) {
        let (x, y) = (origin.x, origin.y);
        unsafe { wlr_output_layout_move(self.layout, output.as_ptr(), x, y) }
    }

    /// Remove an output from this layout.
    ///
    /// If the output was not in the layout, does nothing.
    pub fn remove(&mut self, output: &mut Output) {
        wlr_log!(L_DEBUG, "Removing {:?} from {:?}", output, self);
        unsafe {
            output.clear_output_layout_data();
            wlr_output_layout_remove(self.layout, output.as_ptr());
        };
    }

    /// Creates a weak reference to an `OutputLayout`.
    ///
    /// # Panics
    /// If this `OutputLayout` is a previously upgraded `OutputLayoutHandle`,
    /// then this function will panic.
    pub(crate) fn weak_reference(&self) -> OutputLayoutHandle {
        let arc = self.liveliness.as_ref()
                      .expect("Cannot downgrade a previously upgraded OutputLayoutHandle");
        OutputLayoutHandle { handle: Rc::downgrade(arc),
                             layout: self.layout }
    }

    unsafe fn from_handle(handle: &OutputLayoutHandle) -> Self {
        OutputLayout { liveliness: None,
                       layout: handle.as_ptr(),
                       cursors: Vec::new() }
    }
}

impl Drop for OutputLayout {
    fn drop(&mut self) {
        match self.liveliness {
            None => {}
            Some(ref liveliness) => {
                if Rc::strong_count(liveliness) == 1 {
                    unsafe { wlr_output_layout_destroy(self.layout) }
                    wlr_log!(L_DEBUG, "Dropped {:?}", self);
                    let weak_count = Rc::weak_count(liveliness);
                    if weak_count > 0 {
                        wlr_log!(L_DEBUG,
                                 "Still {} weak pointers to OutputLayout {:?}",
                                 weak_count,
                                 self.layout);
                    }
                }
            }
        }
    }
}

impl OutputLayoutHandle {
    /// Upgrades the `OutputLayoutHandle` to a reference
    /// to the backing `OutputLayout`.
    ///
    /// # Unsafety
    /// This function is unsafe, because it creates an unbound `OutputLayout`
    /// which may live forever..
    /// But the actual lifetime of `OutputLayout` is determined by the user.
    pub(crate) unsafe fn upgrade(&self) -> UpgradeHandleResult<OutputLayout> {
        self.handle.upgrade()
            .ok_or(UpgradeHandleErr::AlreadyDropped)
            // NOTE
            // We drop the Rc here because having two would allow a dangling
            // pointer to exist!
            .and_then(|check| {
                let output_layout = OutputLayout::from_handle(self);
                if check.load(Ordering::Acquire) {
                    return Err(UpgradeHandleErr::AlreadyBorrowed)
                }
                check.store(true, Ordering::Release);
                Ok(output_layout)
            })
    }

    /// Run a function on the referenced OutputLayout, if it still exists
    ///
    /// Returns the result of the function, if successful.
    ///
    /// # Safety
    /// By enforcing a rather harsh limit on the lifetime of the OutputLayout
    /// to a short lived scope of an anonymous function,
    /// this function ensures the OutputLayout does not live longer
    /// than it exists (because the lifetime is controlled by the user).
    pub fn run<F, R>(&mut self, runner: F) -> UpgradeHandleResult<R>
        where F: FnOnce(&mut OutputLayout) -> R
    {
        let mut output_layout = unsafe { self.upgrade()? };
        let res = panic::catch_unwind(panic::AssertUnwindSafe(|| runner(&mut output_layout)));
        self.handle.upgrade().map(|check| {
                                      // Sanity check that it hasn't been tampered with.
                                      if !check.load(Ordering::Acquire) {
                                          wlr_log!(L_ERROR,
                                                   "After running OutputLayout callback, mutable \
                                                    lock was false for: {:?}",
                                                   output_layout);
                                          panic!("Lock in incorrect state!");
                                      }
                                      check.store(false, Ordering::Release);
                                  });
        match res {
            Ok(res) => Ok(res),
            Err(err) => panic::resume_unwind(err)
        }
    }

    unsafe fn as_ptr(&self) -> *mut wlr_output_layout {
        self.layout
    }
}

impl <'output> OutputLayoutOutput<'output> {
    /// Get the absolute top left edge coordinate of this output in the output
    /// layout.
    pub fn top_left_edge(&self) -> Origin {
        unsafe { Origin::new((*self.layout_output).x, (*self.layout_output).y) }
    }

    /// Get the absolute top right edge coordinate of this output in the output
    /// layout.
    pub fn top_right_edge(&self) -> Origin {
        unsafe {
            let (mut width, mut _height) = (0, 0);
            wlr_output_effective_resolution((*self.layout_output).output, &mut width, &mut _height);
            let (x, y) = ((*self.layout_output).x, (*self.layout_output).y);
            Origin::new(x + width, y)
        }
    }

    pub fn bottom_left_edge(&self) -> Origin {
        unsafe {
            let (mut _width, mut height) = (0, 0);
            wlr_output_effective_resolution((*self.layout_output).output, &mut _width, &mut height);
            let (x, y) = ((*self.layout_output).x, (*self.layout_output).y);
            Origin::new(x, y + height)
        }
    }

    pub fn bottom_right_edge(&self) -> Origin {
        unsafe {
            let (mut width, mut height) = (0, 0);
            wlr_output_effective_resolution((*self.layout_output).output, &mut width, &mut height);
            let (x, y) = ((*self.layout_output).x, (*self.layout_output).y);
            Origin::new(x + height, y + height)
        }
    }
}

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

impl Eq for OutputLayoutHandle {}
<|endoftext|>


<commit_before>use std::env;
use std::path::PathBuf;
use std::fs;

pub struct EnvironmentUtils {}

impl EnvironmentUtils {
    pub fn indy_home_path() -> PathBuf {
        // TODO: FIXME: Provide better handling for the unknown home path case!!!

        let mut path = env::home_dir().unwrap_or(PathBuf::from("/home/indy"));
        let mut indy_client_dir = ".indy_client";
        if cfg!(target_os = "ios"){
            indy_client_dir = "Documents/.indy_client";
        }
        path.push(indy_client_dir);

        if cfg!(target_os = "android"){
            let android_dir = env::var("EXTERNAL_STORAGE").unwrap() + "/.indy_client";
            fs::create_dir_all(&android_dir).unwrap();
            path = PathBuf::from(android_dir);
        }
        path
    }

    pub fn wallet_home_path() -> PathBuf {
        let mut path = EnvironmentUtils::indy_home_path();
        path.push("wallet");
        path
    }

    pub fn wallet_path(wallet_name: &str) -> PathBuf {
        let mut path = EnvironmentUtils::wallet_home_path();
        path.push(wallet_name);
        path
    }

    pub fn pool_home_path() -> PathBuf {
        let mut path = EnvironmentUtils::indy_home_path();
        path.push("pool");
        path
    }

    pub fn pool_path(pool_name: &str) -> PathBuf {
        let mut path = EnvironmentUtils::pool_home_path();
        path.push(pool_name);
        path
    }

    pub fn tmp_path() -> PathBuf {
        let mut path = env::temp_dir();
        path.push("indy_client");
        path
    }

    pub fn tmp_file_path(file_name: &str) -> PathBuf {
        let mut path = EnvironmentUtils::tmp_path();
        path.push(file_name);
        path
    }

    #[cfg(test)]
    pub fn test_pool_ip() -> String {
        env::var("TEST_POOL_IP").unwrap_or("127.0.0.1".to_string())
    }
}

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

    #[test]
    fn indy_home_path_works() {
        let path = EnvironmentUtils::indy_home_path();

        assert!(path.is_absolute());
        assert!(path.has_root());
        assert!(path.to_string_lossy().contains(".indy_client"));
    }

    #[test]
    fn wallet_home_path_works() {
        let path = EnvironmentUtils::wallet_home_path();

        assert!(path.is_absolute());
        assert!(path.has_root());
        assert!(path.to_string_lossy().contains(".indy_client"));
        assert!(path.to_string_lossy().contains("wallet"));
    }

    #[test]
    fn wallet_path_works() {
        let path = EnvironmentUtils::wallet_path("wallet1");

        assert!(path.is_absolute());
        assert!(path.has_root());
        assert!(path.to_string_lossy().contains(".indy_client"));
        assert!(path.to_string_lossy().contains("wallet1"));
    }

    #[test]
    fn pool_home_path_works() {
        let path = EnvironmentUtils::pool_home_path();

        assert!(path.is_absolute());
        assert!(path.has_root());
        assert!(path.to_string_lossy().contains(".indy_client"));
        assert!(path.to_string_lossy().contains("pool"));
    }

    #[test]
    fn pool_path_works() {
        let path = EnvironmentUtils::pool_path("pool1");

        assert!(path.is_absolute());
        assert!(path.has_root());
        assert!(path.to_string_lossy().contains(".indy_client"));
        assert!(path.to_string_lossy().contains("pool1"));
    }

    #[test]
    fn tmp_path_works() {
        let path = EnvironmentUtils::tmp_path();

        assert!(path.is_absolute());
        assert!(path.has_root());
        assert!(path.to_string_lossy().contains("indy_client"));
    }

    #[test]
    fn tmp_file_path_works() {
        let path = EnvironmentUtils::tmp_file_path("test.txt");

        assert!(path.is_absolute());
        assert!(path.has_root());
        assert!(path.to_string_lossy().contains("indy_client"));
        assert!(path.to_string_lossy().contains("test.txt"));
    }

    #[test]
    fn test_pool_ip_works() {
        let pool_ip = EnvironmentUtils::test_pool_ip();
        assert!(!pool_ip.is_empty());
    }
}<commit_msg>Moved the logic of creating the directory in android environment to another function<commit_after>use std::env;
use std::path::PathBuf;
use std::fs;

pub struct EnvironmentUtils {}

impl EnvironmentUtils {
    pub fn indy_home_path() -> PathBuf {
        // TODO: FIXME: Provide better handling for the unknown home path case!!!

        let mut path = env::home_dir().unwrap_or(PathBuf::from("/home/indy"));
        let mut indy_client_dir = ".indy_client";
        if cfg!(target_os = "ios"){
            indy_client_dir = "Documents/.indy_client";
        }
        path.push(indy_client_dir);

        if cfg!(target_os = "android"){
            EnvironmentUtils::android_create_indy_client_dir();
            path = EnvironmentUtils::android_indy_client_dir_path();
        }
        path
    }

    pub fn android_indy_client_dir_path() -> PathBuf{
        let android_dir = env::var("EXTERNAL_STORAGE").unwrap() + "/.indy_client";
        PathBuf::from(android_dir)
    }

    pub fn android_create_indy_client_dir(){
        //Creates directory only if it is not present.
        fs::create_dir_all(EnvironmentUtils::android_indy_client_dir_path().as_path()).unwrap();
    }

    pub fn wallet_home_path() -> PathBuf {
        let mut path = EnvironmentUtils::indy_home_path();
        path.push("wallet");
        path
    }

    pub fn wallet_path(wallet_name: &str) -> PathBuf {
        let mut path = EnvironmentUtils::wallet_home_path();
        path.push(wallet_name);
        path
    }

    pub fn pool_home_path() -> PathBuf {
        let mut path = EnvironmentUtils::indy_home_path();
        path.push("pool");
        path
    }

    pub fn pool_path(pool_name: &str) -> PathBuf {
        let mut path = EnvironmentUtils::pool_home_path();
        path.push(pool_name);
        path
    }

    pub fn tmp_path() -> PathBuf {
        let mut path = env::temp_dir();
        path.push("indy_client");
        path
    }

    pub fn tmp_file_path(file_name: &str) -> PathBuf {
        let mut path = EnvironmentUtils::tmp_path();
        path.push(file_name);
        path
    }

    #[cfg(test)]
    pub fn test_pool_ip() -> String {
        env::var("TEST_POOL_IP").unwrap_or("127.0.0.1".to_string())
    }
}

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

    #[test]
    fn indy_home_path_works() {
        let path = EnvironmentUtils::indy_home_path();

        assert!(path.is_absolute());
        assert!(path.has_root());
        assert!(path.to_string_lossy().contains(".indy_client"));
    }

    #[test]
    fn wallet_home_path_works() {
        let path = EnvironmentUtils::wallet_home_path();

        assert!(path.is_absolute());
        assert!(path.has_root());
        assert!(path.to_string_lossy().contains(".indy_client"));
        assert!(path.to_string_lossy().contains("wallet"));
    }

    #[test]
    fn wallet_path_works() {
        let path = EnvironmentUtils::wallet_path("wallet1");

        assert!(path.is_absolute());
        assert!(path.has_root());
        assert!(path.to_string_lossy().contains(".indy_client"));
        assert!(path.to_string_lossy().contains("wallet1"));
    }

    #[test]
    fn pool_home_path_works() {
        let path = EnvironmentUtils::pool_home_path();

        assert!(path.is_absolute());
        assert!(path.has_root());
        assert!(path.to_string_lossy().contains(".indy_client"));
        assert!(path.to_string_lossy().contains("pool"));
    }

    #[test]
    fn pool_path_works() {
        let path = EnvironmentUtils::pool_path("pool1");

        assert!(path.is_absolute());
        assert!(path.has_root());
        assert!(path.to_string_lossy().contains(".indy_client"));
        assert!(path.to_string_lossy().contains("pool1"));
    }

    #[test]
    fn tmp_path_works() {
        let path = EnvironmentUtils::tmp_path();

        assert!(path.is_absolute());
        assert!(path.has_root());
        assert!(path.to_string_lossy().contains("indy_client"));
    }

    #[test]
    fn tmp_file_path_works() {
        let path = EnvironmentUtils::tmp_file_path("test.txt");

        assert!(path.is_absolute());
        assert!(path.has_root());
        assert!(path.to_string_lossy().contains("indy_client"));
        assert!(path.to_string_lossy().contains("test.txt"));
    }

    #[test]
    fn test_pool_ip_works() {
        let pool_ip = EnvironmentUtils::test_pool_ip();
        assert!(!pool_ip.is_empty());
    }
}<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::rc::Rc;
use std::mem;
use slsr_core::geom::{CellId, Geom, Move};
use slsr_core::puzzle::{Edge, Puzzle};

use {Error, State, SolverResult};
use model::side_map::SideMap;
use model::theorem::{Pattern, Theorem, TheoremMatcher};

#[derive(Clone, Debug)]
struct TheoremCount {
    rest_count: usize,
    result: Option<Rc<Vec<(Edge, (CellId, CellId))>>>,
}

impl From<TheoremMatcher> for TheoremCount {
    fn from(matcher: TheoremMatcher) -> TheoremCount {
        TheoremCount {
            rest_count: matcher.num_matcher(),
            result: Some(Rc::new(matcher.result_edges().collect())),
        }
    }
}

impl TheoremCount {
    fn invalidate(&mut self) {
        self.rest_count = 0;
        self.result = None;
    }

    fn update(&mut self, side_map: &mut SideMap) {
        match self.rest_count {
            0 => {
                return;
            }
            1 => {
                self.rest_count = 0;
                for &(edge, points) in &*self.result.take().unwrap() {
                    let _ = side_map.set_edge(points.0, points.1, edge);
                }
            }
            _ => {
                self.rest_count -= 1;
            }
        }
    }
}

#[derive(Clone, Debug)]
struct IndexByEdge {
    points: (CellId, CellId),
    expect_line: Vec<usize>,
    expect_cross: Vec<usize>,
}

impl IndexByEdge {
    fn new(points: (CellId, CellId),
           expect_line: Vec<usize>,
           expect_cross: Vec<usize>)
           -> IndexByEdge {
        IndexByEdge {
            points: points,
            expect_line: expect_line,
            expect_cross: expect_cross,
        }
    }
}

#[derive(Debug)]
pub struct TheoremPool {
    matchers: Vec<TheoremCount>,
    index_by_edge: Vec<Rc<IndexByEdge>>,
}

impl Clone for TheoremPool {
    fn clone(&self) -> TheoremPool {
        TheoremPool {
            matchers: self.matchers.clone(),
            index_by_edge: self.index_by_edge.clone(),
        }
    }

    fn clone_from(&mut self, other: &TheoremPool) {
        self.matchers.clone_from(&other.matchers);
        self.index_by_edge.clone_from(&other.index_by_edge);
    }
}

impl TheoremPool {
    pub fn new<'a, T>(theo_defs: T,
                      puzzle: &Puzzle,
                      sum_of_hint: u32,
                      side_map: &mut SideMap)
                      -> SolverResult<TheoremPool>
        where T: IntoIterator<Item = Theorem>
    {
        let mut matchers = try!(create_matcher_list(theo_defs, puzzle, sum_of_hint, side_map));

        loop {
            let rev = side_map.revision();

            try!(apply_all_theorem(&mut matchers, side_map));
            if side_map.revision() != rev {
                continue;
            }

            break;
        }

        merge_duplicate_matchers(&mut matchers);

        let mut map = HashMap::new();
        for (i, m) in matchers.iter().enumerate() {
            for (edge, points) in m.matcher_edges() {
                let e = map.entry(points).or_insert((vec![], vec![]));
                match edge {
                    Edge::Line => e.0.push(i),
                    Edge::Cross => e.1.push(i),
                }
            }
        }

        let matchers = matchers.into_iter().map(From::from).collect();
        let edges = map.into_iter()
                       .map(|(points, ex)| IndexByEdge::new(points, ex.0, ex.1))
                       .map(Rc::new)
                       .collect();

        Ok(TheoremPool {
            matchers: matchers,
            index_by_edge: edges,
        })
    }

    pub fn apply_all(&mut self, side_map: &mut SideMap) -> SolverResult<()> {
        unsafe {
            let ptr = self.index_by_edge.as_mut_ptr();

            let mut w = 0;
            for r in 0..self.index_by_edge.len() {
                let read = ptr.offset(r as isize);
                let ibe: &IndexByEdge = &*read;

                match side_map.get_edge(ibe.points.0, ibe.points.1) {
                    State::Fixed(Edge::Cross) => {
                        for &i in &ibe.expect_line {
                            self.matchers[i].invalidate();
                        }
                        for &i in &ibe.expect_cross {
                            self.matchers[i].update(side_map);
                        }
                    }
                    State::Fixed(Edge::Line) => {
                        for &i in &ibe.expect_line {
                            self.matchers[i].update(side_map);
                        }
                        for &i in &ibe.expect_cross {
                            self.matchers[i].invalidate();
                        }
                    }
                    State::Unknown => {
                        let write = ptr.offset(w as isize);
                        mem::swap(&mut *write, &mut *read);
                        w += 1;
                    }
                    State::Conflict => {
                        return Err(Error::invalid_board());
                    }
                }
            }

            self.index_by_edge.truncate(w);
        }

        Ok(())
    }
}

fn create_matcher_list<'a, T>(theo_defs: T,
                              puzzle: &Puzzle,
                              sum_of_hint: u32,
                              side_map: &mut SideMap)
                              -> SolverResult<Vec<TheoremMatcher>>
    where T: IntoIterator<Item = Theorem>
{
    let it = theo_defs.into_iter().flat_map(|theo| theo.all_rotations());

    let mut hint_theorem = [vec![], vec![], vec![], vec![], vec![]];
    let mut nonhint_theorem = vec![];

    for theo in it {
        match theo.head() {
            Pattern::Hint(h) => hint_theorem[h.hint() as usize].push(theo),
            _ => nonhint_theorem.push(theo),
        }
    }

    let mut data = vec![];

    for p in puzzle.points() {
        if let Some(x) = puzzle.hint(p) {
            for theo in &hint_theorem[x as usize] {
                let o = match theo.head() {
                    Pattern::Hint(hint) => hint.point(),
                    _ => panic!(),
                };
                let matcher = theo.clone().shift(p - o);
                try!(matcher.matches(puzzle, sum_of_hint, side_map)).update(side_map, &mut data);
            }
        }
    }

    for theo in nonhint_theorem {
        let sz = theo.size();
        for r in (1 - sz.0)..(puzzle.row() + sz.0 - 1) {
            for c in (1 - sz.1)..(puzzle.column() + sz.1 - 1) {
                let matcher = theo.clone().shift(Move(r, c));
                try!(matcher.matches(puzzle, sum_of_hint, side_map)).update(side_map, &mut data);
            }
        }
    }

    Ok(data)
}

fn apply_all_theorem(matchers: &mut Vec<TheoremMatcher>,
                     side_map: &mut SideMap)
                     -> SolverResult<()> {
    let cap = matchers.len();

    for m in mem::replace(matchers, Vec::with_capacity(cap)) {
        try!(m.matches(side_map)).update(side_map, matchers);
    }

    Ok(())
}

fn merge_duplicate_matchers(matchers: &mut Vec<TheoremMatcher>) {
    matchers.sort();

    // Merge elements that have same matchers.
    unsafe {
        let ptr = matchers.as_mut_ptr();

        let mut w = 1;
        for r in 1..matchers.len() {
            let read = ptr.offset(r as isize);
            let cmp = ptr.offset((w - 1) as isize);

            match (*cmp).merge(&*read) {
                Ok(()) => {}
                Err(()) => {
                    let write = cmp.offset(1);
                    mem::swap(&mut *write, &mut *read);
                    w += 1;
                }
            }
        }

        matchers.truncate(w);
    }
}
<commit_msg>solver: reduce copies of vector of vector of Rc<commit_after>use std::collections::HashMap;
use std::rc::Rc;
use std::mem;
use slsr_core::geom::{CellId, Geom, Move};
use slsr_core::puzzle::{Edge, Puzzle};

use {Error, State, SolverResult};
use model::side_map::SideMap;
use model::theorem::{Pattern, Theorem, TheoremMatcher};

#[derive(Clone, Debug)]
struct IndexByEdge {
    points: (CellId, CellId),
    expect_line: Vec<usize>,
    expect_cross: Vec<usize>,
}

#[derive(Debug)]
pub struct TheoremPool {
    counts: Vec<usize>,
    matchers: Rc<Vec<Vec<(Edge, (CellId, CellId))>>>,
    index_by_edge: Vec<Rc<IndexByEdge>>,
}

impl Clone for TheoremPool {
    fn clone(&self) -> TheoremPool {
        TheoremPool {
            counts: self.counts.clone(),
            matchers: self.matchers.clone(),
            index_by_edge: self.index_by_edge.clone(),
        }
    }

    fn clone_from(&mut self, other: &TheoremPool) {
        self.counts.clone_from(&other.counts);
        self.matchers.clone_from(&other.matchers);
        self.index_by_edge.clone_from(&other.index_by_edge);
    }
}

impl TheoremPool {
    pub fn new<'a, T>(theo_defs: T,
                      puzzle: &Puzzle,
                      sum_of_hint: u32,
                      side_map: &mut SideMap)
                      -> SolverResult<TheoremPool>
        where T: IntoIterator<Item = Theorem>
    {
        let mut matchers = try!(create_matcher_list(theo_defs, puzzle, sum_of_hint, side_map));

        loop {
            let rev = side_map.revision();

            try!(apply_all_theorem(&mut matchers, side_map));
            if side_map.revision() != rev {
                continue;
            }

            break;
        }

        merge_duplicate_matchers(&mut matchers);

        let mut map = HashMap::new();
        for (i, m) in matchers.iter().enumerate() {
            for (edge, points) in m.matcher_edges() {
                let e = map.entry(points).or_insert((vec![], vec![]));
                match edge {
                    Edge::Line => e.0.push(i),
                    Edge::Cross => e.1.push(i),
                }
            }
        }

        let counts = matchers.iter().map(|matcher| matcher.num_matcher()).collect();
        let matchers = matchers.into_iter()
                               .map(|matcher| matcher.result_edges().collect())
                               .collect();
        let edges = map.into_iter()
                       .map(|(points, ex)| {
                           IndexByEdge {
                               points: points,
                               expect_line: ex.0,
                               expect_cross: ex.1,
                           }
                       })
                       .map(Rc::new)
                       .collect();

        Ok(TheoremPool {
            counts: counts,
            matchers: Rc::new(matchers),
            index_by_edge: edges,
        })
    }

    fn invalidate(&mut self, i: usize) {
        self.counts[i] = 0;
    }

    fn update(&mut self, i: usize, side_map: &mut SideMap) {
        match self.counts[i] {
            0 => {
                return;
            }
            1 => {
                self.counts[i] = 0;
                for &(edge, points) in &self.matchers[i] {
                    let _ = side_map.set_edge(points.0, points.1, edge);
                }
            }
            _ => {
                self.counts[i] -= 1;
            }
        }
    }

    pub fn apply_all(&mut self, side_map: &mut SideMap) -> SolverResult<()> {
        unsafe {
            let ptr = self.index_by_edge.as_mut_ptr();

            let mut w = 0;
            for r in 0..self.index_by_edge.len() {
                let read = ptr.offset(r as isize);
                let ibe: &IndexByEdge = &*read;

                match side_map.get_edge(ibe.points.0, ibe.points.1) {
                    State::Fixed(Edge::Cross) => {
                        for &i in &ibe.expect_line {
                            self.invalidate(i);
                        }
                        for &i in &ibe.expect_cross {
                            self.update(i, side_map);
                        }
                    }
                    State::Fixed(Edge::Line) => {
                        for &i in &ibe.expect_line {
                            self.update(i, side_map);
                        }
                        for &i in &ibe.expect_cross {
                            self.invalidate(i);
                        }
                    }
                    State::Unknown => {
                        let write = ptr.offset(w as isize);
                        mem::swap(&mut *write, &mut *read);
                        w += 1;
                    }
                    State::Conflict => {
                        return Err(Error::invalid_board());
                    }
                }
            }

            self.index_by_edge.truncate(w);
        }

        Ok(())
    }
}

fn create_matcher_list<'a, T>(theo_defs: T,
                              puzzle: &Puzzle,
                              sum_of_hint: u32,
                              side_map: &mut SideMap)
                              -> SolverResult<Vec<TheoremMatcher>>
    where T: IntoIterator<Item = Theorem>
{
    let it = theo_defs.into_iter().flat_map(|theo| theo.all_rotations());

    let mut hint_theorem = [vec![], vec![], vec![], vec![], vec![]];
    let mut nonhint_theorem = vec![];

    for theo in it {
        match theo.head() {
            Pattern::Hint(h) => hint_theorem[h.hint() as usize].push(theo),
            _ => nonhint_theorem.push(theo),
        }
    }

    let mut data = vec![];

    for p in puzzle.points() {
        if let Some(x) = puzzle.hint(p) {
            for theo in &hint_theorem[x as usize] {
                let o = match theo.head() {
                    Pattern::Hint(hint) => hint.point(),
                    _ => panic!(),
                };
                let matcher = theo.clone().shift(p - o);
                try!(matcher.matches(puzzle, sum_of_hint, side_map)).update(side_map, &mut data);
            }
        }
    }

    for theo in nonhint_theorem {
        let sz = theo.size();
        for r in (1 - sz.0)..(puzzle.row() + sz.0 - 1) {
            for c in (1 - sz.1)..(puzzle.column() + sz.1 - 1) {
                let matcher = theo.clone().shift(Move(r, c));
                try!(matcher.matches(puzzle, sum_of_hint, side_map)).update(side_map, &mut data);
            }
        }
    }

    Ok(data)
}

fn apply_all_theorem(matchers: &mut Vec<TheoremMatcher>,
                     side_map: &mut SideMap)
                     -> SolverResult<()> {
    let cap = matchers.len();

    for m in mem::replace(matchers, Vec::with_capacity(cap)) {
        try!(m.matches(side_map)).update(side_map, matchers);
    }

    Ok(())
}

fn merge_duplicate_matchers(matchers: &mut Vec<TheoremMatcher>) {
    matchers.sort();

    // Merge elements that have same matchers.
    unsafe {
        let ptr = matchers.as_mut_ptr();

        let mut w = 1;
        for r in 1..matchers.len() {
            let read = ptr.offset(r as isize);
            let cmp = ptr.offset((w - 1) as isize);

            match (*cmp).merge(&*read) {
                Ok(()) => {}
                Err(()) => {
                    let write = cmp.offset(1);
                    mem::swap(&mut *write, &mut *read);
                    w += 1;
                }
            }
        }

        matchers.truncate(w);
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate env_logger;
#[macro_use]
extern crate log;
extern crate rls_analysis;
extern crate rls_data as data;
extern crate serde;
#[macro_use]
extern crate serde_json;

use data::GlobalCrateId;
use data::DefKind;
use rls_analysis::{AnalysisHost, AnalysisLoader, SearchDirectory};
use std::collections::{BTreeSet, HashMap};
use std::io::{BufRead, BufReader};
use std::fs::{self, File};
use std::path::{Path, PathBuf};

/// A global definition id in a crate.
///
/// FIXME(emilio): This key is kind of slow, because GlobalCrateId contains a
/// String. There's a "disambiguator" field which may be more than enough for
/// our purposes.
#[derive(Clone, Hash, Debug, Eq, PartialEq)]
pub struct DefId(GlobalCrateId, u32);

/// A map from global definition ids to the actual definition.
pub struct Defs {
    map: HashMap<DefId, data::Def>,
}

struct TreeInfo<'a> {
    src_dir: &'a Path,
    output_dir: &'a Path,
    objdir: &'a Path,
}

// Given a definition, and the global crate id where that definition is found,
// return a qualified name that identifies the definition unambiguously.
fn crate_independent_qualname(
    def: &data::Def,
    crate_id: &data::GlobalCrateId,
) -> String {
    // For functions with "no_mangle", we just use the name.
    if def.kind == DefKind::Function &&
        def.attributes.iter().any(|attr| attr.value == "no_mangle")
    {
        return def.name.clone();
    }

    format!("{}{}", crate_id.name, def.qualname)
}

impl Defs {
    fn new() -> Self {
        Self { map: HashMap::new() }
    }

    fn insert(&mut self, analysis: &data::Analysis, def: &data::Def) {
        let crate_id = analysis.prelude.as_ref().unwrap().crate_id.clone();
        let mut definition = def.clone();
        definition.qualname = crate_independent_qualname(&def, &crate_id);

        let index = definition.id.index;
        let previous = self.map.insert(DefId(crate_id, index), definition);
        if let Some(previous) = previous {
            // This shouldn't happen, but as of right now it can happen with
            // some builtin definitions when highly generic types are involved.
            // This is probably a rust bug, just ignore it for now.
            debug!(
                "Found a definition with the same ID twice? {:?}, {:?}",
                previous,
                def,
            );
        }
    }

    /// Getter for a given local id, which takes care of converting to a global
    /// ID and returning the definition if present.
    fn get(&self, analysis: &data::Analysis, id: data::Id) -> Option<data::Def> {
        let prelude = analysis.prelude.as_ref().unwrap();
        let krate_id = if id.krate == 0 {
            prelude.crate_id.clone()
        } else {
            // TODO(emilio): This escales with the number of crates in this
            // particular crate, but it's probably not too bad, since it should
            // be a pretty fast linear search.
            let krate = prelude.external_crates.iter().find(|krate| {
                krate.num == id.krate
            });

            let krate = match krate {
                Some(k) => k,
                None => {
                    debug!("Crate not found: {:?}", id);
                    return None;
                }
            };

            krate.id.clone()
        };

        let id = DefId(krate_id, id.index);
        self.map.get(&id).cloned()
    }
}

#[derive(Clone)]
pub struct Loader {
    deps_dirs: Vec<PathBuf>,
}

impl Loader {
    pub fn new(deps_dirs: Vec<PathBuf>) -> Self {
        Self { deps_dirs }
    }
}

impl AnalysisLoader for Loader {
    fn needs_hard_reload(&self, _: &Path) -> bool {
        true
    }

    fn fresh_host(&self) -> AnalysisHost<Self> {
        AnalysisHost::new_with_loader(self.clone())
    }

    fn set_path_prefix(&mut self, _: &Path) {}

    fn abs_path_prefix(&self) -> Option<PathBuf> {
        None
    }
    fn search_directories(&self) -> Vec<SearchDirectory> {
        self.deps_dirs.iter().map(|pb| {
            SearchDirectory {
                path: pb.clone(),
                prefix_rewrite: None,
            }
        }).collect()
    }
}

// Searchfox uses 1-indexed lines, 0-indexed columns.
fn span_to_string(span: &data::SpanData) -> String {
    // Rust spans are multi-line... So we just set the start column if it spans
    // multiple rows, searchfox has fallback code to handle this.
    if span.line_start != span.line_end {
        return format!("{}:{}", span.line_start.0, span.column_start.0 - 1);
    }
    if span.column_start == span.column_end {
        return format!("{}:{}", span.line_start.0, span.column_start.0 - 1);
    }
    format!("{}:{}-{}", span.line_start.0, span.column_start.zero_indexed().0, span.column_end.zero_indexed().0)
}

fn def_kind_to_human(kind: DefKind) -> &'static str {
    match kind {
        DefKind::Enum => "enum",
        DefKind::Local => "local",
        DefKind::ExternType => "extern type",
        DefKind::Const => "constant",
        DefKind::Field => "field",
        DefKind::Function | DefKind::ForeignFunction => "function",
        DefKind::Macro => "macro",
        DefKind::Method => "method",
        DefKind::Mod => "module",
        DefKind::Static | DefKind::ForeignStatic => "static",
        DefKind::Struct => "struct",
        DefKind::Tuple => "tuple",
        DefKind::TupleVariant => "tuple variant",
        DefKind::Union => "union",
        DefKind::Type => "type",
        DefKind::Trait => "trait",
        DefKind::StructVariant => "struct variant",
    }
}

fn visit(
    out_data: &mut BTreeSet<String>,
    kind: &'static str,
    location: &data::SpanData,
    qualname: &str,
    def: &data::Def,
    context: Option<&str>,
) {
    use serde_json::map::Map;
    use serde_json::value::Value;

    let mut out = Map::new();
    out.insert("loc".into(), Value::String(span_to_string(location)));
    out.insert("target".into(), json!(1));
    out.insert("kind".into(), Value::String(kind.into()));
    let pretty = {
        let mut pretty = def_kind_to_human(def.kind).to_owned();
        pretty.push_str(" ");
        pretty.push_str(qualname);

        pretty
    };
    out.insert("pretty".into(), Value::String(pretty.clone()));
    out.insert("sym".into(), Value::String(qualname.into()));
    if let Some(context) = context {
        out.insert("context".into(), Value::String(context.into()));
        out.insert("contextsym".into(), Value::String(context.into()));
    }

    let object = serde_json::to_string(&Value::Object(out)).unwrap();
    out_data.insert(object);

    let mut out = Map::new();
    out.insert("loc".into(), Value::String(span_to_string(location)));
    out.insert("source".into(), json!(1));
    out.insert("kind".into(), Value::String(kind.into()));
    out.insert("pretty".into(), Value::String(pretty));
    out.insert("sym".into(), Value::String(qualname.into()));

    let object = serde_json::to_string(&Value::Object(out)).unwrap();
    out_data.insert(object);
}

fn find_generated_or_src_file(
    file_name: &Path,
    tree_info: &TreeInfo,
) -> Option<PathBuf> {
    if let Ok(generated_path) = file_name.strip_prefix(tree_info.objdir) {
        return Some(Path::new("__GENERATED__").join(generated_path))
    }
    file_name.strip_prefix(tree_info.src_dir).ok().map(From::from)
}

fn read_existing_contents(
    map: &mut BTreeSet<String>,
    file: &Path,
) {
    if let Ok(f) = File::open(file) {
        let mut reader = BufReader::new(f);
        for line in reader.lines() {
            map.insert(line.unwrap());
        }
    }
}

fn analyze_file(
    file_name: &PathBuf,
    defs: &Defs,
    file_analysis: &data::Analysis,
    tree_info: &TreeInfo,
) {
    use std::io::Write;

    let file = match find_generated_or_src_file(file_name, tree_info) {
        Some(f) => f,
        None => {
            error!("File not in the source directory or objdir: {}", file_name.display());
            return;
        }
    };

    let output_file = tree_info.output_dir.join(file);
    let mut dataset = BTreeSet::new();
    read_existing_contents(&mut dataset, &output_file);
    let mut output_dir = output_file.clone();
    output_dir.pop();
    if let Err(err) = fs::create_dir_all(output_dir) {
        error!(
            "Couldn't create dir for: {}, {:?}",
            output_file.display(),
            err
        );
        return;
    }
    let mut file = match File::create(&output_file) {
        Ok(f) => f,
        Err(err) => {
            error!(
                "Couldn't open output file: {}, {:?}",
                output_file.display(),
                err
            );
            return;
        }
    };

    for import in &file_analysis.imports {
        let id = match import.ref_id {
            Some(id) => id,
            None => {
                debug!("Dropping import {} ({:?}): {}, no ref", import.name, import.kind, import.value);
                continue;
            }
        };

        let def = match defs.get(file_analysis, id) {
            Some(def) => def,
            None => {
                debug!("Dropping import {} ({:?}): {}, no def for ref {:?}", import.name, import.kind, import.value, id);
                continue;
            }
        };

        visit(&mut dataset, "use", &import.span, &def.qualname, &def, None)
    }

    for def in &file_analysis.defs {
        let parent =
            def.parent.and_then(|parent_id| defs.get(file_analysis, parent_id));

        if let Some(ref parent) = parent {
            if parent.kind == DefKind::Trait {
                let trait_dependent_name =
                    format!("{}::{}", parent.qualname, def.name);
                visit(
                    &mut dataset,
                    "def",
                    &def.span,
                    &trait_dependent_name,
                    &def,
                    Some(&parent.qualname),
                )
            }
        }

        let crate_id = &file_analysis.prelude.as_ref().unwrap().crate_id;
        let qualname = crate_independent_qualname(&def, crate_id);
        visit(&mut dataset, "def", &def.span, &qualname, &def, parent.as_ref().map(|p| &*p.qualname))
    }

    for ref_ in &file_analysis.refs {
        let def = match defs.get(file_analysis, ref_.ref_id) {
            Some(d) => d,
            None => {
                warn!("Dropping ref {:?}, kind {:?}, no def", ref_.ref_id, ref_.kind);
                continue;
            }
        };
        visit(
            &mut dataset,
            "use",
            &ref_.span,
            &def.qualname,
            &def,
            /* context = */ None, // TODO
        )
    }

    for obj in &dataset {
        file.write_all(obj.as_bytes()).unwrap();
        write!(file, "\n").unwrap();
    }
}

fn analyze_crate(
    analysis: &data::Analysis,
    defs: &Defs,
    tree_info: &TreeInfo,
) {
    let mut per_file = HashMap::new();

    println!("Analyzing crate: {:?}", analysis.prelude);

    macro_rules! flat_map_per_file {
        ($field:ident) => {
            for item in &analysis.$field {
                let mut file_analysis =
                    per_file.entry(item.span.file_name.clone())
                        .or_insert_with(|| {
                            let prelude = analysis.prelude.clone();
                            let mut analysis = data::Analysis::new(analysis.config.clone());
                            analysis.prelude = prelude;
                            analysis
                        });
                file_analysis.$field.push(item.clone());
            }
        }
    }

    flat_map_per_file!(imports);
    flat_map_per_file!(defs);
    flat_map_per_file!(impls);
    flat_map_per_file!(refs);
    flat_map_per_file!(macro_refs);
    flat_map_per_file!(relations);

    for (mut name, analysis) in per_file.drain() {
        if name.is_relative() {
            name = tree_info.src_dir.join(name);
        }
        analyze_file(&name, defs, &analysis, tree_info);
    }
}

fn main() {
    use clap::Arg;
    env_logger::init();
    let matches = app_from_crate!()
        .args_from_usage(
            "<src>      'Points to the source root'
             <output>   'Points to the directory where searchfox metadata should go'
             <objdir>   'Points to the objdir generated files may come from'"
        )
        .arg(Arg::with_name("input")
            .required(false)
            .multiple(true)
            .help("rustc analysis directories")
        )
        .get_matches();

    let src_dir = Path::new(matches.value_of("src").unwrap());
    let output_dir = Path::new(matches.value_of("output").unwrap());
    let objdir = Path::new(matches.value_of("objdir").unwrap());

    let tree_info = TreeInfo { src_dir, output_dir, objdir };

    let input_dirs = match matches.values_of("input") {
        Some(inputs) => inputs.map(PathBuf::from).collect(),
        None => vec![],
    };
    let loader = Loader::new(input_dirs);

    let crates = rls_analysis::read_analysis_from_files(&loader, Default::default(), &[]);

    println!("{:?}", crates.iter().map(|k| &k.id.name).collect::<Vec<_>>());

    let mut defs = Defs::new();
    for krate in &crates {
        for def in &krate.analysis.defs {
            // println!("Indexing def: {:?}", def);
            defs.insert(&krate.analysis, def);
        }
    }

    for krate in crates {
        analyze_crate(&krate.analysis, &defs, &tree_info);
    }
}
<commit_msg>Silence warning that is 60% of the indexing logfile<commit_after>#[macro_use]
extern crate clap;
extern crate env_logger;
#[macro_use]
extern crate log;
extern crate rls_analysis;
extern crate rls_data as data;
extern crate serde;
#[macro_use]
extern crate serde_json;

use data::GlobalCrateId;
use data::DefKind;
use rls_analysis::{AnalysisHost, AnalysisLoader, SearchDirectory};
use std::collections::{BTreeSet, HashMap};
use std::io::{BufRead, BufReader};
use std::fs::{self, File};
use std::path::{Path, PathBuf};

/// A global definition id in a crate.
///
/// FIXME(emilio): This key is kind of slow, because GlobalCrateId contains a
/// String. There's a "disambiguator" field which may be more than enough for
/// our purposes.
#[derive(Clone, Hash, Debug, Eq, PartialEq)]
pub struct DefId(GlobalCrateId, u32);

/// A map from global definition ids to the actual definition.
pub struct Defs {
    map: HashMap<DefId, data::Def>,
}

struct TreeInfo<'a> {
    src_dir: &'a Path,
    output_dir: &'a Path,
    objdir: &'a Path,
}

// Given a definition, and the global crate id where that definition is found,
// return a qualified name that identifies the definition unambiguously.
fn crate_independent_qualname(
    def: &data::Def,
    crate_id: &data::GlobalCrateId,
) -> String {
    // For functions with "no_mangle", we just use the name.
    if def.kind == DefKind::Function &&
        def.attributes.iter().any(|attr| attr.value == "no_mangle")
    {
        return def.name.clone();
    }

    format!("{}{}", crate_id.name, def.qualname)
}

impl Defs {
    fn new() -> Self {
        Self { map: HashMap::new() }
    }

    fn insert(&mut self, analysis: &data::Analysis, def: &data::Def) {
        let crate_id = analysis.prelude.as_ref().unwrap().crate_id.clone();
        let mut definition = def.clone();
        definition.qualname = crate_independent_qualname(&def, &crate_id);

        let index = definition.id.index;
        let previous = self.map.insert(DefId(crate_id, index), definition);
        if let Some(previous) = previous {
            // This shouldn't happen, but as of right now it can happen with
            // some builtin definitions when highly generic types are involved.
            // This is probably a rust bug, just ignore it for now.
            debug!(
                "Found a definition with the same ID twice? {:?}, {:?}",
                previous,
                def,
            );
        }
    }

    /// Getter for a given local id, which takes care of converting to a global
    /// ID and returning the definition if present.
    fn get(&self, analysis: &data::Analysis, id: data::Id) -> Option<data::Def> {
        let prelude = analysis.prelude.as_ref().unwrap();
        let krate_id = if id.krate == 0 {
            prelude.crate_id.clone()
        } else {
            // TODO(emilio): This escales with the number of crates in this
            // particular crate, but it's probably not too bad, since it should
            // be a pretty fast linear search.
            let krate = prelude.external_crates.iter().find(|krate| {
                krate.num == id.krate
            });

            let krate = match krate {
                Some(k) => k,
                None => {
                    debug!("Crate not found: {:?}", id);
                    return None;
                }
            };

            krate.id.clone()
        };

        let id = DefId(krate_id, id.index);
        self.map.get(&id).cloned()
    }
}

#[derive(Clone)]
pub struct Loader {
    deps_dirs: Vec<PathBuf>,
}

impl Loader {
    pub fn new(deps_dirs: Vec<PathBuf>) -> Self {
        Self { deps_dirs }
    }
}

impl AnalysisLoader for Loader {
    fn needs_hard_reload(&self, _: &Path) -> bool {
        true
    }

    fn fresh_host(&self) -> AnalysisHost<Self> {
        AnalysisHost::new_with_loader(self.clone())
    }

    fn set_path_prefix(&mut self, _: &Path) {}

    fn abs_path_prefix(&self) -> Option<PathBuf> {
        None
    }
    fn search_directories(&self) -> Vec<SearchDirectory> {
        self.deps_dirs.iter().map(|pb| {
            SearchDirectory {
                path: pb.clone(),
                prefix_rewrite: None,
            }
        }).collect()
    }
}

// Searchfox uses 1-indexed lines, 0-indexed columns.
fn span_to_string(span: &data::SpanData) -> String {
    // Rust spans are multi-line... So we just set the start column if it spans
    // multiple rows, searchfox has fallback code to handle this.
    if span.line_start != span.line_end {
        return format!("{}:{}", span.line_start.0, span.column_start.0 - 1);
    }
    if span.column_start == span.column_end {
        return format!("{}:{}", span.line_start.0, span.column_start.0 - 1);
    }
    format!("{}:{}-{}", span.line_start.0, span.column_start.zero_indexed().0, span.column_end.zero_indexed().0)
}

fn def_kind_to_human(kind: DefKind) -> &'static str {
    match kind {
        DefKind::Enum => "enum",
        DefKind::Local => "local",
        DefKind::ExternType => "extern type",
        DefKind::Const => "constant",
        DefKind::Field => "field",
        DefKind::Function | DefKind::ForeignFunction => "function",
        DefKind::Macro => "macro",
        DefKind::Method => "method",
        DefKind::Mod => "module",
        DefKind::Static | DefKind::ForeignStatic => "static",
        DefKind::Struct => "struct",
        DefKind::Tuple => "tuple",
        DefKind::TupleVariant => "tuple variant",
        DefKind::Union => "union",
        DefKind::Type => "type",
        DefKind::Trait => "trait",
        DefKind::StructVariant => "struct variant",
    }
}

fn visit(
    out_data: &mut BTreeSet<String>,
    kind: &'static str,
    location: &data::SpanData,
    qualname: &str,
    def: &data::Def,
    context: Option<&str>,
) {
    use serde_json::map::Map;
    use serde_json::value::Value;

    let mut out = Map::new();
    out.insert("loc".into(), Value::String(span_to_string(location)));
    out.insert("target".into(), json!(1));
    out.insert("kind".into(), Value::String(kind.into()));
    let pretty = {
        let mut pretty = def_kind_to_human(def.kind).to_owned();
        pretty.push_str(" ");
        pretty.push_str(qualname);

        pretty
    };
    out.insert("pretty".into(), Value::String(pretty.clone()));
    out.insert("sym".into(), Value::String(qualname.into()));
    if let Some(context) = context {
        out.insert("context".into(), Value::String(context.into()));
        out.insert("contextsym".into(), Value::String(context.into()));
    }

    let object = serde_json::to_string(&Value::Object(out)).unwrap();
    out_data.insert(object);

    let mut out = Map::new();
    out.insert("loc".into(), Value::String(span_to_string(location)));
    out.insert("source".into(), json!(1));
    out.insert("kind".into(), Value::String(kind.into()));
    out.insert("pretty".into(), Value::String(pretty));
    out.insert("sym".into(), Value::String(qualname.into()));

    let object = serde_json::to_string(&Value::Object(out)).unwrap();
    out_data.insert(object);
}

fn find_generated_or_src_file(
    file_name: &Path,
    tree_info: &TreeInfo,
) -> Option<PathBuf> {
    if let Ok(generated_path) = file_name.strip_prefix(tree_info.objdir) {
        return Some(Path::new("__GENERATED__").join(generated_path))
    }
    file_name.strip_prefix(tree_info.src_dir).ok().map(From::from)
}

fn read_existing_contents(
    map: &mut BTreeSet<String>,
    file: &Path,
) {
    if let Ok(f) = File::open(file) {
        let mut reader = BufReader::new(f);
        for line in reader.lines() {
            map.insert(line.unwrap());
        }
    }
}

fn analyze_file(
    file_name: &PathBuf,
    defs: &Defs,
    file_analysis: &data::Analysis,
    tree_info: &TreeInfo,
) {
    use std::io::Write;

    let file = match find_generated_or_src_file(file_name, tree_info) {
        Some(f) => f,
        None => {
            error!("File not in the source directory or objdir: {}", file_name.display());
            return;
        }
    };

    let output_file = tree_info.output_dir.join(file);
    let mut dataset = BTreeSet::new();
    read_existing_contents(&mut dataset, &output_file);
    let mut output_dir = output_file.clone();
    output_dir.pop();
    if let Err(err) = fs::create_dir_all(output_dir) {
        error!(
            "Couldn't create dir for: {}, {:?}",
            output_file.display(),
            err
        );
        return;
    }
    let mut file = match File::create(&output_file) {
        Ok(f) => f,
        Err(err) => {
            error!(
                "Couldn't open output file: {}, {:?}",
                output_file.display(),
                err
            );
            return;
        }
    };

    for import in &file_analysis.imports {
        let id = match import.ref_id {
            Some(id) => id,
            None => {
                debug!("Dropping import {} ({:?}): {}, no ref", import.name, import.kind, import.value);
                continue;
            }
        };

        let def = match defs.get(file_analysis, id) {
            Some(def) => def,
            None => {
                debug!("Dropping import {} ({:?}): {}, no def for ref {:?}", import.name, import.kind, import.value, id);
                continue;
            }
        };

        visit(&mut dataset, "use", &import.span, &def.qualname, &def, None)
    }

    for def in &file_analysis.defs {
        let parent =
            def.parent.and_then(|parent_id| defs.get(file_analysis, parent_id));

        if let Some(ref parent) = parent {
            if parent.kind == DefKind::Trait {
                let trait_dependent_name =
                    format!("{}::{}", parent.qualname, def.name);
                visit(
                    &mut dataset,
                    "def",
                    &def.span,
                    &trait_dependent_name,
                    &def,
                    Some(&parent.qualname),
                )
            }
        }

        let crate_id = &file_analysis.prelude.as_ref().unwrap().crate_id;
        let qualname = crate_independent_qualname(&def, crate_id);
        visit(&mut dataset, "def", &def.span, &qualname, &def, parent.as_ref().map(|p| &*p.qualname))
    }

    for ref_ in &file_analysis.refs {
        let def = match defs.get(file_analysis, ref_.ref_id) {
            Some(d) => d,
            None => {
                debug!("Dropping ref {:?}, kind {:?}, no def", ref_.ref_id, ref_.kind);
                continue;
            }
        };
        visit(
            &mut dataset,
            "use",
            &ref_.span,
            &def.qualname,
            &def,
            /* context = */ None, // TODO
        )
    }

    for obj in &dataset {
        file.write_all(obj.as_bytes()).unwrap();
        write!(file, "\n").unwrap();
    }
}

fn analyze_crate(
    analysis: &data::Analysis,
    defs: &Defs,
    tree_info: &TreeInfo,
) {
    let mut per_file = HashMap::new();

    println!("Analyzing crate: {:?}", analysis.prelude);

    macro_rules! flat_map_per_file {
        ($field:ident) => {
            for item in &analysis.$field {
                let mut file_analysis =
                    per_file.entry(item.span.file_name.clone())
                        .or_insert_with(|| {
                            let prelude = analysis.prelude.clone();
                            let mut analysis = data::Analysis::new(analysis.config.clone());
                            analysis.prelude = prelude;
                            analysis
                        });
                file_analysis.$field.push(item.clone());
            }
        }
    }

    flat_map_per_file!(imports);
    flat_map_per_file!(defs);
    flat_map_per_file!(impls);
    flat_map_per_file!(refs);
    flat_map_per_file!(macro_refs);
    flat_map_per_file!(relations);

    for (mut name, analysis) in per_file.drain() {
        if name.is_relative() {
            name = tree_info.src_dir.join(name);
        }
        analyze_file(&name, defs, &analysis, tree_info);
    }
}

fn main() {
    use clap::Arg;
    env_logger::init();
    let matches = app_from_crate!()
        .args_from_usage(
            "<src>      'Points to the source root'
             <output>   'Points to the directory where searchfox metadata should go'
             <objdir>   'Points to the objdir generated files may come from'"
        )
        .arg(Arg::with_name("input")
            .required(false)
            .multiple(true)
            .help("rustc analysis directories")
        )
        .get_matches();

    let src_dir = Path::new(matches.value_of("src").unwrap());
    let output_dir = Path::new(matches.value_of("output").unwrap());
    let objdir = Path::new(matches.value_of("objdir").unwrap());

    let tree_info = TreeInfo { src_dir, output_dir, objdir };

    let input_dirs = match matches.values_of("input") {
        Some(inputs) => inputs.map(PathBuf::from).collect(),
        None => vec![],
    };
    let loader = Loader::new(input_dirs);

    let crates = rls_analysis::read_analysis_from_files(&loader, Default::default(), &[]);

    println!("{:?}", crates.iter().map(|k| &k.id.name).collect::<Vec<_>>());

    let mut defs = Defs::new();
    for krate in &crates {
        for def in &krate.analysis.defs {
            // println!("Indexing def: {:?}", def);
            defs.insert(&krate.analysis, def);
        }
    }

    for krate in crates {
        analyze_crate(&krate.analysis, &defs, &tree_info);
    }
}
<|endoftext|>


<commit_before>use specs::{AMQPArgument, AMQPFlagArgument};
use util::*;

use amq_protocol_types::AMQPType;
use handlebars::{self, Handlebars, Helper, Renderable, RenderContext, RenderError, to_json};
use serde_json::{self};

use std::collections::BTreeMap;

pub type CodeGenerator = Handlebars;

pub trait HandlebarsAMQPExtension {
    fn register_amqp_helpers(self) -> Self;
}

impl HandlebarsAMQPExtension for CodeGenerator {
    fn register_amqp_helpers(mut self) -> CodeGenerator {
        self.register_escape_fn(handlebars::no_escape);
        self.register_helper("camel",         Box::new(camel_helper));
        self.register_helper("snake",         Box::new(snake_helper));
        self.register_helper("snake_type",    Box::new(snake_type_helper));
        self.register_helper("sanitize_name", Box::new(sanitize_name_helper));
        self.register_helper("each_argument", Box::new(each_argument_helper));
        self.register_helper("each_flag",     Box::new(each_flag_helper));
        self
    }
}

pub fn camel_helper (h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
    let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"camel\""))?;
    let param = value.value().as_str().ok_or_else(|| RenderError::new("Non-string param given to helper \"camel\""))?;
    rc.writer.write(camel_case(param).as_bytes())?;
    Ok(())
}

pub fn snake_helper (h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
    let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"snake\""))?;
    let param = value.value().as_str().ok_or_else(|| RenderError::new("Non-string param given to helper \"snake\""))?;
    rc.writer.write(snake_case(param).as_bytes())?;
    Ok(())
}

pub fn snake_type_helper (h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
    let value           = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"snake\""))?;
    let param: AMQPType = serde_json::from_value(value.value().clone()).map_err(|_| RenderError::new("Param is not an AMQPType for helper \"snake_type\""))?;
    rc.writer.write(snake_case(&param.to_string()).as_bytes())?;
    Ok(())
}

pub fn sanitize_name_helper (h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
    let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"sanitize_name\""))?;
    let param = value.value().as_str().ok_or_else(|| RenderError::new("Non-string param given to helper \"sanitize_name\""))?;
    rc.writer.write(param.replace('-', "_").as_bytes())?;
    Ok(())
}

pub fn each_argument_helper (h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
    let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"each_argument\""))?;

    if let Some(t) = h.template() {
        rc.promote_local_vars();
        let local_path_root = value.path_root().map(|p| format!("{}/{}", rc.get_path(), p));
        let arguments : Vec<AMQPArgument> = serde_json::from_value(value.value().clone()).map_err(|_| RenderError::new("Param is not a Vec<AMQPArgument> for helper \"each_argument\""))?;
        for (index, argument) in arguments.iter().enumerate() {
            let mut local_rc = rc.derive();
            if let Some(ref p) = local_path_root {
                local_rc.push_local_path_root(p.clone());
            }
            local_rc.set_local_var("@index".to_string(), to_json(&index));
            if let Some(inner_path) = value.path() {
                let new_path = format!("{}/{}.[{}]", local_rc.get_path(), inner_path, index);
                local_rc.set_path(new_path.clone());
            }
            if let Some(block_param) = h.block_param() {
                let mut map = BTreeMap::new();
                match *argument {
                    AMQPArgument::Value(ref v) => {
                        map.insert(block_param.to_string(), to_json(v));
                        map.insert("argument_is_value".to_string(), to_json(&true));
                    },
                    AMQPArgument::Flags(ref f) => {
                        map.insert(block_param.to_string(), to_json(f));
                        map.insert("argument_is_value".to_string(), to_json(&false));
                    },
                };
                local_rc.push_block_context(&map);
            }
            t.render(r, &mut local_rc)?;
            if h.block_param().is_some() {
                local_rc.pop_block_context();
            }
            if local_path_root.is_some() {
                local_rc.pop_local_path_root();
            }
        }
        rc.demote_local_vars();
    }
    Ok(())
}

pub fn each_flag_helper (h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
    let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"each_flag\""))?;

    if let Some(t) = h.template() {
        rc.promote_local_vars();
        let local_path_root = value.path_root().map(|p| format!("{}/{}", rc.get_path(), p));
        let flags : Vec<AMQPFlagArgument> = serde_json::from_value(value.value().clone()).map_err(|_| RenderError::new("Param is not a Vec<AMQPFlagArgument> for helper \"each_flag\""))?;
        for (index, flag) in flags.iter().enumerate() {
            let mut local_rc = rc.derive();
            if let Some(ref p) = local_path_root {
                local_rc.push_local_path_root(p.clone());
            }
            local_rc.set_local_var("@index".to_string(), to_json(&index));
            if let Some(inner_path) = value.path() {
                let new_path = format!("{}/{}.[{}]", local_rc.get_path(), inner_path, index);
                local_rc.set_path(new_path.clone());
            }
            if let Some(block_param) = h.block_param() {
                let mut map = BTreeMap::new();
                map.insert(block_param.to_string(), to_json(flag));
                local_rc.push_block_context(&map);
            }
            t.render(r, &mut local_rc)?;
            if h.block_param().is_some() {
                local_rc.pop_block_context();
            }
            if local_path_root.is_some() {
                local_rc.pop_local_path_root();
            }
        }
        rc.demote_local_vars();
    }
    Ok(())
}

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

    use specs::*;

    use amq_protocol_types::*;

    use std::collections::BTreeMap;

    pub const TEMPLATE: &'static str = r#"
{{protocol.name}} - {{protocol.major_version}}.{{protocol.minor_version}}.{{protocol.revision}}
{{protocol.copyright}}
port {{protocol.port}}
{{#each protocol.domains ~}}
{{@key}}: {{this}}
{{/each ~}}
{{#each protocol.constants as |constant| ~}}
{{constant.name}}({{constant.class}}) = {{constant.value}}
{{/each ~}}
{{#each protocol.classes as |class| ~}}
{{class.id}} - {{class.name}}
{{#each class.properties as |property| ~}}
{{property.name}}: {{property.type}}
{{/each ~}}
{{#each class.methods as |method| ~}}
{{method.id}} - {{method.name}}
synchronous: {{method.synchronous}}
{{#each_argument method.arguments as |argument| ~}}
{{#if argument_is_value ~}}
{{argument.name}}({{argument.domain}}): {{argument.type}}
{{else}}
{{#each_flag argument as |flag| ~}}
{{flag.name}}: {{flag.default_value}}
{{/each_flag ~}}
{{/if ~}}
{{/each_argument ~}}
{{/each ~}}
{{/each ~}}
"#;

    fn specs() -> AMQProtocolDefinition {
        let mut domains = BTreeMap::new();
        domains.insert("domain1".to_string(), AMQPType::LongString);
        AMQProtocolDefinition {
            name:          "AMQP".to_string(),
            major_version: 0,
            minor_version: 9,
            revision:      1,
            port:          5672,
            copyright:     "Copyright 1\nCopyright 2".to_string(),
            domains:       domains,
            constants:     vec![
                AMQPConstant {
                    name:  "constant1".to_string(),
                    value: 128,
                    klass: Some("class1".to_string()),
                }
            ],
            classes:       vec![
                AMQPClass {
                    id:            42,
                    methods:       vec![
                        AMQPMethod {
                            id:          64,
                            arguments:   vec![
                                AMQPArgument::Value(AMQPValueArgument {
                                    amqp_type:     AMQPType::LongString,
                                    name:          "argument1".to_string(),
                                    default_value: Some(AMQPValue::LongString("value1".to_string())),
                                    domain:        Some("domain1".to_string()),
                                }),
                                AMQPArgument::Flags(vec![
                                    AMQPFlagArgument {
                                        name:         "flag1".to_string(),
                                        default_value: true,
                                    },
                                    AMQPFlagArgument {
                                        name:         "flag2".to_string(),
                                        default_value: false,
                                    },
                                ]),
                            ],
                            has_flags:   true,
                            name:        "method1".to_string(),
                            synchronous: true,
                        }
                    ],
                    name:          "class1".to_string(),
                    properties:    vec![
                        AMQPProperty {
                            amqp_type: AMQPType::LongString,
                            name:      "property1".to_string(),
                        }
                    ],
                    is_connection: false,
                }
            ],
        }
    }

    #[test]
    fn main_template() {
        let mut data    = BTreeMap::new();
        let mut codegen = CodeGenerator::new().register_amqp_helpers();
        data.insert("protocol".to_string(), specs());
        codegen.register_template_string("main", TEMPLATE.to_string()).unwrap();
        assert_eq!(codegen.render("main", &data).unwrap(), r#"
AMQP - 0.9.1
Copyright 1
Copyright 2
port 5672
domain1: LongString
constant1(class1) = 128
42 - class1
property1: LongString
64 - method1
synchronous: true
argument1(domain1): LongString

flag1: true
flag2: false

"#);
    }
}
<commit_msg>codegen: fix tests<commit_after>use specs::{AMQPArgument, AMQPFlagArgument};
use util::*;

use amq_protocol_types::AMQPType;
use handlebars::{self, Handlebars, Helper, Renderable, RenderContext, RenderError, to_json};
use serde_json::{self};

use std::collections::BTreeMap;

pub type CodeGenerator = Handlebars;

pub trait HandlebarsAMQPExtension {
    fn register_amqp_helpers(self) -> Self;
}

impl HandlebarsAMQPExtension for CodeGenerator {
    fn register_amqp_helpers(mut self) -> CodeGenerator {
        self.register_escape_fn(handlebars::no_escape);
        self.register_helper("camel",         Box::new(camel_helper));
        self.register_helper("snake",         Box::new(snake_helper));
        self.register_helper("snake_type",    Box::new(snake_type_helper));
        self.register_helper("sanitize_name", Box::new(sanitize_name_helper));
        self.register_helper("each_argument", Box::new(each_argument_helper));
        self.register_helper("each_flag",     Box::new(each_flag_helper));
        self
    }
}

pub fn camel_helper (h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
    let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"camel\""))?;
    let param = value.value().as_str().ok_or_else(|| RenderError::new("Non-string param given to helper \"camel\""))?;
    rc.writer.write(camel_case(param).as_bytes())?;
    Ok(())
}

pub fn snake_helper (h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
    let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"snake\""))?;
    let param = value.value().as_str().ok_or_else(|| RenderError::new("Non-string param given to helper \"snake\""))?;
    rc.writer.write(snake_case(param).as_bytes())?;
    Ok(())
}

pub fn snake_type_helper (h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
    let value           = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"snake\""))?;
    let param: AMQPType = serde_json::from_value(value.value().clone()).map_err(|_| RenderError::new("Param is not an AMQPType for helper \"snake_type\""))?;
    rc.writer.write(snake_case(&param.to_string()).as_bytes())?;
    Ok(())
}

pub fn sanitize_name_helper (h: &Helper, _: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
    let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"sanitize_name\""))?;
    let param = value.value().as_str().ok_or_else(|| RenderError::new("Non-string param given to helper \"sanitize_name\""))?;
    rc.writer.write(param.replace('-', "_").as_bytes())?;
    Ok(())
}

pub fn each_argument_helper (h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
    let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"each_argument\""))?;

    if let Some(t) = h.template() {
        rc.promote_local_vars();
        let local_path_root = value.path_root().map(|p| format!("{}/{}", rc.get_path(), p));
        let arguments : Vec<AMQPArgument> = serde_json::from_value(value.value().clone()).map_err(|_| RenderError::new("Param is not a Vec<AMQPArgument> for helper \"each_argument\""))?;
        for (index, argument) in arguments.iter().enumerate() {
            let mut local_rc = rc.derive();
            if let Some(ref p) = local_path_root {
                local_rc.push_local_path_root(p.clone());
            }
            local_rc.set_local_var("@index".to_string(), to_json(&index));
            if let Some(inner_path) = value.path() {
                let new_path = format!("{}/{}.[{}]", local_rc.get_path(), inner_path, index);
                local_rc.set_path(new_path.clone());
            }
            if let Some(block_param) = h.block_param() {
                let mut map = BTreeMap::new();
                match *argument {
                    AMQPArgument::Value(ref v) => {
                        map.insert(block_param.to_string(), to_json(v));
                        map.insert("argument_is_value".to_string(), to_json(&true));
                    },
                    AMQPArgument::Flags(ref f) => {
                        map.insert(block_param.to_string(), to_json(f));
                        map.insert("argument_is_value".to_string(), to_json(&false));
                    },
                };
                local_rc.push_block_context(&map);
            }
            t.render(r, &mut local_rc)?;
            if h.block_param().is_some() {
                local_rc.pop_block_context();
            }
            if local_path_root.is_some() {
                local_rc.pop_local_path_root();
            }
        }
        rc.demote_local_vars();
    }
    Ok(())
}

pub fn each_flag_helper (h: &Helper, r: &Handlebars, rc: &mut RenderContext) -> Result<(), RenderError> {
    let value = h.param(0).ok_or_else(|| RenderError::new("Param not found for helper \"each_flag\""))?;

    if let Some(t) = h.template() {
        rc.promote_local_vars();
        let local_path_root = value.path_root().map(|p| format!("{}/{}", rc.get_path(), p));
        let flags : Vec<AMQPFlagArgument> = serde_json::from_value(value.value().clone()).map_err(|_| RenderError::new("Param is not a Vec<AMQPFlagArgument> for helper \"each_flag\""))?;
        for (index, flag) in flags.iter().enumerate() {
            let mut local_rc = rc.derive();
            if let Some(ref p) = local_path_root {
                local_rc.push_local_path_root(p.clone());
            }
            local_rc.set_local_var("@index".to_string(), to_json(&index));
            if let Some(inner_path) = value.path() {
                let new_path = format!("{}/{}.[{}]", local_rc.get_path(), inner_path, index);
                local_rc.set_path(new_path.clone());
            }
            if let Some(block_param) = h.block_param() {
                let mut map = BTreeMap::new();
                map.insert(block_param.to_string(), to_json(flag));
                local_rc.push_block_context(&map);
            }
            t.render(r, &mut local_rc)?;
            if h.block_param().is_some() {
                local_rc.pop_block_context();
            }
            if local_path_root.is_some() {
                local_rc.pop_local_path_root();
            }
        }
        rc.demote_local_vars();
    }
    Ok(())
}

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

    use specs::*;

    use amq_protocol_types::*;

    use std::collections::BTreeMap;

    pub const TEMPLATE: &'static str = r#"
{{protocol.name}} - {{protocol.major_version}}.{{protocol.minor_version}}.{{protocol.revision}}
{{protocol.copyright}}
port {{protocol.port}}
{{#each protocol.domains ~}}
{{@key}}: {{this}}
{{/each ~}}
{{#each protocol.constants as |constant| ~}}
{{constant.name}} = {{constant.value}}
{{/each ~}}
{{#each protocol.classes as |class| ~}}
{{class.id}} - {{class.name}}
{{#each class.properties as |property| ~}}
{{property.name}}: {{property.type}}
{{/each ~}}
{{#each class.methods as |method| ~}}
{{method.id}} - {{method.name}}
synchronous: {{method.synchronous}}
{{#each_argument method.arguments as |argument| ~}}
{{#if argument_is_value ~}}
{{argument.name}}({{argument.domain}}): {{argument.type}}
{{else}}
{{#each_flag argument as |flag| ~}}
{{flag.name}}: {{flag.default_value}}
{{/each_flag ~}}
{{/if ~}}
{{/each_argument ~}}
{{/each ~}}
{{/each ~}}
"#;

    fn specs() -> AMQProtocolDefinition {
        let mut domains = BTreeMap::new();
        domains.insert("domain1".to_string(), AMQPType::LongString);
        AMQProtocolDefinition {
            name:          "AMQP".to_string(),
            major_version: 0,
            minor_version: 9,
            revision:      1,
            port:          5672,
            copyright:     "Copyright 1\nCopyright 2".to_string(),
            domains:       domains,
            constants:     vec![
                AMQPConstant {
                    name:      "constant1".to_string(),
                    amqp_type: AMQPType::ShortUInt,
                    value:     128,
                }
            ],
            soft_errors:   Vec::new(),
            hard_errors:   Vec::new(),
            classes:       vec![
                AMQPClass {
                    id:            42,
                    methods:       vec![
                        AMQPMethod {
                            id:          64,
                            arguments:   vec![
                                AMQPArgument::Value(AMQPValueArgument {
                                    amqp_type:     AMQPType::LongString,
                                    name:          "argument1".to_string(),
                                    default_value: Some(AMQPValue::LongString("value1".to_string())),
                                    domain:        Some("domain1".to_string()),
                                }),
                                AMQPArgument::Flags(vec![
                                    AMQPFlagArgument {
                                        name:         "flag1".to_string(),
                                        default_value: true,
                                    },
                                    AMQPFlagArgument {
                                        name:         "flag2".to_string(),
                                        default_value: false,
                                    },
                                ]),
                            ],
                            has_flags:   true,
                            name:        "method1".to_string(),
                            synchronous: true,
                        }
                    ],
                    name:          "class1".to_string(),
                    properties:    vec![
                        AMQPProperty {
                            amqp_type: AMQPType::LongString,
                            name:      "property1".to_string(),
                        }
                    ],
                    is_connection: false,
                }
            ],
        }
    }

    #[test]
    fn main_template() {
        let mut data    = BTreeMap::new();
        let mut codegen = CodeGenerator::new().register_amqp_helpers();
        data.insert("protocol".to_string(), specs());
        codegen.register_template_string("main", TEMPLATE.to_string()).unwrap();
        assert_eq!(codegen.render("main", &data).unwrap(), r#"
AMQP - 0.9.1
Copyright 1
Copyright 2
port 5672
domain1: LongString
constant1 = 128
42 - class1
property1: LongString
64 - method1
synchronous: true
argument1(domain1): LongString

flag1: true
flag2: false

"#);
    }
}
<|endoftext|>