<commit_before>//! The global state.

use parking_lot::Mutex;
use std::collections::HashSet;
use std::mem;
use {rand, hazard, mpsc};
use garbage::Garbage;

lazy_static! {
    /// The global state.
    ///
    /// This state is shared between all the threads.
    static ref STATE: State = State::new();
}

/// Create a new hazard.
///
/// This creates a new hazard and registers it in the global state. It's secondary, writer part is
/// returned.
pub fn create_hazard() -> hazard::Writer {
    // Create the hazard.
    let (write, read) = hazard::create();
    // Communicate the new hazard to the global state through the channel.
    STATE.chan.send(Message::NewHazard(read));
    // Return the other half of the hazard.
    write
}

/// Export garbage into the global state.
///
/// This adds the garbage, which will eventually be destroyed, to the global state.
pub fn export_garbage(garbage: Vec<Garbage>) {
    // Send the garbage to the message-passing channel of the state.
    STATE.chan.send(Message::Garbage(garbage));
    // Tick the clock, potentially triggering a garbage collection.
    tick();
}

/// Atempt to garbage collect.
///
/// If another garbage collection is currently running, nothing will happen.
pub fn gc() {
    STATE.try_gc();
}

/// Tick the clock.
///
/// This shall be called when new garbage is added, as it will trigger a GC by some probability.
pub fn tick() {
    /// The probabiity of triggering a GC.
    ///
    /// This probability is given such that `0` corresponds to 0 and `!0` corresponds to `1`.
    const GC_PROBABILITY: usize = (!0) / 64;

    // Generate a random number and compare it against the probability.
    if rand::random::<usize>() <= GC_PROBABILITY {
        // The outfall was to GC.
        gc();
    }
}

/// A message to the global state.
enum Message {
    /// Add new garbage.
    Garbage(Vec<Garbage>),
    /// Add a new hazard.
    NewHazard(hazard::Reader),
}

/// The global state.
///
/// The global state is shared between all threads and keeps track of the garbage and the active
/// hazards.
///
/// It is divided into two parts: The channel and the garbo. The channel buffers messages, which
/// will eventually be executed at garbo, which holds all the data structures and is protected by a
/// mutex. The garbo holds the other end to the channel.
struct State {
    /// The message-passing channel.
    chan: mpsc::Sender<Message>,
    /// The garbo part of the state.
    garbo: Mutex<Garbo>,
}

impl State {
    /// Initialize a new state.
    fn new() -> State {
        // Create the message-passing channel.
        let (send, recv) = mpsc::channel();

        // Construct the state from the two halfs of the channel.
        State {
            chan: send,
            garbo: Mutex::new(Garbo {
                chan: recv,
                garbage: Vec::new(),
                hazards: Vec::new(),
            })
        }
    }

    /// Try to collect the garbage.
    ///
    /// This will handle all of the messages in the channel and then attempt at collect the
    /// garbage. If another thread is currently collecting garbage, it will be equivalent to NOP.
    ///
    /// Garbage collection works by scanning the hazards and dropping all the garbage which is not
    /// currently active in the hazards.
    fn try_gc(&self) {
        // Lock the "garbo" (the part of the state needed to GC).
        if let Some(mut garbo) = self.garbo.try_lock() {
            // Handle all the messages sent.
            garbo.handle_all();
            // Collect the garbage.
            garbo.gc();
        }
    }
}

/// The garbo part of the state.
///
/// This part is supposed to act like the garbage collecting part. It handles hazards, garbage, and
/// the receiving point of the message-passing channel.
struct Garbo {
    /// The channel of messages.
    chan: mpsc::Receiver<Message>,
    /// The to-be-destroyed garbage.
    garbage: Vec<Garbage>,
    /// The current hazards.
    hazards: Vec<hazard::Reader>,
}

impl Garbo {
    /// Handle a given message.
    ///
    /// "Handle" in this case refers to applying the operation defined by the message to the state,
    /// effectually executing the instruction of the message.
    fn handle(&mut self, msg: Message) {
        match msg {
            // Append the garbage bulk to the garbage list.
            Message::Garbage(mut garbage) => self.garbage.append(&mut garbage),
            // Register the new hazard into the state.
            Message::NewHazard(hazard) => self.hazards.push(hazard),
        }
    }

    /// Receive and handle all the messages.
    fn handle_all(&mut self) {
        // Go over every message.
        for msg in self.chan.recv_all() {
            self.handle(msg);
        }
    }

    /// Garbage collect all unused garbage.
    fn gc(&mut self) {
        // Create the set which will keep the _active_ hazards.
        let mut active = HashSet::with_capacity(self.hazards.len());

        // Take out the hazards and go over them one-by-one.
        let len = self.hazards.len(); // TODO: This should be substituted into next line.
        for hazard in mem::replace(&mut self.hazards, Vec::with_capacity(len)) {
            match hazard.get() {
                // The hazard is dead, so the other end (the writer) is not available anymore,
                // hence we can safely destroy it.
                hazard::State::Dead => unsafe { hazard.destroy() },
                // The hazard is free and must thus be put back to the hazard list.
                hazard::State::Free => self.hazards.push(hazard),
                hazard::State::Protect(ptr) => {
                    // This hazard is active, hence we insert the pointer it contains in our
                    // "active" set.
                    active.insert(ptr);
                    // Since the hazard is still alive, we must put it back to the hazard list for
                    // future use.
                    self.hazards.push(hazard);
                },
            }
        }

        // Take the garbage and scan it for unused garbage.
        for garbage in mem::replace(&mut self.garbage, Vec::new()) {
            if active.contains(&garbage.ptr()) {
                // If the garbage is in the set of active pointers, it will be put back to the
                // garbage list.
                self.garbage.push(garbage);
            } else {
                // The garbage is unused and not referenced by any hazard, hence we can safely
                // destroy it.
                garbage.destroy();
            }
        }
    }
}
<commit_msg>Do a final GC `concurrent` state ends.<commit_after>//! The global state.

use parking_lot::Mutex;
use std::collections::HashSet;
use std::mem;
use {rand, hazard, mpsc};
use garbage::Garbage;

lazy_static! {
    /// The global state.
    ///
    /// This state is shared between all the threads.
    static ref STATE: State = State::new();
}

/// Create a new hazard.
///
/// This creates a new hazard and registers it in the global state. It's secondary, writer part is
/// returned.
pub fn create_hazard() -> hazard::Writer {
    // Create the hazard.
    let (write, read) = hazard::create();
    // Communicate the new hazard to the global state through the channel.
    STATE.chan.send(Message::NewHazard(read));
    // Return the other half of the hazard.
    write
}

/// Export garbage into the global state.
///
/// This adds the garbage, which will eventually be destroyed, to the global state.
pub fn export_garbage(garbage: Vec<Garbage>) {
    // Send the garbage to the message-passing channel of the state.
    STATE.chan.send(Message::Garbage(garbage));
    // Tick the clock, potentially triggering a garbage collection.
    tick();
}

/// Atempt to garbage collect.
///
/// If another garbage collection is currently running, nothing will happen.
pub fn gc() {
    STATE.try_gc();
}

/// Tick the clock.
///
/// This shall be called when new garbage is added, as it will trigger a GC by some probability.
pub fn tick() {
    /// The probabiity of triggering a GC.
    ///
    /// This probability is given such that `0` corresponds to 0 and `!0` corresponds to `1`.
    const GC_PROBABILITY: usize = (!0) / 64;

    // Generate a random number and compare it against the probability.
    if rand::random::<usize>() <= GC_PROBABILITY {
        // The outfall was to GC.
        gc();
    }
}

/// A message to the global state.
enum Message {
    /// Add new garbage.
    Garbage(Vec<Garbage>),
    /// Add a new hazard.
    NewHazard(hazard::Reader),
}

/// The global state.
///
/// The global state is shared between all threads and keeps track of the garbage and the active
/// hazards.
///
/// It is divided into two parts: The channel and the garbo. The channel buffers messages, which
/// will eventually be executed at garbo, which holds all the data structures and is protected by a
/// mutex. The garbo holds the other end to the channel.
struct State {
    /// The message-passing channel.
    chan: mpsc::Sender<Message>,
    /// The garbo part of the state.
    garbo: Mutex<Garbo>,
}

impl State {
    /// Initialize a new state.
    fn new() -> State {
        // Create the message-passing channel.
        let (send, recv) = mpsc::channel();

        // Construct the state from the two halfs of the channel.
        State {
            chan: send,
            garbo: Mutex::new(Garbo {
                chan: recv,
                garbage: Vec::new(),
                hazards: Vec::new(),
            })
        }
    }

    /// Try to collect the garbage.
    ///
    /// This will handle all of the messages in the channel and then attempt at collect the
    /// garbage. If another thread is currently collecting garbage, it will be equivalent to NOP.
    ///
    /// Garbage collection works by scanning the hazards and dropping all the garbage which is not
    /// currently active in the hazards.
    fn try_gc(&self) {
        // Lock the "garbo" (the part of the state needed to GC).
        if let Some(mut garbo) = self.garbo.try_lock() {
            // Handle all the messages sent.
            garbo.handle_all();
            // Collect the garbage.
            garbo.gc();
        }
    }
}

/// The garbo part of the state.
///
/// This part is supposed to act like the garbage collecting part. It handles hazards, garbage, and
/// the receiving point of the message-passing channel.
struct Garbo {
    /// The channel of messages.
    chan: mpsc::Receiver<Message>,
    /// The to-be-destroyed garbage.
    garbage: Vec<Garbage>,
    /// The current hazards.
    hazards: Vec<hazard::Reader>,
}

impl Garbo {
    /// Handle a given message.
    ///
    /// "Handle" in this case refers to applying the operation defined by the message to the state,
    /// effectually executing the instruction of the message.
    fn handle(&mut self, msg: Message) {
        match msg {
            // Append the garbage bulk to the garbage list.
            Message::Garbage(mut garbage) => self.garbage.append(&mut garbage),
            // Register the new hazard into the state.
            Message::NewHazard(hazard) => self.hazards.push(hazard),
        }
    }

    /// Receive and handle all the messages.
    fn handle_all(&mut self) {
        // Go over every message.
        for msg in self.chan.recv_all() {
            self.handle(msg);
        }
    }

    /// Garbage collect all unused garbage.
    fn gc(&mut self) {
        // Create the set which will keep the _active_ hazards.
        let mut active = HashSet::with_capacity(self.hazards.len());

        // Take out the hazards and go over them one-by-one.
        let len = self.hazards.len(); // TODO: This should be substituted into next line.
        for hazard in mem::replace(&mut self.hazards, Vec::with_capacity(len)) {
            match hazard.get() {
                // The hazard is dead, so the other end (the writer) is not available anymore,
                // hence we can safely destroy it.
                hazard::State::Dead => unsafe { hazard.destroy() },
                // The hazard is free and must thus be put back to the hazard list.
                hazard::State::Free => self.hazards.push(hazard),
                hazard::State::Protect(ptr) => {
                    // This hazard is active, hence we insert the pointer it contains in our
                    // "active" set.
                    active.insert(ptr);
                    // Since the hazard is still alive, we must put it back to the hazard list for
                    // future use.
                    self.hazards.push(hazard);
                },
            }
        }

        // Take the garbage and scan it for unused garbage.
        for garbage in mem::replace(&mut self.garbage, Vec::new()) {
            if active.contains(&garbage.ptr()) {
                // If the garbage is in the set of active pointers, it will be put back to the
                // garbage list.
                self.garbage.push(garbage);
            } else {
                // The garbage is unused and not referenced by any hazard, hence we can safely
                // destroy it.
                garbage.destroy();
            }
        }
    }
}

impl Drop for Garbo {
    fn drop(&mut self) {
        // Do a final GC.
        self.gc();
    }
}
<|endoftext|>


<commit_before><commit_msg>refactor(tests): Show more information<commit_after><|endoftext|>


<commit_before>use diesel::migrations::search_for_migrations_directory;
use std::error::Error;
use std::path::{Path, PathBuf};
use syntax::ast;
use syntax::codemap::Span;
use syntax::ext::base::*;
use syntax::util::small_vector::SmallVector;
use syntax::ptr::P;
use syntax::ext::build::AstBuilder;
use syntax::tokenstream;

pub fn expand_embed_migrations<'cx>(
    cx: &'cx mut ExtCtxt,
    sp: Span,
    tts: &[tokenstream::TokenTree]
) -> Box<MacResult+'cx> {
    let migrations_expr = migrations_directory_from_args(cx, sp, tts)
        .and_then(|d| migration_literals_from_path(cx, sp, &d));
    let migrations_expr = match migrations_expr {
        Err(e) => {
            cx.span_err(sp, &format!("Error reading migrations: {}", e));
            return DummyResult::expr(sp);
        }
        Ok(v) => v,
    };

    let item = quote_item!(cx, mod embedded_migrations {
        extern crate diesel;

        use self::diesel::migrations::*;
        use self::diesel::connection::SimpleConnection;
        use std::io;

        struct EmbeddedMigration {
            version: &'static str,
            up_sql: &'static str,
        }

        impl Migration for EmbeddedMigration {
            fn version(&self) -> &str {
                self.version
            }

            fn run(&self, conn: &SimpleConnection) -> Result<(), RunMigrationsError> {
                conn.batch_execute(self.up_sql).map_err(Into::into)
            }

            fn revert(&self, conn: &SimpleConnection) -> Result<(), RunMigrationsError> {
                unreachable!()
            }
        }

        const ALL_MIGRATIONS: &'static [&'static Migration] = $migrations_expr;

        pub fn run<C: MigrationConnection>(conn: &C) -> Result<(), RunMigrationsError> {
            run_with_output(conn, &mut io::sink())
        }

        pub fn run_with_output<C: MigrationConnection>(conn: &C, out: &mut io::Write)
            -> Result<(), RunMigrationsError>
        {
            run_migrations(conn, ALL_MIGRATIONS.iter().map(|v| *v), out)
        }
    }).unwrap();
    MacEager::items(SmallVector::one(item))
}

fn migrations_directory_from_args(
    cx: &mut ExtCtxt,
    sp: Span,
    tts: &[tokenstream::TokenTree],
) -> Result<PathBuf, Box<Error>> {
    let callsite_file = cx.codemap().span_to_filename(sp);
    let relative_path_to_migrations = if tts.is_empty() {
        None
    } else {
        match get_single_str_from_tts(cx, sp, tts, "embed_migrations!") {
            None => return Err("Usage error".into()),
            value => value,
        }
    };
    let callsite_path = Path::new(&callsite_file);
    let migrations_path = relative_path_to_migrations.as_ref().map(Path::new);
    resolve_migrations_directory(callsite_path, migrations_path)
}

fn resolve_migrations_directory(
    callsite: &Path,
    relative_path_to_migrations: Option<&Path>,
) -> Result<PathBuf, Box<Error>> {
    let callsite_dir = callsite.parent().unwrap();

    let result = match relative_path_to_migrations {
        Some(dir) => callsite_dir.join(dir),
        None => try!(search_for_migrations_directory(&callsite_dir)),
    };

    result.canonicalize().map_err(Into::into)
}

fn migration_literals_from_path(
    cx: &ExtCtxt,
    sp: Span,
    path: &Path,
) -> Result<P<ast::Expr>, Box<Error>> {
    use diesel::migrations::migration_paths_in_directory;

    let exprs = try!(migration_paths_in_directory(&path)).into_iter()
        .map(|e| migration_literal_from_path(cx, &e.path()))
        .collect();
    Ok(cx.expr_vec_slice(sp, try!(exprs)))
}

fn migration_literal_from_path(
    cx: &ExtCtxt,
    path: &Path,
) -> Result<P<ast::Expr>, Box<Error>> {
    use diesel::migrations::version_from_path;

    let version = try!(version_from_path(path));
    let sql_file = path.join("up.sql");
    let sql_file_path = sql_file.to_string_lossy();

    Ok(quote_expr!(cx, &EmbeddedMigration {
        version: $version,
        up_sql: include_str!($sql_file_path),
    }))
}

#[cfg(test)]
mod tests {
    extern crate tempdir;

    use self::tempdir::TempDir;
    use std::fs;
    use std::path::Path;
    use super::resolve_migrations_directory;

    #[test]
    fn migrations_directory_resolved_relative_to_callsite_dir() {
        let tempdir = TempDir::new("diesel").unwrap();
        fs::create_dir_all(tempdir.path().join("foo/special_migrations")).unwrap();
        let callsite = tempdir.path().join("foo/bar.rs");
        let relative_path = Some(Path::new("special_migrations"));

        assert_eq!(
            tempdir.path().join("foo/special_migrations").canonicalize().ok(),
            resolve_migrations_directory(&callsite, relative_path).ok()
        );
    }

    #[test]
    fn migrations_directory_canonicalizes_result() {
        let tempdir = TempDir::new("diesel").unwrap();
        fs::create_dir_all(tempdir.path().join("foo/migrations/bar")).unwrap();
        fs::create_dir_all(tempdir.path().join("foo/bar")).unwrap();
        let callsite = tempdir.path().join("foo/bar/baz.rs");
        let relative_path = Some(Path::new("../migrations/bar"));

        assert_eq!(
            tempdir.path().join("foo/migrations/bar").canonicalize().ok(),
            resolve_migrations_directory(&callsite, relative_path).ok()
        );
    }

    #[test]
    fn migrations_directory_defaults_to_searching_migrations_path() {
        let tempdir = TempDir::new("diesel").unwrap();
        fs::create_dir_all(tempdir.path().join("foo/migrations")).unwrap();
        fs::create_dir_all(tempdir.path().join("foo/bar")).unwrap();
        let callsite = tempdir.path().join("foo/bar/baz.rs");

        assert_eq!(
            tempdir.path().join("foo/migrations").canonicalize().ok(),
            resolve_migrations_directory(&callsite, None).ok()
        );
    }

    #[test]
    fn migrations_directory_allows_no_parent_dir_for_callsite() {
        let tempdir = TempDir::new("diesel").unwrap();
        fs::create_dir_all(tempdir.path().join("special_migrations")).unwrap();
        let callsite = tempdir.path().join("bar.rs");
        let relative_path = Some(Path::new("special_migrations"));
        assert_eq!(
            tempdir.path().join("special_migrations").canonicalize().ok(),
            resolve_migrations_directory(&callsite, relative_path).ok()
        );
    }
}
<commit_msg>Prevent unused variable warning in embed_migrations macro.<commit_after>use diesel::migrations::search_for_migrations_directory;
use std::error::Error;
use std::path::{Path, PathBuf};
use syntax::ast;
use syntax::codemap::Span;
use syntax::ext::base::*;
use syntax::util::small_vector::SmallVector;
use syntax::ptr::P;
use syntax::ext::build::AstBuilder;
use syntax::tokenstream;

pub fn expand_embed_migrations<'cx>(
    cx: &'cx mut ExtCtxt,
    sp: Span,
    tts: &[tokenstream::TokenTree]
) -> Box<MacResult+'cx> {
    let migrations_expr = migrations_directory_from_args(cx, sp, tts)
        .and_then(|d| migration_literals_from_path(cx, sp, &d));
    let migrations_expr = match migrations_expr {
        Err(e) => {
            cx.span_err(sp, &format!("Error reading migrations: {}", e));
            return DummyResult::expr(sp);
        }
        Ok(v) => v,
    };

    let item = quote_item!(cx, mod embedded_migrations {
        extern crate diesel;

        use self::diesel::migrations::*;
        use self::diesel::connection::SimpleConnection;
        use std::io;

        struct EmbeddedMigration {
            version: &'static str,
            up_sql: &'static str,
        }

        impl Migration for EmbeddedMigration {
            fn version(&self) -> &str {
                self.version
            }

            fn run(&self, conn: &SimpleConnection) -> Result<(), RunMigrationsError> {
                conn.batch_execute(self.up_sql).map_err(Into::into)
            }

            fn revert(&self, _conn: &SimpleConnection) -> Result<(), RunMigrationsError> {
                unreachable!()
            }
        }

        const ALL_MIGRATIONS: &'static [&'static Migration] = $migrations_expr;

        pub fn run<C: MigrationConnection>(conn: &C) -> Result<(), RunMigrationsError> {
            run_with_output(conn, &mut io::sink())
        }

        pub fn run_with_output<C: MigrationConnection>(conn: &C, out: &mut io::Write)
            -> Result<(), RunMigrationsError>
        {
            run_migrations(conn, ALL_MIGRATIONS.iter().map(|v| *v), out)
        }
    }).unwrap();
    MacEager::items(SmallVector::one(item))
}

fn migrations_directory_from_args(
    cx: &mut ExtCtxt,
    sp: Span,
    tts: &[tokenstream::TokenTree],
) -> Result<PathBuf, Box<Error>> {
    let callsite_file = cx.codemap().span_to_filename(sp);
    let relative_path_to_migrations = if tts.is_empty() {
        None
    } else {
        match get_single_str_from_tts(cx, sp, tts, "embed_migrations!") {
            None => return Err("Usage error".into()),
            value => value,
        }
    };
    let callsite_path = Path::new(&callsite_file);
    let migrations_path = relative_path_to_migrations.as_ref().map(Path::new);
    resolve_migrations_directory(callsite_path, migrations_path)
}

fn resolve_migrations_directory(
    callsite: &Path,
    relative_path_to_migrations: Option<&Path>,
) -> Result<PathBuf, Box<Error>> {
    let callsite_dir = callsite.parent().unwrap();

    let result = match relative_path_to_migrations {
        Some(dir) => callsite_dir.join(dir),
        None => try!(search_for_migrations_directory(&callsite_dir)),
    };

    result.canonicalize().map_err(Into::into)
}

fn migration_literals_from_path(
    cx: &ExtCtxt,
    sp: Span,
    path: &Path,
) -> Result<P<ast::Expr>, Box<Error>> {
    use diesel::migrations::migration_paths_in_directory;

    let exprs = try!(migration_paths_in_directory(&path)).into_iter()
        .map(|e| migration_literal_from_path(cx, &e.path()))
        .collect();
    Ok(cx.expr_vec_slice(sp, try!(exprs)))
}

fn migration_literal_from_path(
    cx: &ExtCtxt,
    path: &Path,
) -> Result<P<ast::Expr>, Box<Error>> {
    use diesel::migrations::version_from_path;

    let version = try!(version_from_path(path));
    let sql_file = path.join("up.sql");
    let sql_file_path = sql_file.to_string_lossy();

    Ok(quote_expr!(cx, &EmbeddedMigration {
        version: $version,
        up_sql: include_str!($sql_file_path),
    }))
}

#[cfg(test)]
mod tests {
    extern crate tempdir;

    use self::tempdir::TempDir;
    use std::fs;
    use std::path::Path;
    use super::resolve_migrations_directory;

    #[test]
    fn migrations_directory_resolved_relative_to_callsite_dir() {
        let tempdir = TempDir::new("diesel").unwrap();
        fs::create_dir_all(tempdir.path().join("foo/special_migrations")).unwrap();
        let callsite = tempdir.path().join("foo/bar.rs");
        let relative_path = Some(Path::new("special_migrations"));

        assert_eq!(
            tempdir.path().join("foo/special_migrations").canonicalize().ok(),
            resolve_migrations_directory(&callsite, relative_path).ok()
        );
    }

    #[test]
    fn migrations_directory_canonicalizes_result() {
        let tempdir = TempDir::new("diesel").unwrap();
        fs::create_dir_all(tempdir.path().join("foo/migrations/bar")).unwrap();
        fs::create_dir_all(tempdir.path().join("foo/bar")).unwrap();
        let callsite = tempdir.path().join("foo/bar/baz.rs");
        let relative_path = Some(Path::new("../migrations/bar"));

        assert_eq!(
            tempdir.path().join("foo/migrations/bar").canonicalize().ok(),
            resolve_migrations_directory(&callsite, relative_path).ok()
        );
    }

    #[test]
    fn migrations_directory_defaults_to_searching_migrations_path() {
        let tempdir = TempDir::new("diesel").unwrap();
        fs::create_dir_all(tempdir.path().join("foo/migrations")).unwrap();
        fs::create_dir_all(tempdir.path().join("foo/bar")).unwrap();
        let callsite = tempdir.path().join("foo/bar/baz.rs");

        assert_eq!(
            tempdir.path().join("foo/migrations").canonicalize().ok(),
            resolve_migrations_directory(&callsite, None).ok()
        );
    }

    #[test]
    fn migrations_directory_allows_no_parent_dir_for_callsite() {
        let tempdir = TempDir::new("diesel").unwrap();
        fs::create_dir_all(tempdir.path().join("special_migrations")).unwrap();
        let callsite = tempdir.path().join("bar.rs");
        let relative_path = Some(Path::new("special_migrations"));
        assert_eq!(
            tempdir.path().join("special_migrations").canonicalize().ok(),
            resolve_migrations_directory(&callsite, relative_path).ok()
        );
    }
}
<|endoftext|>


<commit_before>use crate::win32::menu as mh;
use crate::NwgError;
use super::{ControlBase, ControlHandle};
use std::ptr;

const NOT_BOUND: &'static str = "Menu/MenuItem is not yet bound to a winapi object";
const BAD_HANDLE: &'static str = "INTERNAL ERROR: Menu/MenuItem handle is not HMENU!";

bitflags! {
    /**
        Menu flags to use with the `Menu::popup_with_flags` function.
        Using `PopupMenuFlags::empty` is the same as `ALIGN_LEFT|ALIGN_TOP|LEFT_BUTTON`

        Aligment flags:

        * ALIGN_LEFT:     Positions the shortcut menu so that its left side is aligned with the coordinate specified by the x parameter.
        * ALIGN_H_CENTER: Centers the shortcut menu horizontally relative to the coordinate specified by the x parameter.
        * ALIGN_RIGHT:    Positions the shortcut menu so that its right side is aligned with the coordinate specified by the x parameter.
        * ALIGN_BOTTOM:   Positions the shortcut menu so that its bottom side is aligned with the coordinate specified by the y parameter.
        * ALIGN_TOP:      Positions the shortcut menu so that its top side is aligned with the coordinate specified by the y parameter.
        * ALIGN_V_CENTER: Centers the shortcut menu vertically relative to the coordinate specified by the y parameter.

        Button flags:

        * LEFT_BUTTON:    The user can select menu items with only the left mouse button.
        * RIGHT_BUTTON:   The user can select menu items with both the left **AND** right mouse buttons.

        Animations flags:

        * ANIMATE_NONE:   Displays menu without animation.
        * ANIMATE_RIGHT_TO_LEFT:  Animates the menu from right to left.
        * ANIMATE_LEFT_TO_RIGHT:  Animates the menu from left to right.
        * ANIMATE_BOTTOM_TO_TOP:  Animates the menu from bottom to top.
        * ANIMATE_TOP_TO_BOTTOM: Animates the menu from top to bottom.
    */
    pub struct PopupMenuFlags: u32 {
        const ALIGN_LEFT = 0x0000;
        const ALIGN_H_CENTER = 0x0004;
        const ALIGN_RIGHT = 0x0008;

        const ALIGN_BOTTOM = 0x0020;
        const ALIGN_TOP = 0x0000;
        const ALIGN_V_CENTER = 0x0010;

        const LEFT_BUTTON = 0x0000;
        const RIGHT_BUTTON = 0x0002;

        const ANIMATE_NONE = 0x4000;
        const ANIMATE_RIGHT_TO_LEFT = 0x8000;
        const ANIMATE_LEFT_TO_RIGHT = 0x4000;
        const ANIMATE_BOTTOM_TO_TOP = 0x2000;
        const ANIMATE_TOP_TO_BOTTOM = 0x1000;
    }
}

/**
    A windows menu. Can represent a menu in a window menubar, a context menu, or a submenu in another menu

    Requires the `menu` feature.

    **Builder parameters:**
      - text: The text of the menu, including access key and shortcut label
      - disabled: If the menu can be selected by the user
      - popup: The menu is a context menu
      - parent: A top level window, a menu or None. With a top level window, the menu is added to the menu bar if popup is set to false.

    **Control events:**
      - OnMenuOpen: Sent when a drop-down menu or submenu is about to become active.
      - OnMenuHover: When the user hovers the menu
      - OnMenuEnter: When the user enters the menu. Technically, when the user enters the menu modal loop.
      - OnMenuExit: When the menu is closed. Technically, when the user exits the menu modal loop.

    **Menu Access Keys**

    Menu can have access keys. An access key is an underlined letter in the text of a menu item.
    When a menu is active, the user can select a menu item by pressing the key that corresponds to the item's underlined letter.
    The user makes the menu bar active by pressing the ALT key to highlight the first item on the menu bar.
    A menu is active when it is displayed.

    To create an access key for a menu item, precede any character in the item's text string with an ampersand.
    For example, the text string "&Move" causes the system to underline the letter "M".

    ```rust
    use native_windows_gui as nwg;

    fn menu(menu: &mut nwg::Menu, window: &nwg::Window) -> Result<(), nwg::NwgError> {
        nwg::Menu::builder()
            .text("&Hello")
            .disabled(false)
            .parent(window)
            .build(menu)
    }
    ```

    **Shortcut Label**

    A shortcut label like "Ctrl+O" can be added with the `text` field. By prefixing the shortcut with a tab character `\t` and
    adding it as a suffix to the text label, it will be rendered right-aligned in the menu item.

    For example, an "Exit" menu item could have both an access key "E" and a shortcut label "Alt+F4" with `text: "&Exit\tAlt+F4"`.

    **note:** This will only add a text label to the menu item, the keyboard handling must be done through other means.

    ```rust
    use native_windows_gui as nwg;

    fn menu(menu: &mut nwg::Menu, window: &nwg::Window) -> Result<(), nwg::NwgError> {
        nwg::Menu::builder()
            .text("&Exit\tAlt+F4")
            .disabled(false)
            .parent(window)
            .build(menu)
    }
    ```
*/
#[derive(Default, PartialEq, Eq)]
pub struct Menu {
    pub handle: ControlHandle
}

impl Menu {

    pub fn builder<'a>() -> MenuBuilder<'a> {
        MenuBuilder {
            text: "Menu",
            disabled: false,
            popup: false,
            parent: None
        }
    }

    /// Return true if the control user can interact with the control, return false otherwise
    pub fn enabled(&self) -> bool {
        if self.handle.blank() { panic!("{}", NOT_BOUND); }
        let (parent_handle, handle) = match self.handle {
            ControlHandle::Menu(parent, menu) => (parent, menu),
            ControlHandle::PopMenu(_, _) => { return true; },
            _ => panic!("{}", BAD_HANDLE)
        };

        unsafe { mh::is_menu_enabled(parent_handle, handle) }
    }

    /// Enable or disable the control
    /// A popup menu cannot be disabled
    pub fn set_enabled(&self, v: bool) {
        if self.handle.blank() { panic!("{}", NOT_BOUND); }
        let (parent_handle, handle) = match self.handle {
            ControlHandle::Menu(parent, menu) => (parent, menu),
            ControlHandle::PopMenu(_, _) => { return; },
            _ => panic!("{}", BAD_HANDLE)
        };

        unsafe { mh::enable_menu(parent_handle, handle, v); }
    }

    /// Show a popup menu as the selected position. Do nothing for menubar menu.
    pub fn popup_with_flags(&self, x: i32, y: i32, flags: PopupMenuFlags) {
        use winapi::um::winuser::{TrackPopupMenu, SetForegroundWindow};
        use winapi::ctypes::c_int;

        if self.handle.blank() { panic!("Menu is not bound"); }
        let (parent_handle, handle) = match self.handle.pop_hmenu() {
            Some(v) => v,
            None => { return; }
        };

        unsafe {
            SetForegroundWindow(parent_handle);
            TrackPopupMenu(
                handle,
                flags.bits(),
                x as c_int,
                y as c_int,
                0,
                parent_handle,
                ptr::null()
            );
        }
    }

    /// Show a popup menu as the selected position. Do nothing for menubar menu.
    pub fn popup(&self, x: i32, y: i32) {
        self.popup_with_flags(x, y, PopupMenuFlags::empty())
    }

}

impl Drop for Menu {
    fn drop(&mut self) {
        self.handle.destroy();
    }
}

pub struct MenuBuilder<'a> {
    text: &'a str,
    disabled: bool,
    popup: bool,
    parent: Option<ControlHandle>
}

impl<'a> MenuBuilder<'a> {

    pub fn text(mut self, text: &'a str) -> MenuBuilder<'a> {
        self.text = text;
        self
    }

    pub fn disabled(mut self, disabled: bool) -> MenuBuilder<'a> {
        self.disabled = disabled;
        self
    }

    pub fn popup(mut self, popup: bool) -> MenuBuilder<'a> {
        self.popup = popup;
        self
    }

    pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> MenuBuilder<'a> {
        self.parent = Some(p.into());
        self
    }

    pub fn build(self, menu: &mut Menu) -> Result<(), NwgError> {
        if self.parent.is_none() {
            return Err(NwgError::no_parent_menu());
        }

        menu.handle = ControlBase::build_hmenu()
            .text(self.text)
            .item(false)
            .popup(self.popup)
            .parent(self.parent.unwrap())
            .build()?;

        if self.disabled {
            menu.set_enabled(false)
        }

        Ok(())
    }
}

/**
    A windows menu item. Can be added to a menubar or another menu.

    Requires the `menu` feature.

   **Builder parameters:**
      - text: The text of the menu item
      - disabled: If the item can be selected by the user
      - check: If the item should have a check mark next to it.
      - parent: A top level window or a menu. With a top level window, the menu item is added to the menu bar.

   **Control events:**
      - OnMenuItemSelected: When a menu item is selected. This can be done by clicking or using the hot-key.
      - OnMenuHover: When the user hovers the menu

    **Menu Access Keys**

    Just like Menus, menu items can have access keys. An access key is an underlined letter in the text of a menu item.
    When a menu is active, the user can select a menu item by pressing the key that corresponds to the item's underlined letter.
    The user makes the menu bar active by pressing the ALT key to highlight the first item on the menu bar.
    A menu is active when it is displayed.

    To create an access key for a menu item, precede any character in the item's text string with an ampersand.
    For example, the text string "&Move" causes the system to underline the letter "M".

    ```rust
    use native_windows_gui as nwg;

    fn menu_item(item: &mut nwg::MenuItem, menu: &nwg::Menu) -> Result<(), nwg::NwgError> {
        nwg::MenuItem::builder()
            .text("&Hello")
            .disabled(true)
            .parent(menu)
            .build(item)
    }
    ```
*/
#[derive(Default, Debug, PartialEq, Eq)]
pub struct MenuItem {
    pub handle: ControlHandle
}

impl MenuItem {

    pub fn builder<'a>() -> MenuItemBuilder<'a> {
        MenuItemBuilder {
            text: "Menu Item",
            disabled: false,
            check: false,
            parent: None
        }
    }

    /// Return true if the control user can interact with the control, return false otherwise
    pub fn enabled(&self) -> bool {
        if self.handle.blank() { panic!("{}", NOT_BOUND); }
        let (parent_handle, id) = self.handle.hmenu_item().expect(BAD_HANDLE);

        unsafe { mh::is_menuitem_enabled(parent_handle, None, Some(id)) }
    }

    /// Enable or disable the control
    pub fn set_enabled(&self, v: bool) {
        if self.handle.blank() { panic!("{}", NOT_BOUND); }
        let (parent_handle, id) = self.handle.hmenu_item().expect(BAD_HANDLE);

        unsafe { mh::enable_menuitem(parent_handle, None, Some(id), v); }
    }

    /// Sets the check state of a menu item
    pub fn set_checked(&self, check: bool) {
        if self.handle.blank() { panic!("{}", NOT_BOUND); }
        let (parent_handle, id) = self.handle.hmenu_item().expect(BAD_HANDLE);

        unsafe { mh::check_menu_item(parent_handle, id, check); }
    }

    /// Returns the check state of a menu item
    pub fn checked(&self) -> bool {
        if self.handle.blank() { panic!("{}", NOT_BOUND); }
        let (parent_handle, id) = self.handle.hmenu_item().expect(BAD_HANDLE);

        unsafe { mh::menu_item_checked(parent_handle, id) }
    }

}

impl Drop for MenuItem {
    fn drop(&mut self) {
        self.handle.destroy();
    }
}

pub struct MenuItemBuilder<'a> {
    text: &'a str,
    disabled: bool,
    check: bool,
    parent: Option<ControlHandle>
}

impl<'a> MenuItemBuilder<'a> {

    pub fn text(mut self, text: &'a str) -> MenuItemBuilder<'a> {
        self.text = text;
        self
    }

    pub fn disabled(mut self, disabled: bool) -> MenuItemBuilder<'a> {
        self.disabled = disabled;
        self
    }

    pub fn check(mut self, check: bool) -> MenuItemBuilder<'a> {
        self.check = check;
        self
    }

    pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> MenuItemBuilder<'a> {
        self.parent = Some(p.into());
        self
    }

    pub fn build(self, item: &mut MenuItem) -> Result<(), NwgError> {
        if self.parent.is_none() {
            return Err(NwgError::no_parent_menu());
        }

        item.handle = ControlBase::build_hmenu()
            .text(self.text)
            .item(true)
            .parent(self.parent.unwrap())
            .build()?;

        if self.disabled {
            item.set_enabled(false);
        }

        if self.check {
            item.set_checked(true);
        }

        Ok(())
    }
}

/**
    A menu separator. Can be added between two menu item to separte them. Cannot be added to a menubar.

    Requires the `menu` feature.

    **Builder parameters:**
      - parent: A top level window or a menu. With a top level window, the menu item is added to the menu bar.

   **Control events:**
      - OnMenuHover: When the user hovers the menu

    ```rust
    use native_windows_gui as nwg;

    fn separator(sep: &mut nwg::MenuSeparator, menu: &nwg::Menu) -> Result<(), nwg::NwgError> {
        nwg::MenuSeparator::builder()
            .parent(menu)
            .build(sep)
    }
    ```
*/
#[derive(Default, Debug, PartialEq, Eq)]
pub struct MenuSeparator {
    pub handle: ControlHandle
}

impl MenuSeparator {

    pub fn builder() -> MenuSeparatorBuilder {
        MenuSeparatorBuilder {
            parent: None
        }
    }

}

pub struct MenuSeparatorBuilder {
    parent: Option<ControlHandle>
}

impl MenuSeparatorBuilder {

    pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> MenuSeparatorBuilder {
        self.parent = Some(p.into());
        self
    }

    pub fn build(self, sep: &mut MenuSeparator) -> Result<(), NwgError> {
        if self.parent.is_none() {
            return Err(NwgError::no_parent_menu());
        }

        sep.handle = ControlBase::build_hmenu()
            .separator(true)
            .parent(self.parent.unwrap())
            .build()?;

        Ok(())
    }
}

impl Drop for MenuSeparator {
    fn drop(&mut self) {
        self.handle.destroy();
    }
}
<commit_msg>Move documentation to right place<commit_after>use crate::win32::menu as mh;
use crate::NwgError;
use super::{ControlBase, ControlHandle};
use std::ptr;

const NOT_BOUND: &'static str = "Menu/MenuItem is not yet bound to a winapi object";
const BAD_HANDLE: &'static str = "INTERNAL ERROR: Menu/MenuItem handle is not HMENU!";

bitflags! {
    /**
        Menu flags to use with the `Menu::popup_with_flags` function.
        Using `PopupMenuFlags::empty` is the same as `ALIGN_LEFT|ALIGN_TOP|LEFT_BUTTON`

        Aligment flags:

        * ALIGN_LEFT:     Positions the shortcut menu so that its left side is aligned with the coordinate specified by the x parameter.
        * ALIGN_H_CENTER: Centers the shortcut menu horizontally relative to the coordinate specified by the x parameter.
        * ALIGN_RIGHT:    Positions the shortcut menu so that its right side is aligned with the coordinate specified by the x parameter.
        * ALIGN_BOTTOM:   Positions the shortcut menu so that its bottom side is aligned with the coordinate specified by the y parameter.
        * ALIGN_TOP:      Positions the shortcut menu so that its top side is aligned with the coordinate specified by the y parameter.
        * ALIGN_V_CENTER: Centers the shortcut menu vertically relative to the coordinate specified by the y parameter.

        Button flags:

        * LEFT_BUTTON:    The user can select menu items with only the left mouse button.
        * RIGHT_BUTTON:   The user can select menu items with both the left **AND** right mouse buttons.

        Animations flags:

        * ANIMATE_NONE:   Displays menu without animation.
        * ANIMATE_RIGHT_TO_LEFT:  Animates the menu from right to left.
        * ANIMATE_LEFT_TO_RIGHT:  Animates the menu from left to right.
        * ANIMATE_BOTTOM_TO_TOP:  Animates the menu from bottom to top.
        * ANIMATE_TOP_TO_BOTTOM: Animates the menu from top to bottom.
    */
    pub struct PopupMenuFlags: u32 {
        const ALIGN_LEFT = 0x0000;
        const ALIGN_H_CENTER = 0x0004;
        const ALIGN_RIGHT = 0x0008;

        const ALIGN_BOTTOM = 0x0020;
        const ALIGN_TOP = 0x0000;
        const ALIGN_V_CENTER = 0x0010;

        const LEFT_BUTTON = 0x0000;
        const RIGHT_BUTTON = 0x0002;

        const ANIMATE_NONE = 0x4000;
        const ANIMATE_RIGHT_TO_LEFT = 0x8000;
        const ANIMATE_LEFT_TO_RIGHT = 0x4000;
        const ANIMATE_BOTTOM_TO_TOP = 0x2000;
        const ANIMATE_TOP_TO_BOTTOM = 0x1000;
    }
}

/**
    A windows menu. Can represent a menu in a window menubar, a context menu, or a submenu in another menu

    Requires the `menu` feature.

    **Builder parameters:**
      - text: The text of the menu
      - disabled: If the menu can be selected by the user
      - popup: The menu is a context menu
      - parent: A top level window, a menu or None. With a top level window, the menu is added to the menu bar if popup is set to false.

    **Control events:**
      - OnMenuOpen: Sent when a drop-down menu or submenu is about to become active.
      - OnMenuHover: When the user hovers the menu
      - OnMenuEnter: When the user enters the menu. Technically, when the user enters the menu modal loop.
      - OnMenuExit: When the menu is closed. Technically, when the user exits the menu modal loop.

    **Menu Access Keys**

    Menu can have access keys. An access key is an underlined letter in the text of a menu item.
    When a menu is active, the user can select a menu item by pressing the key that corresponds to the item's underlined letter.
    The user makes the menu bar active by pressing the ALT key to highlight the first item on the menu bar.
    A menu is active when it is displayed.

    To create an access key for a menu item, precede any character in the item's text string with an ampersand.
    For example, the text string "&Move" causes the system to underline the letter "M".

    ```rust
    use native_windows_gui as nwg;

    fn menu(menu: &mut nwg::Menu, window: &nwg::Window) -> Result<(), nwg::NwgError> {
        nwg::Menu::builder()
            .text("&Hello")
            .disabled(false)
            .parent(window)
            .build(menu)
    }
    ```
*/
#[derive(Default, PartialEq, Eq)]
pub struct Menu {
    pub handle: ControlHandle
}

impl Menu {

    pub fn builder<'a>() -> MenuBuilder<'a> {
        MenuBuilder {
            text: "Menu",
            disabled: false,
            popup: false,
            parent: None
        }
    }

    /// Return true if the control user can interact with the control, return false otherwise
    pub fn enabled(&self) -> bool {
        if self.handle.blank() { panic!("{}", NOT_BOUND); }
        let (parent_handle, handle) = match self.handle {
            ControlHandle::Menu(parent, menu) => (parent, menu),
            ControlHandle::PopMenu(_, _) => { return true; },
            _ => panic!("{}", BAD_HANDLE)
        };

        unsafe { mh::is_menu_enabled(parent_handle, handle) }
    }

    /// Enable or disable the control
    /// A popup menu cannot be disabled
    pub fn set_enabled(&self, v: bool) {
        if self.handle.blank() { panic!("{}", NOT_BOUND); }
        let (parent_handle, handle) = match self.handle {
            ControlHandle::Menu(parent, menu) => (parent, menu),
            ControlHandle::PopMenu(_, _) => { return; },
            _ => panic!("{}", BAD_HANDLE)
        };

        unsafe { mh::enable_menu(parent_handle, handle, v); }
    }

    /// Show a popup menu as the selected position. Do nothing for menubar menu.
    pub fn popup_with_flags(&self, x: i32, y: i32, flags: PopupMenuFlags) {
        use winapi::um::winuser::{TrackPopupMenu, SetForegroundWindow};
        use winapi::ctypes::c_int;

        if self.handle.blank() { panic!("Menu is not bound"); }
        let (parent_handle, handle) = match self.handle.pop_hmenu() {
            Some(v) => v,
            None => { return; }
        };

        unsafe {
            SetForegroundWindow(parent_handle);
            TrackPopupMenu(
                handle,
                flags.bits(),
                x as c_int,
                y as c_int,
                0,
                parent_handle,
                ptr::null()
            );
        }
    }

    /// Show a popup menu as the selected position. Do nothing for menubar menu.
    pub fn popup(&self, x: i32, y: i32) {
        self.popup_with_flags(x, y, PopupMenuFlags::empty())
    }

}

impl Drop for Menu {
    fn drop(&mut self) {
        self.handle.destroy();
    }
}

pub struct MenuBuilder<'a> {
    text: &'a str,
    disabled: bool,
    popup: bool,
    parent: Option<ControlHandle>
}

impl<'a> MenuBuilder<'a> {

    pub fn text(mut self, text: &'a str) -> MenuBuilder<'a> {
        self.text = text;
        self
    }

    pub fn disabled(mut self, disabled: bool) -> MenuBuilder<'a> {
        self.disabled = disabled;
        self
    }

    pub fn popup(mut self, popup: bool) -> MenuBuilder<'a> {
        self.popup = popup;
        self
    }

    pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> MenuBuilder<'a> {
        self.parent = Some(p.into());
        self
    }

    pub fn build(self, menu: &mut Menu) -> Result<(), NwgError> {
        if self.parent.is_none() {
            return Err(NwgError::no_parent_menu());
        }

        menu.handle = ControlBase::build_hmenu()
            .text(self.text)
            .item(false)
            .popup(self.popup)
            .parent(self.parent.unwrap())
            .build()?;

        if self.disabled {
            menu.set_enabled(false)
        }

        Ok(())
    }
}

/**
    A windows menu item. Can be added to a menubar or another menu.

    Requires the `menu` feature.

   **Builder parameters:**
      - text: The text of the menu, including access key and shortcut label
      - disabled: If the item can be selected by the user
      - check: If the item should have a check mark next to it.
      - parent: A top level window or a menu. With a top level window, the menu item is added to the menu bar.

   **Control events:**
      - OnMenuItemSelected: When a menu item is selected. This can be done by clicking or using the hot-key.
      - OnMenuHover: When the user hovers the menu

    **Menu Access Keys**

    Just like Menus, menu items can have access keys. An access key is an underlined letter in the text of a menu item.
    When a menu is active, the user can select a menu item by pressing the key that corresponds to the item's underlined letter.
    The user makes the menu bar active by pressing the ALT key to highlight the first item on the menu bar.
    A menu is active when it is displayed.

    To create an access key for a menu item, precede any character in the item's text string with an ampersand.
    For example, the text string "&Move" causes the system to underline the letter "M".

    ```rust
    use native_windows_gui as nwg;

    fn menu_item(item: &mut nwg::MenuItem, menu: &nwg::Menu) -> Result<(), nwg::NwgError> {
        nwg::MenuItem::builder()
            .text("&Hello")
            .disabled(true)
            .parent(menu)
            .build(item)
    }
    ```

    **Shortcut Label**

    A shortcut label like "Ctrl+O" can be added with the `text` field. By prefixing the shortcut with a tab character `\t` and
    adding it as a suffix to the text label, it will be rendered right-aligned in the menu item.

    For example, an "Exit" menu item could have both an access key "E" and a shortcut label "Alt+F4" with `text: "&Exit\tAlt+F4"`.

    **note:** This will only add a text label to the menu item, the keyboard handling must be done through other means.

    ```rust
    use native_windows_gui as nwg;

    fn menu(menu: &mut nwg::Menu, window: &nwg::Window) -> Result<(), nwg::NwgError> {
        nwg::Menu::builder()
            .text("&Exit\tAlt+F4")
            .disabled(false)
            .parent(window)
            .build(menu)
    }
    ```
*/
#[derive(Default, Debug, PartialEq, Eq)]
pub struct MenuItem {
    pub handle: ControlHandle
}

impl MenuItem {

    pub fn builder<'a>() -> MenuItemBuilder<'a> {
        MenuItemBuilder {
            text: "Menu Item",
            disabled: false,
            check: false,
            parent: None
        }
    }

    /// Return true if the control user can interact with the control, return false otherwise
    pub fn enabled(&self) -> bool {
        if self.handle.blank() { panic!("{}", NOT_BOUND); }
        let (parent_handle, id) = self.handle.hmenu_item().expect(BAD_HANDLE);

        unsafe { mh::is_menuitem_enabled(parent_handle, None, Some(id)) }
    }

    /// Enable or disable the control
    pub fn set_enabled(&self, v: bool) {
        if self.handle.blank() { panic!("{}", NOT_BOUND); }
        let (parent_handle, id) = self.handle.hmenu_item().expect(BAD_HANDLE);

        unsafe { mh::enable_menuitem(parent_handle, None, Some(id), v); }
    }

    /// Sets the check state of a menu item
    pub fn set_checked(&self, check: bool) {
        if self.handle.blank() { panic!("{}", NOT_BOUND); }
        let (parent_handle, id) = self.handle.hmenu_item().expect(BAD_HANDLE);

        unsafe { mh::check_menu_item(parent_handle, id, check); }
    }

    /// Returns the check state of a menu item
    pub fn checked(&self) -> bool {
        if self.handle.blank() { panic!("{}", NOT_BOUND); }
        let (parent_handle, id) = self.handle.hmenu_item().expect(BAD_HANDLE);

        unsafe { mh::menu_item_checked(parent_handle, id) }
    }

}

impl Drop for MenuItem {
    fn drop(&mut self) {
        self.handle.destroy();
    }
}

pub struct MenuItemBuilder<'a> {
    text: &'a str,
    disabled: bool,
    check: bool,
    parent: Option<ControlHandle>
}

impl<'a> MenuItemBuilder<'a> {

    pub fn text(mut self, text: &'a str) -> MenuItemBuilder<'a> {
        self.text = text;
        self
    }

    pub fn disabled(mut self, disabled: bool) -> MenuItemBuilder<'a> {
        self.disabled = disabled;
        self
    }

    pub fn check(mut self, check: bool) -> MenuItemBuilder<'a> {
        self.check = check;
        self
    }

    pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> MenuItemBuilder<'a> {
        self.parent = Some(p.into());
        self
    }

    pub fn build(self, item: &mut MenuItem) -> Result<(), NwgError> {
        if self.parent.is_none() {
            return Err(NwgError::no_parent_menu());
        }

        item.handle = ControlBase::build_hmenu()
            .text(self.text)
            .item(true)
            .parent(self.parent.unwrap())
            .build()?;

        if self.disabled {
            item.set_enabled(false);
        }

        if self.check {
            item.set_checked(true);
        }

        Ok(())
    }
}

/**
    A menu separator. Can be added between two menu item to separte them. Cannot be added to a menubar.

    Requires the `menu` feature.

    **Builder parameters:**
      - parent: A top level window or a menu. With a top level window, the menu item is added to the menu bar.

   **Control events:**
      - OnMenuHover: When the user hovers the menu

    ```rust
    use native_windows_gui as nwg;

    fn separator(sep: &mut nwg::MenuSeparator, menu: &nwg::Menu) -> Result<(), nwg::NwgError> {
        nwg::MenuSeparator::builder()
            .parent(menu)
            .build(sep)
    }
    ```
*/
#[derive(Default, Debug, PartialEq, Eq)]
pub struct MenuSeparator {
    pub handle: ControlHandle
}

impl MenuSeparator {

    pub fn builder() -> MenuSeparatorBuilder {
        MenuSeparatorBuilder {
            parent: None
        }
    }

}

pub struct MenuSeparatorBuilder {
    parent: Option<ControlHandle>
}

impl MenuSeparatorBuilder {

    pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> MenuSeparatorBuilder {
        self.parent = Some(p.into());
        self
    }

    pub fn build(self, sep: &mut MenuSeparator) -> Result<(), NwgError> {
        if self.parent.is_none() {
            return Err(NwgError::no_parent_menu());
        }

        sep.handle = ControlBase::build_hmenu()
            .separator(true)
            .parent(self.parent.unwrap())
            .build()?;

        Ok(())
    }
}

impl Drop for MenuSeparator {
    fn drop(&mut self) {
        self.handle.destroy();
    }
}
<|endoftext|>


<commit_before>//! Low-level bindings for the c-ares library.
//!
//! In most cases this crate should not be used directly.  The c-ares crate
//! provides a safe wrapper and should be preferred wherever possible.

extern crate libc;
extern crate c_types;

#[cfg(windows)]
extern crate winapi;

mod ffi;
mod constants;

pub use constants::*;
pub use ffi::*;
<commit_msg>alphabetical<commit_after>//! Low-level bindings for the c-ares library.
//!
//! In most cases this crate should not be used directly.  The c-ares crate
//! provides a safe wrapper and should be preferred wherever possible.

extern crate libc;
extern crate c_types;

#[cfg(windows)]
extern crate winapi;

mod constants;
mod ffi;

pub use constants::*;
pub use ffi::*;
<|endoftext|>


<commit_before>extern crate toml;

use std::path::PathBuf;
use super::util;
use std::error::Error;
//[replay]
//browser = "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe"
//url = "localhost"
//port = "8000"
#[derive(Deserialize, Debug)]
pub struct ScaiiConfig {
    replay: Option<ReplayConfig>,
}
#[derive(Deserialize, Debug)]
struct ReplayConfig {
    #[serde(default = "default_browser")]
    browser: String,
    #[serde(default = "default_url")]
    url: String,
    #[serde(default = "default_port")]
    port: String,
}

#[cfg(target_os = "windows")]
fn default_browser() -> String {
    "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe".to_string()
}
#[cfg(target_os = "macos")]
fn default_browser() -> String {
    "open".to_string(); // will open url in safari
}
#[cfg(target_os = "linux")]
fn default_browser() -> String {
    "/usr/bin/chrome".to_string()
}

fn default_url() -> String {
    "localhost".to_string()
}

fn default_port() -> String {
    "8000".to_string()
}

// fn get_scaii_root() -> Result<PathBuf, Box<Error>> {
//     use std::env;
//     //look at current dir, see if peer directories are as expected, if so use parent.
//     let mut parent_dir: PathBuf = env::current_dir()?;
//     // find parent
//     parent_dir.pop();

//     let mut core_dir = parent_dir.clone();
//     core_dir.push("core");
//     if !core_dir.exists() {
//         return Err(Box::new(util::ScaiiError::new("core directory could not be found, coult not verify SCAII_ROOT")));
//     }
//     let mut common_protos_dir = parent_dir.clone();
//     common_protos_dir.push("common_protos");
//     if !common_protos_dir.exists() {
//         return Err(Box::new(ScaiiError::new("common_protos directory could not be found, coult not verify SCAII_ROOT")));
//     }
//     Ok(parent_dir)
// }
#[allow(unused_assignments)]
pub fn load_scaii_config() -> ScaiiConfig {
    use std::fs::File;
    use std::io::prelude::*;
    // create the path to cfg.toml
    let scaii_root_result = util::get_scaii_root();
    let mut config_file_pathbuf: PathBuf = PathBuf::new();
    match scaii_root_result {
        Ok(root) => {
            config_file_pathbuf = root;
        }
        Err(_) => {
            panic!("Could not determine SCAII root directory while loading config file");
        }
    }
    config_file_pathbuf.push("cfg.toml");
    // try loading the cfg.toml file
    let file_open_result = File::open(config_file_pathbuf.as_path());
    let default_scaii_config = get_default_scaii_config();
    match file_open_result {
        Ok(mut config_file) => {
            // read the contents
            let mut config_data = String::new();
            let read_result = config_file.read_to_string(&mut config_data);
            match read_result {
                Ok(_) => {}
                //Ok(_) => { return default_scaii_config ;},
                Err(_) => {
                    println!(
                        "Problem reading data from config file {:?}.  Will try default values.",
                        config_file_pathbuf.as_path()
                    );
                    return default_scaii_config;
                }
            }
            let scaii_config_parse_result = toml::from_str(&config_data);

            match scaii_config_parse_result {
                Ok(parsed_scaii_config) => {
                    return parsed_scaii_config;
                }
                Err(error) => {
                    println!(
                        "Problem parsing data from config file {:?}.  Will try default values. {:?}",
                        config_file_pathbuf.as_path(),
                        error.description()
                    );
                    return default_scaii_config;
                }
            }
        }
        Err(error) => {
            println!(
                "config file {:?} not found.  Will use default values. {:?}",
                config_file_pathbuf.as_path(),
                error
            );
            default_scaii_config
        }
    }
}

fn get_default_scaii_config() -> ScaiiConfig {
    ScaiiConfig {
        replay: Some(ReplayConfig {
            browser: default_browser(),
            url: default_url(),
            port: default_port(),
        }),
    }
}

impl ScaiiConfig {
    pub fn get_replay_url(&mut self) -> String {
        match &self.replay {
            &None => default_url(),
            &Some(ref replay) => replay.url.clone(),
        }
    }

    pub fn get_replay_port(&mut self) -> String {
        match &self.replay {
            &None => default_port(),
            &Some(ref replay) => replay.port.clone(),
        }
    }

    pub fn get_replay_browser(&mut self) -> String {
        match &self.replay {
            &None => default_browser(),
            &Some(ref replay) => replay.browser.clone(),
        }
    }

    pub fn get_full_replay_http_url(&mut self) -> String {
        let url = self.get_replay_url();
        let port = self.get_replay_port();
        return format!("http://{}:{}", url, port);
    }
}
<commit_msg>remove dead code<commit_after>extern crate toml;

use std::path::PathBuf;
use super::util;
use std::error::Error;
//[replay]
//browser = "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe"
//url = "localhost"
//port = "8000"
#[derive(Deserialize, Debug)]
pub struct ScaiiConfig {
    replay: Option<ReplayConfig>,
}
#[derive(Deserialize, Debug)]
struct ReplayConfig {
    #[serde(default = "default_browser")]
    browser: String,
    #[serde(default = "default_url")]
    url: String,
    #[serde(default = "default_port")]
    port: String,
}

#[cfg(target_os = "windows")]
fn default_browser() -> String {
    "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe".to_string()
}
#[cfg(target_os = "macos")]
fn default_browser() -> String {
    "open".to_string(); // will open url in safari
}
#[cfg(target_os = "linux")]
fn default_browser() -> String {
    "/usr/bin/chrome".to_string()
}

fn default_url() -> String {
    "localhost".to_string()
}

fn default_port() -> String {
    "8000".to_string()
}

#[allow(unused_assignments)]
pub fn load_scaii_config() -> ScaiiConfig {
    use std::fs::File;
    use std::io::prelude::*;
    // create the path to cfg.toml
    let scaii_root_result = util::get_scaii_root();
    let mut config_file_pathbuf: PathBuf = PathBuf::new();
    match scaii_root_result {
        Ok(root) => {
            config_file_pathbuf = root;
        }
        Err(_) => {
            panic!("Could not determine SCAII root directory while loading config file");
        }
    }
    config_file_pathbuf.push("cfg.toml");
    // try loading the cfg.toml file
    let file_open_result = File::open(config_file_pathbuf.as_path());
    let default_scaii_config = get_default_scaii_config();
    match file_open_result {
        Ok(mut config_file) => {
            // read the contents
            let mut config_data = String::new();
            let read_result = config_file.read_to_string(&mut config_data);
            match read_result {
                Ok(_) => {}
                //Ok(_) => { return default_scaii_config ;},
                Err(_) => {
                    println!(
                        "Problem reading data from config file {:?}.  Will try default values.",
                        config_file_pathbuf.as_path()
                    );
                    return default_scaii_config;
                }
            }
            let scaii_config_parse_result = toml::from_str(&config_data);

            match scaii_config_parse_result {
                Ok(parsed_scaii_config) => {
                    return parsed_scaii_config;
                }
                Err(error) => {
                    println!(
                        "Problem parsing data from config file {:?}.  Will try default values. {:?}",
                        config_file_pathbuf.as_path(),
                        error.description()
                    );
                    return default_scaii_config;
                }
            }
        }
        Err(error) => {
            println!(
                "config file {:?} not found.  Will use default values. {:?}",
                config_file_pathbuf.as_path(),
                error
            );
            default_scaii_config
        }
    }
}

fn get_default_scaii_config() -> ScaiiConfig {
    ScaiiConfig {
        replay: Some(ReplayConfig {
            browser: default_browser(),
            url: default_url(),
            port: default_port(),
        }),
    }
}

impl ScaiiConfig {
    pub fn get_replay_url(&mut self) -> String {
        match &self.replay {
            &None => default_url(),
            &Some(ref replay) => replay.url.clone(),
        }
    }

    pub fn get_replay_port(&mut self) -> String {
        match &self.replay {
            &None => default_port(),
            &Some(ref replay) => replay.port.clone(),
        }
    }

    pub fn get_replay_browser(&mut self) -> String {
        match &self.replay {
            &None => default_browser(),
            &Some(ref replay) => replay.browser.clone(),
        }
    }

    pub fn get_full_replay_http_url(&mut self) -> String {
        let url = self.get_replay_url();
        let port = self.get_replay_port();
        return format!("http://{}:{}", url, port);
    }
}
<|endoftext|>


<commit_before>//! Snapshot testing library, see
//! https://github.com/rust-analyzer/rust-analyzer/pull/5101
use std::{
    collections::HashMap,
    env, fmt, fs, mem,
    ops::Range,
    panic,
    path::{Path, PathBuf},
    sync::Mutex,
};

use difference::Changeset;
use once_cell::sync::Lazy;
use stdx::{lines_with_ends, trim_indent};

const HELP: &str = "
You can update all `expect![[]]` tests by:

    env UPDATE_EXPECT=1 cargo test

To update a single test, place the cursor on `expect` token and use `run` feature of rust-analyzer.
";

fn update_expect() -> bool {
    env::var("UPDATE_EXPECT").is_ok()
}

/// expect![[""]]
#[macro_export]
macro_rules! expect {
    [[$lit:literal]] => {$crate::Expect {
        file: file!(),
        line: line!(),
        column: column!(),
        data: $lit,
    }};
    [[]] => { $crate::expect![[""]] };
}

#[derive(Debug)]
pub struct Expect {
    pub file: &'static str,
    pub line: u32,
    pub column: u32,
    pub data: &'static str,
}

impl Expect {
    pub fn assert_eq(&self, actual: &str) {
        let trimmed = self.trimmed();
        if &trimmed == actual {
            return;
        }
        Runtime::fail(self, &trimmed, actual);
    }
    pub fn assert_debug_eq(&self, actual: &impl fmt::Debug) {
        let actual = format!("{:#?}\n", actual);
        self.assert_eq(&actual)
    }

    fn trimmed(&self) -> String {
        if !self.data.contains('\n') {
            return self.data.to_string();
        }
        trim_indent(self.data)
    }

    fn locate(&self, file: &str) -> Location {
        let mut target_line = None;
        let mut line_start = 0;
        for (i, line) in lines_with_ends(file).enumerate() {
            if i == self.line as usize - 1 {
                let pat = "expect![[";
                let offset = line.find(pat).unwrap();
                let literal_start = line_start + offset + pat.len();
                let indent = line.chars().take_while(|&it| it == ' ').count();
                target_line = Some((literal_start, indent));
                break;
            }
            line_start += line.len();
        }
        let (literal_start, line_indent) = target_line.unwrap();
        let literal_length =
            file[literal_start..].find("]]").expect("Couldn't find matching `]]` for `expect![[`.");
        let literal_range = literal_start..literal_start + literal_length;
        Location { line_indent, literal_range }
    }
}

#[derive(Default)]
struct Runtime {
    help_printed: bool,
    per_file: HashMap<&'static str, FileRuntime>,
}
static RT: Lazy<Mutex<Runtime>> = Lazy::new(Default::default);

impl Runtime {
    fn fail(expect: &Expect, expected: &str, actual: &str) {
        let mut rt = RT.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
        if update_expect() {
            println!(
                "\x1b[1m\x1b[92mupdating\x1b[0m: {}:{}:{}",
                expect.file, expect.line, expect.column
            );
            rt.per_file
                .entry(expect.file)
                .or_insert_with(|| FileRuntime::new(expect))
                .update(expect, actual);
            return;
        }
        let print_help = !mem::replace(&mut rt.help_printed, true);
        let help = if print_help { HELP } else { "" };

        let diff = Changeset::new(actual, expected, "\n");

        println!(
            "\n
\x1b[1m\x1b[91merror\x1b[97m: expect test failed\x1b[0m
   \x1b[1m\x1b[34m-->\x1b[0m {}:{}:{}
{}
\x1b[1mExpect\x1b[0m:
----
{}
----

\x1b[1mActual\x1b[0m:
----
{}
----

\x1b[1mDiff\x1b[0m:
----
{}
----
",
            expect.file, expect.line, expect.column, help, expected, actual, diff
        );
        // Use resume_unwind instead of panic!() to prevent a backtrace, which is unnecessary noise.
        panic::resume_unwind(Box::new(()));
    }
}

struct FileRuntime {
    path: PathBuf,
    original_text: String,
    patchwork: Patchwork,
}

impl FileRuntime {
    fn new(expect: &Expect) -> FileRuntime {
        let path = workspace_root().join(expect.file);
        let original_text = fs::read_to_string(&path).unwrap();
        let patchwork = Patchwork::new(original_text.clone());
        FileRuntime { path, original_text, patchwork }
    }
    fn update(&mut self, expect: &Expect, actual: &str) {
        let loc = expect.locate(&self.original_text);
        let patch = format_patch(loc.line_indent.clone(), actual);
        self.patchwork.patch(loc.literal_range, &patch);
        fs::write(&self.path, &self.patchwork.text).unwrap()
    }
}

#[derive(Debug)]
struct Location {
    line_indent: usize,
    literal_range: Range<usize>,
}

#[derive(Debug)]
struct Patchwork {
    text: String,
    indels: Vec<(Range<usize>, usize)>,
}

impl Patchwork {
    fn new(text: String) -> Patchwork {
        Patchwork { text, indels: Vec::new() }
    }
    fn patch(&mut self, mut range: Range<usize>, patch: &str) {
        self.indels.push((range.clone(), patch.len()));
        self.indels.sort_by_key(|(delete, _insert)| delete.start);

        let (delete, insert) = self
            .indels
            .iter()
            .take_while(|(delete, _)| delete.start < range.start)
            .map(|(delete, insert)| (delete.end - delete.start, insert))
            .fold((0usize, 0usize), |(x1, y1), (x2, y2)| (x1 + x2, y1 + y2));

        for pos in &mut [&mut range.start, &mut range.end] {
            **pos -= delete;
            **pos += insert;
        }

        self.text.replace_range(range, &patch);
    }
}

fn format_patch(line_indent: usize, patch: &str) -> String {
    let mut max_hashes = 0;
    let mut cur_hashes = 0;
    for byte in patch.bytes() {
        if byte != b'#' {
            cur_hashes = 0;
            continue;
        }
        cur_hashes += 1;
        max_hashes = max_hashes.max(cur_hashes);
    }
    let hashes = &"#".repeat(max_hashes + 1);
    let indent = &" ".repeat(line_indent);
    let is_multiline = patch.contains('\n');

    let mut buf = String::new();
    buf.push('r');
    buf.push_str(hashes);
    buf.push('"');
    if is_multiline {
        buf.push('\n');
    }
    let mut final_newline = false;
    for line in lines_with_ends(patch) {
        if is_multiline {
            buf.push_str(indent);
            buf.push_str("    ");
        }
        buf.push_str(line);
        final_newline = line.ends_with('\n');
    }
    if final_newline {
        buf.push_str(indent);
    }
    buf.push('"');
    buf.push_str(hashes);
    buf
}

fn workspace_root() -> PathBuf {
    Path::new(
        &env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_owned()),
    )
    .ancestors()
    .nth(2)
    .unwrap()
    .to_path_buf()
}

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

    #[test]
    fn test_format_patch() {
        let patch = format_patch(0, "hello\nworld\n");
        expect![[r##"
            r#"
                hello
                world
            "#"##]]
        .assert_eq(&patch);

        let patch = format_patch(4, "single line");
        expect![[r##"r#"single line"#"##]].assert_eq(&patch);
    }

    #[test]
    fn test_patchwork() {
        let mut patchwork = Patchwork::new("one two three".to_string());
        patchwork.patch(4..7, "zwei");
        patchwork.patch(0..3, "один");
        patchwork.patch(8..13, "3");
        expect![[r#"
            Patchwork {
                text: "один zwei 3",
                indels: [
                    (
                        0..3,
                        8,
                    ),
                    (
                        4..7,
                        4,
                    ),
                    (
                        8..13,
                        1,
                    ),
                ],
            }
        "#]]
        .assert_debug_eq(&patchwork);
    }
}
<commit_msg>Cleanup<commit_after>//! Snapshot testing library, see
//! https://github.com/rust-analyzer/rust-analyzer/pull/5101
use std::{
    collections::HashMap,
    env, fmt, fs, mem,
    ops::Range,
    panic,
    path::{Path, PathBuf},
    sync::Mutex,
};

use difference::Changeset;
use once_cell::sync::Lazy;
use stdx::{lines_with_ends, trim_indent};

const HELP: &str = "
You can update all `expect![[]]` tests by:

    env UPDATE_EXPECT=1 cargo test

To update a single test, place the cursor on `expect` token and use `run` feature of rust-analyzer.
";

fn update_expect() -> bool {
    env::var("UPDATE_EXPECT").is_ok()
}

/// expect![[""]]
#[macro_export]
macro_rules! expect {
    [[$lit:literal]] => {$crate::Expect {
        position: $crate::Position {
            file: file!(),
            line: line!(),
            column: column!(),
        },
        data: $lit,
    }};
    [[]] => { $crate::expect![[""]] };
}

#[derive(Debug)]
pub struct Expect {
    pub position: Position,
    pub data: &'static str,
}

#[derive(Debug)]
pub struct Position {
    pub file: &'static str,
    pub line: u32,
    pub column: u32,
}

impl fmt::Display for Position {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}:{}:{}", self.file, self.line, self.column)
    }
}

impl Expect {
    pub fn assert_eq(&self, actual: &str) {
        let trimmed = self.trimmed();
        if &trimmed == actual {
            return;
        }
        Runtime::fail(self, &trimmed, actual);
    }
    pub fn assert_debug_eq(&self, actual: &impl fmt::Debug) {
        let actual = format!("{:#?}\n", actual);
        self.assert_eq(&actual)
    }

    fn trimmed(&self) -> String {
        if !self.data.contains('\n') {
            return self.data.to_string();
        }
        trim_indent(self.data)
    }

    fn locate(&self, file: &str) -> Location {
        let mut target_line = None;
        let mut line_start = 0;
        for (i, line) in lines_with_ends(file).enumerate() {
            if i == self.position.line as usize - 1 {
                let pat = "expect![[";
                let offset = line.find(pat).unwrap();
                let literal_start = line_start + offset + pat.len();
                let indent = line.chars().take_while(|&it| it == ' ').count();
                target_line = Some((literal_start, indent));
                break;
            }
            line_start += line.len();
        }
        let (literal_start, line_indent) = target_line.unwrap();
        let literal_length =
            file[literal_start..].find("]]").expect("Couldn't find matching `]]` for `expect![[`.");
        let literal_range = literal_start..literal_start + literal_length;
        Location { line_indent, literal_range }
    }
}

#[derive(Default)]
struct Runtime {
    help_printed: bool,
    per_file: HashMap<&'static str, FileRuntime>,
}
static RT: Lazy<Mutex<Runtime>> = Lazy::new(Default::default);

impl Runtime {
    fn fail(expect: &Expect, expected: &str, actual: &str) {
        let mut rt = RT.lock().unwrap_or_else(|poisoned| poisoned.into_inner());
        if update_expect() {
            println!("\x1b[1m\x1b[92mupdating\x1b[0m: {}", expect.position);
            rt.per_file
                .entry(expect.position.file)
                .or_insert_with(|| FileRuntime::new(expect))
                .update(expect, actual);
            return;
        }
        let print_help = !mem::replace(&mut rt.help_printed, true);
        let help = if print_help { HELP } else { "" };

        let diff = Changeset::new(actual, expected, "\n");

        println!(
            "\n
\x1b[1m\x1b[91merror\x1b[97m: expect test failed\x1b[0m
   \x1b[1m\x1b[34m-->\x1b[0m {}
{}
\x1b[1mExpect\x1b[0m:
----
{}
----

\x1b[1mActual\x1b[0m:
----
{}
----

\x1b[1mDiff\x1b[0m:
----
{}
----
",
            expect.position, help, expected, actual, diff
        );
        // Use resume_unwind instead of panic!() to prevent a backtrace, which is unnecessary noise.
        panic::resume_unwind(Box::new(()));
    }
}

struct FileRuntime {
    path: PathBuf,
    original_text: String,
    patchwork: Patchwork,
}

impl FileRuntime {
    fn new(expect: &Expect) -> FileRuntime {
        let path = workspace_root().join(expect.position.file);
        let original_text = fs::read_to_string(&path).unwrap();
        let patchwork = Patchwork::new(original_text.clone());
        FileRuntime { path, original_text, patchwork }
    }
    fn update(&mut self, expect: &Expect, actual: &str) {
        let loc = expect.locate(&self.original_text);
        let patch = format_patch(loc.line_indent.clone(), actual);
        self.patchwork.patch(loc.literal_range, &patch);
        fs::write(&self.path, &self.patchwork.text).unwrap()
    }
}

#[derive(Debug)]
struct Location {
    line_indent: usize,
    literal_range: Range<usize>,
}

#[derive(Debug)]
struct Patchwork {
    text: String,
    indels: Vec<(Range<usize>, usize)>,
}

impl Patchwork {
    fn new(text: String) -> Patchwork {
        Patchwork { text, indels: Vec::new() }
    }
    fn patch(&mut self, mut range: Range<usize>, patch: &str) {
        self.indels.push((range.clone(), patch.len()));
        self.indels.sort_by_key(|(delete, _insert)| delete.start);

        let (delete, insert) = self
            .indels
            .iter()
            .take_while(|(delete, _)| delete.start < range.start)
            .map(|(delete, insert)| (delete.end - delete.start, insert))
            .fold((0usize, 0usize), |(x1, y1), (x2, y2)| (x1 + x2, y1 + y2));

        for pos in &mut [&mut range.start, &mut range.end] {
            **pos -= delete;
            **pos += insert;
        }

        self.text.replace_range(range, &patch);
    }
}

fn format_patch(line_indent: usize, patch: &str) -> String {
    let mut max_hashes = 0;
    let mut cur_hashes = 0;
    for byte in patch.bytes() {
        if byte != b'#' {
            cur_hashes = 0;
            continue;
        }
        cur_hashes += 1;
        max_hashes = max_hashes.max(cur_hashes);
    }
    let hashes = &"#".repeat(max_hashes + 1);
    let indent = &" ".repeat(line_indent);
    let is_multiline = patch.contains('\n');

    let mut buf = String::new();
    buf.push('r');
    buf.push_str(hashes);
    buf.push('"');
    if is_multiline {
        buf.push('\n');
    }
    let mut final_newline = false;
    for line in lines_with_ends(patch) {
        if is_multiline {
            buf.push_str(indent);
            buf.push_str("    ");
        }
        buf.push_str(line);
        final_newline = line.ends_with('\n');
    }
    if final_newline {
        buf.push_str(indent);
    }
    buf.push('"');
    buf.push_str(hashes);
    buf
}

fn workspace_root() -> PathBuf {
    Path::new(
        &env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_owned()),
    )
    .ancestors()
    .nth(2)
    .unwrap()
    .to_path_buf()
}

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

    #[test]
    fn test_format_patch() {
        let patch = format_patch(0, "hello\nworld\n");
        expect![[r##"
            r#"
                hello
                world
            "#"##]]
        .assert_eq(&patch);

        let patch = format_patch(4, "single line");
        expect![[r##"r#"single line"#"##]].assert_eq(&patch);
    }

    #[test]
    fn test_patchwork() {
        let mut patchwork = Patchwork::new("one two three".to_string());
        patchwork.patch(4..7, "zwei");
        patchwork.patch(0..3, "один");
        patchwork.patch(8..13, "3");
        expect![[r#"
            Patchwork {
                text: "один zwei 3",
                indels: [
                    (
                        0..3,
                        8,
                    ),
                    (
                        4..7,
                        4,
                    ),
                    (
                        8..13,
                        1,
                    ),
                ],
            }
        "#]]
        .assert_debug_eq(&patchwork);
    }
}
<|endoftext|>


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

use backend::DevNullBackend;
use key::*;
use key::tests::*;
use std::sync::Arc;
use test::Bencher;
use util::Process;

#[bench]
fn insert_1_key_x_128000_zeros(bench: &mut Bencher) {
    let backend = Arc::new(DevNullBackend);
    let ks_p: StoreProcess<EntryStub, _> =
        Process::new(Store::new_for_testing(backend, 4 * 1024 * 1024).unwrap());

    let bytes = vec![0u8; 128*1024];

    let mut i = 0i32;
    bench.iter(|| {
        i += 1;

        let entry = EntryStub {
            data: Some(vec![bytes.clone()]),
            key_entry: Entry::new(None, format!("{}", i).as_bytes().to_vec(), None),
        };

        ks_p.send_reply(Msg::Insert(entry.key_entry.clone(),
                                    Some(Box::new(move |()| Some(entry)))))
            .unwrap();
    });

    ks_p.send_reply(Msg::Flush).unwrap();
    bench.bytes = 128 * 1024;
}

#[bench]
fn insert_1_key_x_128000_unique(bench: &mut Bencher) {
    let backend = Arc::new(DevNullBackend);
    let ks_p: StoreProcess<EntryStub, _> =
        Process::new(Store::new_for_testing(backend, 4 * 1024 * 1024).unwrap());

    let bytes = vec![0u8; 128*1024];

    let mut i = 0i32;
    bench.iter(|| {
        i += 1;

        let mut my_bytes = bytes.clone();
        my_bytes[0] = i as u8;
        my_bytes[1] = (i / 256) as u8;
        my_bytes[2] = (i / 65536) as u8;

        let entry = EntryStub {
            data: Some(vec![my_bytes]),
            key_entry: Entry::new(None, format!("{}", i).as_bytes().to_vec(), None),
        };

        ks_p.send_reply(Msg::Insert(entry.key_entry.clone(),
                                    Some(Box::new(move |()| Some(entry)))))
            .unwrap();
    });

    ks_p.send_reply(Msg::Flush).unwrap();
    bench.bytes = 128 * 1024;
}

#[bench]
fn insert_1_key_x_16_x_128000_zeros(bench: &mut Bencher) {
    let backend = Arc::new(DevNullBackend);
    let ks_p: StoreProcess<EntryStub, _> =
        Process::new(Store::new_for_testing(backend, 4 * 1024 * 1024).unwrap());

    bench.iter(|| {
        let bytes = vec![0u8; 128*1024];

        let entry = EntryStub {
            data: Some(vec![bytes; 16]),
            key_entry: Entry::new(None, vec![1u8, 2, 3].to_vec(), None),
        };
        ks_p.send_reply(Msg::Insert(entry.key_entry.clone(),
                                    Some(Box::new(move |()| Some(entry)))))
            .unwrap();

        match ks_p.send_reply(Msg::Flush).unwrap() {
            Reply::FlushOk => (),
            _ => panic!("Unexpected result from key store."),
        }
    });

    ks_p.send_reply(Msg::Flush).unwrap();
    bench.bytes = 16 * (128 * 1024);
}

#[bench]
fn insert_1_key_x_16_x_128000_unique(bench: &mut Bencher) {
    let backend = Arc::new(DevNullBackend);
    let ks_p: StoreProcess<EntryStub, _> =
        Process::new(Store::new_for_testing(backend, 4 * 1024 * 1024).unwrap());

    let bytes = vec![0u8; 128*1024];
    let mut i = 0i32;

    bench.iter(|| {
        i += 1;

        let mut my_bytes = bytes.clone();
        my_bytes[0] = i as u8;
        my_bytes[1] = (i / 256) as u8;
        my_bytes[2] = (i / 65536) as u8;

        let mut chunks = vec![];
        for i in 0..16 {
            let mut local_bytes = my_bytes.clone();
            local_bytes[3] = i as u8;
            chunks.push(local_bytes);
        }

        let entry = EntryStub {
            data: Some(chunks),
            key_entry: Entry::new(None, vec![1u8, 2, 3], None),
        };

        ks_p.send_reply(Msg::Insert(entry.key_entry.clone(),
                                    Some(Box::new(move |()| Some(entry)))))
            .unwrap();

        match ks_p.send_reply(Msg::Flush).unwrap() {
            Reply::FlushOk => (),
            _ => panic!("Unexpected result from key store."),
        }
    });

    bench.bytes = 16 * (128 * 1024);
}

#[bench]
fn insert_1_key_unchanged_empty(bench: &mut Bencher) {
    let backend = Arc::new(DevNullBackend);
    let ks_p: StoreProcess<EntryStub, _> =
        Process::new(Store::new_for_testing(backend, 4 * 1024 * 1024).unwrap());

    bench.iter(|| {
        let entry = EntryStub {
            data: None,
            key_entry: Entry::new(None, vec![1u8, 2, 3], None),
        };
        ks_p.send_reply(Msg::Insert(entry.key_entry.clone(), None)).unwrap();
    });

    ks_p.send_reply(Msg::Flush).unwrap();
}

#[bench]
fn insert_1_key_updated_empty(bench: &mut Bencher) {
    let backend = Arc::new(DevNullBackend);
    let ks_p: StoreProcess<EntryStub, _> =
        Process::new(Store::new_for_testing(backend, 4 * 1024 * 1024).unwrap());

    let mut i = 0;
    bench.iter(|| {
        i += 1;
        let entry = EntryStub {
            data: None,
            key_entry: Entry {
                parent_id: None,
                id: None,
                node_id: None,
                data_hash: None,
                info: Info {
                    name: vec![1u8, 2, 3].to_vec(),
                    created_ts_secs: Some(i),
                    modified_ts_secs: Some(i),
                    accessed_ts_secs: Some(i),
                    group_id: None,
                    user_id: None,
                    permissions: None,
                    byte_length: None,
                    hat_snapshot_top: false,
                    hat_snapshot_ts: 0,
                },
            },
        };
        ks_p.send_reply(Msg::Insert(entry.key_entry.clone(), None)).unwrap();
    });

    ks_p.send_reply(Msg::Flush).unwrap();
}

#[bench]
fn insert_1_key_unique_empty(bench: &mut Bencher) {
    let backend = Arc::new(DevNullBackend);
    let ks_p: StoreProcess<EntryStub, _> =
        Process::new(Store::new_for_testing(backend, 4 * 1024 * 1024).unwrap());

    let mut i = 0;
    bench.iter(|| {
        i += 1;
        let entry = EntryStub {
            data: None,
            key_entry: Entry::new(None, format!("{}", i).as_bytes().to_vec(), None),
        };
        ks_p.send_reply(Msg::Insert(entry.key_entry.clone(), None)).unwrap();
    });

    ks_p.send_reply(Msg::Flush).unwrap();
}
<commit_msg>Fix benchmarks.<commit_after>// Copyright 2014 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use backend::DevNullBackend;
use key::*;
use key::tests::*;
use std::sync::Arc;
use test::Bencher;
use util::Process;

#[bench]
fn insert_1_key_x_128000_zeros(bench: &mut Bencher) {
    let backend = Arc::new(DevNullBackend);
    let ks_p: StoreProcess<EntryStub, _> =
        Process::new(Store::new_for_testing(backend, 4 * 1024 * 1024).unwrap());

    let bytes = vec![0u8; 128*1024];

    let mut i = 0i32;
    bench.iter(|| {
        i += 1;

        let entry = EntryStub {
            data: Some(vec![bytes.clone()]),
            key_entry: Entry::new(None, format!("{}", i).as_bytes().to_vec(), None),
        };

        ks_p.send_reply(Msg::Insert(entry.key_entry.clone(),
                                    Some(Box::new(move |()| Some(entry)))))
            .unwrap();
    });

    ks_p.send_reply(Msg::Flush).unwrap();
    bench.bytes = 128 * 1024;
}

#[bench]
fn insert_1_key_x_128000_unique(bench: &mut Bencher) {
    let backend = Arc::new(DevNullBackend);
    let ks_p: StoreProcess<EntryStub, _> =
        Process::new(Store::new_for_testing(backend, 4 * 1024 * 1024).unwrap());

    let bytes = vec![0u8; 128*1024];

    let mut i = 0i32;
    bench.iter(|| {
        i += 1;

        let mut my_bytes = bytes.clone();
        my_bytes[0] = i as u8;
        my_bytes[1] = (i / 256) as u8;
        my_bytes[2] = (i / 65536) as u8;

        let entry = EntryStub {
            data: Some(vec![my_bytes]),
            key_entry: Entry::new(None, format!("{}", i).as_bytes().to_vec(), None),
        };

        ks_p.send_reply(Msg::Insert(entry.key_entry.clone(),
                                    Some(Box::new(move |()| Some(entry)))))
            .unwrap();
    });

    ks_p.send_reply(Msg::Flush).unwrap();
    bench.bytes = 128 * 1024;
}

#[bench]
fn insert_1_key_x_16_x_128000_zeros(bench: &mut Bencher) {
    let backend = Arc::new(DevNullBackend);
    let ks_p: StoreProcess<EntryStub, _> =
        Process::new(Store::new_for_testing(backend, 4 * 1024 * 1024).unwrap());

    bench.iter(|| {
        let bytes = vec![0u8; 128*1024];

        let entry = EntryStub {
            data: Some(vec![bytes; 16]),
            key_entry: Entry::new(None, vec![1u8, 2, 3].to_vec(), None),
        };
        ks_p.send_reply(Msg::Insert(entry.key_entry.clone(),
                                    Some(Box::new(move |()| Some(entry)))))
            .unwrap();

        match ks_p.send_reply(Msg::Flush).unwrap() {
            Reply::FlushOk => (),
            _ => panic!("Unexpected result from key store."),
        }
    });

    ks_p.send_reply(Msg::Flush).unwrap();
    bench.bytes = 16 * (128 * 1024);
}

#[bench]
fn insert_1_key_x_16_x_128000_unique(bench: &mut Bencher) {
    let backend = Arc::new(DevNullBackend);
    let ks_p: StoreProcess<EntryStub, _> =
        Process::new(Store::new_for_testing(backend, 4 * 1024 * 1024).unwrap());

    let bytes = vec![0u8; 128*1024];
    let mut i = 0i32;

    bench.iter(|| {
        i += 1;

        let mut my_bytes = bytes.clone();
        my_bytes[0] = i as u8;
        my_bytes[1] = (i / 256) as u8;
        my_bytes[2] = (i / 65536) as u8;

        let mut chunks = vec![];
        for i in 0..16 {
            let mut local_bytes = my_bytes.clone();
            local_bytes[3] = i as u8;
            chunks.push(local_bytes);
        }

        let entry = EntryStub {
            data: Some(chunks),
            key_entry: Entry::new(None, vec![1u8, 2, 3], None),
        };

        ks_p.send_reply(Msg::Insert(entry.key_entry.clone(),
                                    Some(Box::new(move |()| Some(entry)))))
            .unwrap();

        match ks_p.send_reply(Msg::Flush).unwrap() {
            Reply::FlushOk => (),
            _ => panic!("Unexpected result from key store."),
        }
    });

    bench.bytes = 16 * (128 * 1024);
}

#[bench]
fn insert_1_key_unchanged_empty(bench: &mut Bencher) {
    let backend = Arc::new(DevNullBackend);
    let ks_p: StoreProcess<EntryStub, _> =
        Process::new(Store::new_for_testing(backend, 4 * 1024 * 1024).unwrap());

    bench.iter(|| {
        let entry = EntryStub {
            data: None,
            key_entry: Entry::new(None, vec![1u8, 2, 3], None),
        };
        ks_p.send_reply(Msg::Insert(entry.key_entry.clone(), None)).unwrap();
    });

    ks_p.send_reply(Msg::Flush).unwrap();
}

#[bench]
fn insert_1_key_updated_empty(bench: &mut Bencher) {
    let backend = Arc::new(DevNullBackend);
    let ks_p: StoreProcess<EntryStub, _> =
        Process::new(Store::new_for_testing(backend, 4 * 1024 * 1024).unwrap());

    let mut i = 0;
    bench.iter(|| {
        i += 1;
        let entry = EntryStub {
            data: None,
            key_entry: Entry {
                parent_id: None,
                node_id: None,
                data_hash: None,
                info: Info {
                    name: vec![1u8, 2, 3].to_vec(),
                    created_ts_secs: Some(i),
                    modified_ts_secs: Some(i),
                    accessed_ts_secs: Some(i),
                    group_id: None,
                    user_id: None,
                    permissions: None,
                    byte_length: None,
                    hat_snapshot_top: false,
                    hat_snapshot_ts: 0,
                },
            },
        };
        ks_p.send_reply(Msg::Insert(entry.key_entry.clone(), None)).unwrap();
    });

    ks_p.send_reply(Msg::Flush).unwrap();
}

#[bench]
fn insert_1_key_unique_empty(bench: &mut Bencher) {
    let backend = Arc::new(DevNullBackend);
    let ks_p: StoreProcess<EntryStub, _> =
        Process::new(Store::new_for_testing(backend, 4 * 1024 * 1024).unwrap());

    let mut i = 0;
    bench.iter(|| {
        i += 1;
        let entry = EntryStub {
            data: None,
            key_entry: Entry::new(None, format!("{}", i).as_bytes().to_vec(), None),
        };
        ks_p.send_reply(Msg::Insert(entry.key_entry.clone(), None)).unwrap();
    });

    ks_p.send_reply(Msg::Flush).unwrap();
}
<|endoftext|>