<commit_before>//! Configuration of git-global.
//!
//! Exports the `Config` struct, which defines the base path for finding git
//! repos on the machine, path patterns to ignore when scanning for repos, the
//! location of a cache file, and other config options for running git-global.

use std::fs::{remove_file, File};
use std::io::{BufRead, BufReader, Write};
use std::path::PathBuf;

use app_dirs::{app_dir, get_app_dir, AppDataType, AppInfo};
use dirs::home_dir;
use git2;
use walkdir::{DirEntry, WalkDir};

use repo::Repo;

const APP: AppInfo = AppInfo {
    name: "git-global",
    author: "peap",
};
const CACHE_FILE: &'static str = "repos.txt";

const DEFAULT_CMD: &'static str = "status";
const DEFAULT_SHOW_UNTRACKED: bool = true;

const SETTING_BASEDIR: &'static str = "global.basedir";
const SETTING_IGNORE: &'static str = "global.ignore";
const SETTING_DEFAULT_CMD: &'static str = "global.default-cmd";
const SETTING_SHOW_UNTRACKED: &'static str = "global.show-untracked";

/// A container for git-global configuration options.
pub struct Config {
    /// The base directory to walk when searching for git repositories.
    ///
    /// Default: $HOME.
    pub basedir: PathBuf,

    /// Path patterns to ignore when searching for git repositories.
    ///
    /// Default: none
    pub ignored_patterns: Vec<String>,

    /// The git-global subcommand to run when unspecified.
    ///
    /// Default: `status`
    pub default_cmd: String,

    /// Whether to show untracked files in output.
    ///
    /// Default: true
    pub show_untracked: bool,

    /// Path a cache file for git-global's usage.
    ///
    /// Default: `repos.txt` in the user's XDG cache directory.
    pub cache_file: PathBuf,
}

impl Config {
    /// Create a new `Config` with the default behavior, first checking global
    /// git config options in ~/.gitconfig, then using defaults:
    pub fn new() -> Config {
        // Find the user's home directory.
        let homedir = home_dir().expect("Could not determine home directory.");
        // Set the options that aren't user-configurable.
        let cache_file =
            match get_app_dir(AppDataType::UserCache, &APP, "cache") {
                Ok(mut dir) => {
                    dir.push(CACHE_FILE);
                    dir
                }
                Err(_) => panic!("TODO: work without XDG"),
            };
        // Try to read user's Git configuration and return a Config object.
        match git2::Config::open_default() {
            Ok(cfg) => {
                (Config {
                    basedir: cfg.get_path(SETTING_BASEDIR).unwrap_or(homedir),
                    ignored_patterns: cfg
                        .get_string(SETTING_IGNORE)
                        .unwrap_or(String::new())
                        .split(",")
                        .map(|p| p.trim().to_string())
                        .collect(),
                    default_cmd: cfg
                        .get_string(SETTING_DEFAULT_CMD)
                        .unwrap_or(String::from(DEFAULT_CMD)),
                    show_untracked: cfg
                        .get_bool(SETTING_SHOW_UNTRACKED)
                        .unwrap_or(DEFAULT_SHOW_UNTRACKED),
                    cache_file: cache_file,
                })
            }
            Err(_) => {
                // Build the default configuration.
                (Config {
                    basedir: homedir,
                    ignored_patterns: vec![],
                    default_cmd: String::from(DEFAULT_CMD),
                    show_untracked: DEFAULT_SHOW_UNTRACKED,
                    cache_file: cache_file,
                })
            }
        }
    }

    /// Returns all known git repos, populating the cache first, if necessary.
    pub fn get_repos(&mut self) -> Vec<Repo> {
        if !self.has_cache() {
            let repos = self.find_repos();
            self.cache_repos(&repos);
        }
        self.get_cached_repos()
    }

    /// Clears the cache of known git repos, forcing a re-scan on the next
    /// `get_repos()` call.
    pub fn clear_cache(&mut self) {
        if self.has_cache() {
            remove_file(&self.cache_file)
                .expect("Failed to delete cache file.");
        }
    }

    /// Returns `true` if this directory entry should be included in scans.
    fn filter(&self, entry: &DirEntry) -> bool {
        if let Some(entry_path) = entry.path().to_str() {
            self.ignored_patterns
                .iter()
                .filter(|p| p != &"")
                .fold(true, |acc, pattern| acc && !entry_path.contains(pattern))
        } else {
            // Skip invalid file name
            false
        }
    }

    /// Walks the configured base directory, looking for git repos.
    fn find_repos(&self) -> Vec<Repo> {
        let mut repos = Vec::new();
        println!(
            "Scanning for git repos under {}; this may take a while...",
            self.basedir.display()
        );
        for entry in WalkDir::new(&self.basedir)
            .follow_links(true)
            .same_file_system(true)
            .into_iter()
            .filter_entry(|e| self.filter(e))
        {
            match entry {
                Ok(entry) => {
                    if entry.file_type().is_dir() && entry.file_name() == ".git"
                    {
                        let parent_path = entry
                            .path()
                            .parent()
                            .expect("Could not determine parent.");
                        match parent_path.to_str() {
                            Some(path) => {
                                repos.push(Repo::new(path.to_string()));
                            }
                            None => (),
                        }
                    }
                }
                Err(_) => (),
            }
        }
        repos.sort_by(|a, b| a.path().cmp(&b.path()));
        repos
    }

    /// Returns boolean indicating if the cache file exists.
    fn has_cache(&self) -> bool {
        self.cache_file.exists()
    }

    /// Writes the given repo paths to the cache file.
    fn cache_repos(&self, repos: &Vec<Repo>) {
        if !self.cache_file.as_path().exists() {
            // Try to create the cache directory if the cache *file* doesn't
            // exist; app_dir() handles an existing directory just fine.
            match app_dir(AppDataType::UserCache, &APP, "cache") {
                Ok(_) => (),
                Err(e) => panic!("Could not create cache directory: {}", e),
            }
        }
        let mut f = File::create(&self.cache_file)
            .expect("Could not create cache file.");
        for repo in repos.iter() {
            match writeln!(f, "{}", repo.path()) {
                Ok(_) => (),
                Err(e) => panic!("Problem writing cache file: {}", e),
            }
        }
    }

    /// Returns the list of repos found in the cache file.
    fn get_cached_repos(&self) -> Vec<Repo> {
        let mut repos = Vec::new();
        if self.cache_file.exists() {
            let f = File::open(&self.cache_file)
                .expect("Could not open cache file.");
            let reader = BufReader::new(f);
            for line in reader.lines() {
                match line {
                    Ok(repo_path) => repos.push(Repo::new(repo_path)),
                    Err(_) => (), // TODO: handle errors
                }
            }
        }
        repos
    }
}
<commit_msg>Set same_file_system on supported platforms only<commit_after>//! Configuration of git-global.
//!
//! Exports the `Config` struct, which defines the base path for finding git
//! repos on the machine, path patterns to ignore when scanning for repos, the
//! location of a cache file, and other config options for running git-global.

use std::fs::{remove_file, File};
use std::io::{BufRead, BufReader, Write};
use std::path::PathBuf;

use app_dirs::{app_dir, get_app_dir, AppDataType, AppInfo};
use dirs::home_dir;
use git2;
use walkdir::{DirEntry, WalkDir};

use repo::Repo;

const APP: AppInfo = AppInfo {
    name: "git-global",
    author: "peap",
};
const CACHE_FILE: &'static str = "repos.txt";

const DEFAULT_CMD: &'static str = "status";
const DEFAULT_SHOW_UNTRACKED: bool = true;

const SETTING_BASEDIR: &'static str = "global.basedir";
const SETTING_IGNORE: &'static str = "global.ignore";
const SETTING_DEFAULT_CMD: &'static str = "global.default-cmd";
const SETTING_SHOW_UNTRACKED: &'static str = "global.show-untracked";

/// A container for git-global configuration options.
pub struct Config {
    /// The base directory to walk when searching for git repositories.
    ///
    /// Default: $HOME.
    pub basedir: PathBuf,

    /// Path patterns to ignore when searching for git repositories.
    ///
    /// Default: none
    pub ignored_patterns: Vec<String>,

    /// The git-global subcommand to run when unspecified.
    ///
    /// Default: `status`
    pub default_cmd: String,

    /// Whether to show untracked files in output.
    ///
    /// Default: true
    pub show_untracked: bool,

    /// Path a cache file for git-global's usage.
    ///
    /// Default: `repos.txt` in the user's XDG cache directory.
    pub cache_file: PathBuf,
}

impl Config {
    /// Create a new `Config` with the default behavior, first checking global
    /// git config options in ~/.gitconfig, then using defaults:
    pub fn new() -> Config {
        // Find the user's home directory.
        let homedir = home_dir().expect("Could not determine home directory.");
        // Set the options that aren't user-configurable.
        let cache_file =
            match get_app_dir(AppDataType::UserCache, &APP, "cache") {
                Ok(mut dir) => {
                    dir.push(CACHE_FILE);
                    dir
                }
                Err(_) => panic!("TODO: work without XDG"),
            };
        // Try to read user's Git configuration and return a Config object.
        match git2::Config::open_default() {
            Ok(cfg) => {
                (Config {
                    basedir: cfg.get_path(SETTING_BASEDIR).unwrap_or(homedir),
                    ignored_patterns: cfg
                        .get_string(SETTING_IGNORE)
                        .unwrap_or(String::new())
                        .split(",")
                        .map(|p| p.trim().to_string())
                        .collect(),
                    default_cmd: cfg
                        .get_string(SETTING_DEFAULT_CMD)
                        .unwrap_or(String::from(DEFAULT_CMD)),
                    show_untracked: cfg
                        .get_bool(SETTING_SHOW_UNTRACKED)
                        .unwrap_or(DEFAULT_SHOW_UNTRACKED),
                    cache_file: cache_file,
                })
            }
            Err(_) => {
                // Build the default configuration.
                (Config {
                    basedir: homedir,
                    ignored_patterns: vec![],
                    default_cmd: String::from(DEFAULT_CMD),
                    show_untracked: DEFAULT_SHOW_UNTRACKED,
                    cache_file: cache_file,
                })
            }
        }
    }

    /// Returns all known git repos, populating the cache first, if necessary.
    pub fn get_repos(&mut self) -> Vec<Repo> {
        if !self.has_cache() {
            let repos = self.find_repos();
            self.cache_repos(&repos);
        }
        self.get_cached_repos()
    }

    /// Clears the cache of known git repos, forcing a re-scan on the next
    /// `get_repos()` call.
    pub fn clear_cache(&mut self) {
        if self.has_cache() {
            remove_file(&self.cache_file)
                .expect("Failed to delete cache file.");
        }
    }

    /// Returns `true` if this directory entry should be included in scans.
    fn filter(&self, entry: &DirEntry) -> bool {
        if let Some(entry_path) = entry.path().to_str() {
            self.ignored_patterns
                .iter()
                .filter(|p| p != &"")
                .fold(true, |acc, pattern| acc && !entry_path.contains(pattern))
        } else {
            // Skip invalid file name
            false
        }
    }

    /// Walks the configured base directory, looking for git repos.
    fn find_repos(&self) -> Vec<Repo> {
        let mut repos = Vec::new();
        println!(
            "Scanning for git repos under {}; this may take a while...",
            self.basedir.display()
        );
        let walker = WalkDir::new(&self.basedir).follow_links(true);
        #[cfg(any(unix, windows))]
        let walker = walker.same_file_system(true);
        for entry in walker.into_iter().filter_entry(|e| self.filter(e)) {
            match entry {
                Ok(entry) => {
                    if entry.file_type().is_dir() && entry.file_name() == ".git"
                    {
                        let parent_path = entry
                            .path()
                            .parent()
                            .expect("Could not determine parent.");
                        match parent_path.to_str() {
                            Some(path) => {
                                repos.push(Repo::new(path.to_string()));
                            }
                            None => (),
                        }
                    }
                }
                Err(_) => (),
            }
        }
        repos.sort_by(|a, b| a.path().cmp(&b.path()));
        repos
    }

    /// Returns boolean indicating if the cache file exists.
    fn has_cache(&self) -> bool {
        self.cache_file.exists()
    }

    /// Writes the given repo paths to the cache file.
    fn cache_repos(&self, repos: &Vec<Repo>) {
        if !self.cache_file.as_path().exists() {
            // Try to create the cache directory if the cache *file* doesn't
            // exist; app_dir() handles an existing directory just fine.
            match app_dir(AppDataType::UserCache, &APP, "cache") {
                Ok(_) => (),
                Err(e) => panic!("Could not create cache directory: {}", e),
            }
        }
        let mut f = File::create(&self.cache_file)
            .expect("Could not create cache file.");
        for repo in repos.iter() {
            match writeln!(f, "{}", repo.path()) {
                Ok(_) => (),
                Err(e) => panic!("Problem writing cache file: {}", e),
            }
        }
    }

    /// Returns the list of repos found in the cache file.
    fn get_cached_repos(&self) -> Vec<Repo> {
        let mut repos = Vec::new();
        if self.cache_file.exists() {
            let f = File::open(&self.cache_file)
                .expect("Could not open cache file.");
            let reader = BufReader::new(f);
            for line in reader.lines() {
                match line {
                    Ok(repo_path) => repos.push(Repo::new(repo_path)),
                    Err(_) => (), // TODO: handle errors
                }
            }
        }
        repos
    }
}
<|endoftext|>


<commit_before>// Copyright 2015-2016, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use std::ffi::CString;
use std::path::Path;

use enums::{Content, DeviceType, ScriptMode};
use error::Error;
use ffi;
#[cfg(feature = "use_glib")]
use glib::translate::*;
use recording_surface::RecordingSurface;
use std::fmt;
use std::ptr;
use surface::Surface;
use utils::status_to_result;

#[derive(Debug)]
pub struct Device(ptr::NonNull<ffi::cairo_device_t>);

impl Device {
    pub unsafe fn from_raw_none(ptr: *mut ffi::cairo_device_t) -> Device {
        assert!(!ptr.is_null());
        ffi::cairo_device_reference(ptr);
        Device(ptr::NonNull::new_unchecked(ptr))
    }

    pub unsafe fn from_raw_borrow(ptr: *mut ffi::cairo_device_t) -> ::Borrowed<Device> {
        assert!(!ptr.is_null());
        ::Borrowed::new(Device(ptr::NonNull::new_unchecked(ptr)))
    }

    pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_device_t) -> Device {
        assert!(!ptr.is_null());
        Device(ptr::NonNull::new_unchecked(ptr))
    }

    pub fn to_raw_none(&self) -> *mut ffi::cairo_device_t {
        self.0.as_ptr()
    }

    pub fn create<P: AsRef<Path>>(filename: P) -> Option<Device> {
        unsafe {
            let filename = filename.as_ref().to_string_lossy().into_owned();
            let filename = CString::new(filename).unwrap();
            let p = ffi::cairo_script_create(filename.as_ptr());
            if p.is_null() {
                None
            } else {
                Some(Self::from_raw_full(p))
            }
        }
    }

    pub fn from_recording_surface(&self, surface: &RecordingSurface) -> Result<(), Error> {
        unsafe {
            let status =
                ffi::cairo_script_from_recording_surface(self.to_raw_none(), surface.to_raw_none());
            status_to_result(status)
        }
    }

    pub fn get_mode(&self) -> ScriptMode {
        unsafe { ScriptMode::from(ffi::cairo_script_get_mode(self.to_raw_none())) }
    }

    pub fn set_mode(&self, mode: ScriptMode) {
        unsafe { ffi::cairo_script_set_mode(self.to_raw_none(), mode.into()) }
    }

    pub fn surface_create(
        &self,
        content: Content,
        width: f64,
        height: f64,
    ) -> Result<Surface, Error> {
        unsafe {
            Ok(Surface::from_raw_full(ffi::cairo_script_surface_create(
                self.to_raw_none(),
                content.into(),
                width,
                height,
            ))?)
        }
    }

    pub fn surface_create_for_target(&self, target: &Surface) -> Result<Surface, Error> {
        unsafe {
            Ok(Surface::from_raw_full(
                ffi::cairo_script_surface_create_for_target(
                    self.to_raw_none(),
                    target.to_raw_none(),
                ),
            )?)
        }
    }

    pub fn write_comment(&self, comment: &str) {
        unsafe {
            let len = comment.len();
            let comment = CString::new(comment).unwrap();
            ffi::cairo_script_write_comment(self.to_raw_none(), comment.as_ptr(), len as i32)
        }
    }

    pub fn finish(&self) {
        unsafe { ffi::cairo_device_finish(self.to_raw_none()) }
    }

    pub fn flush(&self) {
        unsafe { ffi::cairo_device_flush(self.to_raw_none()) }
    }

    pub fn get_type(&self) -> DeviceType {
        unsafe { DeviceType::from(ffi::cairo_device_get_type(self.to_raw_none())) }
    }

    // Maybe improve this API?
    pub fn acquire(&self) -> Result<(), Error> {
        unsafe {
            let status = ffi::cairo_device_acquire(self.to_raw_none());
            status_to_result(status)
        }
    }

    // Maybe improve this API?
    pub fn release(&self) {
        unsafe { ffi::cairo_device_release(self.to_raw_none()) }
    }

    pub fn observer_elapsed(&self) -> f64 {
        unsafe { ffi::cairo_device_observer_elapsed(self.to_raw_none()) }
    }

    pub fn observer_fill_elapsed(&self) -> f64 {
        unsafe { ffi::cairo_device_observer_fill_elapsed(self.to_raw_none()) }
    }

    pub fn observer_glyphs_elapsed(&self) -> f64 {
        unsafe { ffi::cairo_device_observer_glyphs_elapsed(self.to_raw_none()) }
    }

    pub fn observer_mask_elapsed(&self) -> f64 {
        unsafe { ffi::cairo_device_observer_mask_elapsed(self.to_raw_none()) }
    }

    pub fn observer_paint_elapsed(&self) -> f64 {
        unsafe { ffi::cairo_device_observer_paint_elapsed(self.to_raw_none()) }
    }

    pub fn observer_stroke_elapsed(&self) -> f64 {
        unsafe { ffi::cairo_device_observer_stroke_elapsed(self.to_raw_none()) }
    }

    #[cfg(any(feature = "xlib", feature = "xcb", feature = "dox"))]
    pub fn debug_cap_xrender_version(&self, major_version: i32, minor_version: i32) {
        unsafe {
            match self.get_type() {
                DeviceType::Xlib => {
                    #[cfg(feature = "xlib")]
                    {
                        ffi::cairo_xlib_device_debug_cap_xrender_version(
                            self.to_raw_none(),
                            major_version,
                            minor_version,
                        )
                    }
                    #[cfg(not(feature = "xlib"))]
                    {
                        panic!("you need to enable \"xlib\" feature")
                    }
                }
                DeviceType::Xcb => {
                    #[cfg(feature = "xcb")]
                    {
                        ffi::cairo_xcb_device_debug_cap_xrender_version(
                            self.to_raw_none(),
                            major_version,
                            minor_version,
                        )
                    }
                    #[cfg(not(feature = "xcb"))]
                    {
                        panic!("you need to enable \"xcb\" feature")
                    }
                }
                d => panic!("invalid device type: {}", d),
            }
        }
    }

    #[cfg(any(feature = "xlib", feature = "xcb", feature = "dox"))]
    pub fn debug_get_precision(&self) -> i32 {
        unsafe {
            match self.get_type() {
                DeviceType::Xlib => {
                    #[cfg(feature = "xlib")]
                    {
                        ffi::cairo_xlib_device_debug_get_precision(self.to_raw_none())
                    }
                    #[cfg(not(feature = "xlib"))]
                    {
                        panic!("you need to enable \"xlib\" feature")
                    }
                }
                DeviceType::Xcb => {
                    #[cfg(feature = "xcb")]
                    {
                        ffi::cairo_xcb_device_debug_get_precision(self.to_raw_none())
                    }
                    #[cfg(not(feature = "xcb"))]
                    {
                        panic!("you need to enable \"xcb\" feature")
                    }
                }
                d => panic!("invalid device type: {}", d),
            }
        }
    }

    #[cfg(any(feature = "xlib", feature = "xcb", feature = "dox"))]
    pub fn debug_set_precision(&self, precision: i32) {
        unsafe {
            match self.get_type() {
                DeviceType::Xlib => {
                    #[cfg(feature = "xlib")]
                    {
                        ffi::cairo_xlib_device_debug_set_precision(self.to_raw_none(), precision)
                    }
                    #[cfg(not(feature = "xlib"))]
                    {
                        panic!("you need to enable \"xlib\" feature")
                    }
                }
                DeviceType::Xcb => {
                    #[cfg(feature = "xcb")]
                    {
                        ffi::cairo_xcb_device_debug_set_precision(self.to_raw_none(), precision)
                    }
                    #[cfg(not(feature = "xcb"))]
                    {
                        panic!("you need to enable \"xcb\" feature")
                    }
                }
                d => panic!("invalid device type: {}", d),
            }
        }
    }

    user_data_methods! {
        ffi::cairo_device_get_user_data,
        ffi::cairo_device_set_user_data,
    }
}

#[cfg(feature = "use_glib")]
impl<'a> ToGlibPtr<'a, *mut ffi::cairo_device_t> for Device {
    type Storage = &'a Device;

    #[inline]
    fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_device_t, Self> {
        Stash(self.to_raw_none(), self)
    }

    #[inline]
    fn to_glib_full(&self) -> *mut ffi::cairo_device_t {
        unsafe { ffi::cairo_device_reference(self.to_raw_none()) }
    }
}

#[cfg(feature = "use_glib")]
impl FromGlibPtrNone<*mut ffi::cairo_device_t> for Device {
    #[inline]
    unsafe fn from_glib_none(ptr: *mut ffi::cairo_device_t) -> Device {
        Self::from_raw_none(ptr)
    }
}

#[cfg(feature = "use_glib")]
impl FromGlibPtrBorrow<*mut ffi::cairo_device_t> for Device {
    #[inline]
    unsafe fn from_glib_borrow(ptr: *mut ffi::cairo_device_t) -> ::Borrowed<Device> {
        Self::from_raw_borrow(ptr)
    }
}

#[cfg(feature = "use_glib")]
impl FromGlibPtrFull<*mut ffi::cairo_device_t> for Device {
    #[inline]
    unsafe fn from_glib_full(ptr: *mut ffi::cairo_device_t) -> Device {
        Self::from_raw_full(ptr)
    }
}

#[cfg(feature = "use_glib")]
gvalue_impl!(
    Device,
    ffi::cairo_device_t,
    ffi::gobject::cairo_gobject_device_get_type
);

impl Clone for Device {
    fn clone(&self) -> Device {
        unsafe { Self::from_raw_none(ffi::cairo_device_reference(self.0.as_ptr())) }
    }
}

impl Drop for Device {
    fn drop(&mut self) {
        unsafe {
            ffi::cairo_device_destroy(self.0.as_ptr());
        }
    }
}

impl fmt::Display for Device {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Device")
    }
}
<commit_msg>device: clean acquire/release API<commit_after>// Copyright 2015-2016, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use std::ffi::CString;
use std::path::Path;

use enums::{Content, DeviceType, ScriptMode};
use error::Error;
use ffi;
#[cfg(feature = "use_glib")]
use glib::translate::*;
use recording_surface::RecordingSurface;
use std::fmt;
use std::ptr;
use surface::Surface;
use utils::status_to_result;

#[derive(Debug)]
pub struct DeviceAcquireGuard<'a>(&'a Device);

impl<'a> Drop for DeviceAcquireGuard<'a> {
    fn drop(&mut self) {
        self.0.release();
    }
}

#[derive(Debug)]
pub struct Device(ptr::NonNull<ffi::cairo_device_t>);

impl Device {
    pub unsafe fn from_raw_none(ptr: *mut ffi::cairo_device_t) -> Device {
        assert!(!ptr.is_null());
        ffi::cairo_device_reference(ptr);
        Device(ptr::NonNull::new_unchecked(ptr))
    }

    pub unsafe fn from_raw_borrow(ptr: *mut ffi::cairo_device_t) -> ::Borrowed<Device> {
        assert!(!ptr.is_null());
        ::Borrowed::new(Device(ptr::NonNull::new_unchecked(ptr)))
    }

    pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_device_t) -> Device {
        assert!(!ptr.is_null());
        Device(ptr::NonNull::new_unchecked(ptr))
    }

    pub fn to_raw_none(&self) -> *mut ffi::cairo_device_t {
        self.0.as_ptr()
    }

    pub fn create<P: AsRef<Path>>(filename: P) -> Option<Device> {
        unsafe {
            let filename = filename.as_ref().to_string_lossy().into_owned();
            let filename = CString::new(filename).unwrap();
            let p = ffi::cairo_script_create(filename.as_ptr());
            if p.is_null() {
                None
            } else {
                Some(Self::from_raw_full(p))
            }
        }
    }

    pub fn from_recording_surface(&self, surface: &RecordingSurface) -> Result<(), Error> {
        unsafe {
            let status =
                ffi::cairo_script_from_recording_surface(self.to_raw_none(), surface.to_raw_none());
            status_to_result(status)
        }
    }

    pub fn get_mode(&self) -> ScriptMode {
        unsafe { ScriptMode::from(ffi::cairo_script_get_mode(self.to_raw_none())) }
    }

    pub fn set_mode(&self, mode: ScriptMode) {
        unsafe { ffi::cairo_script_set_mode(self.to_raw_none(), mode.into()) }
    }

    pub fn surface_create(
        &self,
        content: Content,
        width: f64,
        height: f64,
    ) -> Result<Surface, Error> {
        unsafe {
            Ok(Surface::from_raw_full(ffi::cairo_script_surface_create(
                self.to_raw_none(),
                content.into(),
                width,
                height,
            ))?)
        }
    }

    pub fn surface_create_for_target(&self, target: &Surface) -> Result<Surface, Error> {
        unsafe {
            Ok(Surface::from_raw_full(
                ffi::cairo_script_surface_create_for_target(
                    self.to_raw_none(),
                    target.to_raw_none(),
                ),
            )?)
        }
    }

    pub fn write_comment(&self, comment: &str) {
        unsafe {
            let len = comment.len();
            let comment = CString::new(comment).unwrap();
            ffi::cairo_script_write_comment(self.to_raw_none(), comment.as_ptr(), len as i32)
        }
    }

    pub fn finish(&self) {
        unsafe { ffi::cairo_device_finish(self.to_raw_none()) }
    }

    pub fn flush(&self) {
        unsafe { ffi::cairo_device_flush(self.to_raw_none()) }
    }

    pub fn get_type(&self) -> DeviceType {
        unsafe { DeviceType::from(ffi::cairo_device_get_type(self.to_raw_none())) }
    }

    pub fn acquire(&self) -> Result<DeviceAcquireGuard, Error> {
        unsafe {
            let status = ffi::cairo_device_acquire(self.to_raw_none());
            status_to_result(status)?;
        }
        Ok(DeviceAcquireGuard { 0: self })
    }

    fn release(&self) {
        unsafe { ffi::cairo_device_release(self.to_raw_none()) }
    }

    pub fn observer_elapsed(&self) -> f64 {
        unsafe { ffi::cairo_device_observer_elapsed(self.to_raw_none()) }
    }

    pub fn observer_fill_elapsed(&self) -> f64 {
        unsafe { ffi::cairo_device_observer_fill_elapsed(self.to_raw_none()) }
    }

    pub fn observer_glyphs_elapsed(&self) -> f64 {
        unsafe { ffi::cairo_device_observer_glyphs_elapsed(self.to_raw_none()) }
    }

    pub fn observer_mask_elapsed(&self) -> f64 {
        unsafe { ffi::cairo_device_observer_mask_elapsed(self.to_raw_none()) }
    }

    pub fn observer_paint_elapsed(&self) -> f64 {
        unsafe { ffi::cairo_device_observer_paint_elapsed(self.to_raw_none()) }
    }

    pub fn observer_stroke_elapsed(&self) -> f64 {
        unsafe { ffi::cairo_device_observer_stroke_elapsed(self.to_raw_none()) }
    }

    #[cfg(any(feature = "xlib", feature = "xcb", feature = "dox"))]
    pub fn debug_cap_xrender_version(&self, major_version: i32, minor_version: i32) {
        unsafe {
            match self.get_type() {
                DeviceType::Xlib => {
                    #[cfg(feature = "xlib")]
                    {
                        ffi::cairo_xlib_device_debug_cap_xrender_version(
                            self.to_raw_none(),
                            major_version,
                            minor_version,
                        )
                    }
                    #[cfg(not(feature = "xlib"))]
                    {
                        panic!("you need to enable \"xlib\" feature")
                    }
                }
                DeviceType::Xcb => {
                    #[cfg(feature = "xcb")]
                    {
                        ffi::cairo_xcb_device_debug_cap_xrender_version(
                            self.to_raw_none(),
                            major_version,
                            minor_version,
                        )
                    }
                    #[cfg(not(feature = "xcb"))]
                    {
                        panic!("you need to enable \"xcb\" feature")
                    }
                }
                d => panic!("invalid device type: {}", d),
            }
        }
    }

    #[cfg(any(feature = "xlib", feature = "xcb", feature = "dox"))]
    pub fn debug_get_precision(&self) -> i32 {
        unsafe {
            match self.get_type() {
                DeviceType::Xlib => {
                    #[cfg(feature = "xlib")]
                    {
                        ffi::cairo_xlib_device_debug_get_precision(self.to_raw_none())
                    }
                    #[cfg(not(feature = "xlib"))]
                    {
                        panic!("you need to enable \"xlib\" feature")
                    }
                }
                DeviceType::Xcb => {
                    #[cfg(feature = "xcb")]
                    {
                        ffi::cairo_xcb_device_debug_get_precision(self.to_raw_none())
                    }
                    #[cfg(not(feature = "xcb"))]
                    {
                        panic!("you need to enable \"xcb\" feature")
                    }
                }
                d => panic!("invalid device type: {}", d),
            }
        }
    }

    #[cfg(any(feature = "xlib", feature = "xcb", feature = "dox"))]
    pub fn debug_set_precision(&self, precision: i32) {
        unsafe {
            match self.get_type() {
                DeviceType::Xlib => {
                    #[cfg(feature = "xlib")]
                    {
                        ffi::cairo_xlib_device_debug_set_precision(self.to_raw_none(), precision)
                    }
                    #[cfg(not(feature = "xlib"))]
                    {
                        panic!("you need to enable \"xlib\" feature")
                    }
                }
                DeviceType::Xcb => {
                    #[cfg(feature = "xcb")]
                    {
                        ffi::cairo_xcb_device_debug_set_precision(self.to_raw_none(), precision)
                    }
                    #[cfg(not(feature = "xcb"))]
                    {
                        panic!("you need to enable \"xcb\" feature")
                    }
                }
                d => panic!("invalid device type: {}", d),
            }
        }
    }

    user_data_methods! {
        ffi::cairo_device_get_user_data,
        ffi::cairo_device_set_user_data,
    }
}

#[cfg(feature = "use_glib")]
impl<'a> ToGlibPtr<'a, *mut ffi::cairo_device_t> for Device {
    type Storage = &'a Device;

    #[inline]
    fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_device_t, Self> {
        Stash(self.to_raw_none(), self)
    }

    #[inline]
    fn to_glib_full(&self) -> *mut ffi::cairo_device_t {
        unsafe { ffi::cairo_device_reference(self.to_raw_none()) }
    }
}

#[cfg(feature = "use_glib")]
impl FromGlibPtrNone<*mut ffi::cairo_device_t> for Device {
    #[inline]
    unsafe fn from_glib_none(ptr: *mut ffi::cairo_device_t) -> Device {
        Self::from_raw_none(ptr)
    }
}

#[cfg(feature = "use_glib")]
impl FromGlibPtrBorrow<*mut ffi::cairo_device_t> for Device {
    #[inline]
    unsafe fn from_glib_borrow(ptr: *mut ffi::cairo_device_t) -> ::Borrowed<Device> {
        Self::from_raw_borrow(ptr)
    }
}

#[cfg(feature = "use_glib")]
impl FromGlibPtrFull<*mut ffi::cairo_device_t> for Device {
    #[inline]
    unsafe fn from_glib_full(ptr: *mut ffi::cairo_device_t) -> Device {
        Self::from_raw_full(ptr)
    }
}

#[cfg(feature = "use_glib")]
gvalue_impl!(
    Device,
    ffi::cairo_device_t,
    ffi::gobject::cairo_gobject_device_get_type
);

impl Clone for Device {
    fn clone(&self) -> Device {
        unsafe { Self::from_raw_none(ffi::cairo_device_reference(self.0.as_ptr())) }
    }
}

impl Drop for Device {
    fn drop(&mut self) {
        unsafe {
            ffi::cairo_device_destroy(self.0.as_ptr());
        }
    }
}

impl fmt::Display for Device {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Device")
    }
}
<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2014 Kashyap
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

use super::x86asm::outb;
use super::x86asm::inb;

const COM1 : u16 = 0x3f8;

pub fn earlyinit () {
	outb(COM1+2 , 0);
	outb(COM1+3, 0x80);    // Unlock divisor
	outb(COM1+0, 12);
	outb(COM1+1, 0);
	outb(COM1+3, 0x03);    // Lock divisor, 8 data bits.
	outb(COM1+4, 0);
	outb(COM1+1, 0x01);    // Enable receive interrupts.

	inb(COM1+5);

	outb(COM1, 66);
}
<commit_msg>added commented static variable - need to check how it is supposed to work<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2014 Kashyap
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

use super::x86asm::outb;
use super::x86asm::inb;

const COM1 : u16 = 0x3f8;
//static uartInitialized : bool = false;

pub fn earlyinit () {
	outb(COM1+2 , 0);
	outb(COM1+3, 0x80);    // Unlock divisor
	outb(COM1+0, 12);
	outb(COM1+1, 0);
	outb(COM1+3, 0x03);    // Lock divisor, 8 data bits.
	outb(COM1+4, 0);
	outb(COM1+1, 0x01);    // Enable receive interrupts.

	if inb(COM1+5) == 0xff {
		return;
	}

	//uartInitialized = true;

	outb(COM1, 66);
}
<|endoftext|>


<commit_before>use std::io::{Result, Error, ErrorKind};
use std::rc::Rc;

use mio;
use mio::{Token, Handler, EventSet, PollOpt};
use mio::tcp::{TcpStream, TcpListener};

use slab::Slab;

use frontend::Frontend;
use connection::{TokenType, ListenerToken, IncomingToken, OutgoingToken, Connection};

type EventLoop = mio::EventLoop<Driver>;

pub struct Driver {
    incoming_connections: Slab<Connection, IncomingToken>,
    outgoing_connections: Slab<Option<IncomingToken>, OutgoingToken>,
    listeners: Slab<Listener, ListenerToken>,
}

struct Listener {
    listener: TcpListener,
    frontend: Rc<Frontend>,
}

impl Driver {
    pub fn new() -> Driver {
        Driver {
            incoming_connections: Slab::new_starting_at(IncomingToken(1), 4096),
            outgoing_connections: Slab::new_starting_at(OutgoingToken(1), 4096),
            listeners: Slab::new_starting_at(ListenerToken(1), 128),
        }
    }

    pub fn register(&mut self, event_loop: &mut EventLoop, frontend: Rc<Frontend>) -> Result<()> {
        for listen_addr in frontend.listen_addrs() {
            let listener = try!(TcpListener::bind(&listen_addr));
            let token = try!(self.listeners
                                 .insert(Listener {
                                     listener: listener,
                                     frontend: frontend.clone(),
                                 })
                                 .map_err(|_| {
                                     Error::new(ErrorKind::Other, "Listener buffer full")
                                 }));

            try!(event_loop.register_opt(&self.listeners[token].listener,
                                         token.as_raw_token(),
                                         EventSet::readable(),
                                         PollOpt::edge()));

            debug!("Added listener or {:?}", listen_addr);
        }

        Ok(())
    }

    fn listener_ready(&mut self,
                      event_loop: &mut EventLoop,
                      token: ListenerToken,
                      events: EventSet) {
        assert!(events.is_readable());

        info!("Accepting connection");

        let listener = &self.listeners[token];

        let incoming = match listener.listener.accept() {
            Ok(Some(client)) => client,
            Ok(None) => {
                warn!("Accept would block");
                return;
            }
            Err(e) => {
                error!("Accept error: {}", e);
                return;
            }
        };

        let backend = listener.frontend.decide_backend();
        let target = backend.borrow_mut().decide_target();

        let outgoing = match TcpStream::connect(&target) {
            Ok(client) => client,
            Err(e) => {
                error!("Connect error: {}", e);
                return;
            }
        };

        let outgoing_token = self.outgoing_connections
                                 .insert(None)
                                 .expect("Outgoing buffer full");

        let incoming_token = self.incoming_connections
                                 .insert_with(|incoming_token| {
                                     Connection::new(incoming,
                                                     incoming_token,
                                                     outgoing,
                                                     outgoing_token)
                                 })
                                 .expect("Incoming buffer full");

        self.outgoing_connections[outgoing_token] = Some(incoming_token);

        let connection = &self.incoming_connections[incoming_token];

        event_loop.register_opt(connection.incoming_stream(),
                                incoming_token.as_raw_token(),
                                EventSet::all(),
                                PollOpt::edge())
                  .unwrap();
        event_loop.register_opt(connection.outgoing_stream(),
                                outgoing_token.as_raw_token(),
                                EventSet::all(),
                                PollOpt::edge())
                  .unwrap();
    }

    fn incoming_ready(&mut self,
                      event_loop: &mut EventLoop,
                      token: IncomingToken,
                      events: EventSet) {
        let mut remove = false;

        {
            let mut connection = &mut self.incoming_connections[token];

            connection.incoming_ready(events);
            connection.tick();

            if connection.is_incoming_closed() {
                remove = true;
            }
        }

        if remove {
            self.remove_connection(event_loop, token);
        }
    }

    fn outgoing_ready(&mut self,
                      event_loop: &mut EventLoop,
                      token: OutgoingToken,
                      events: EventSet) {
        let incoming_token = self.outgoing_connections[token]
                                 .expect("Event on unknown outgoing token");

        let mut remove = false;

        {
            let mut connection = &mut self.incoming_connections[incoming_token];

            connection.outgoing_ready(events);
            connection.tick();

            if connection.is_outgoing_closed() {
                remove = true;
            }
        }

        if remove {
            self.remove_connection(event_loop, incoming_token);
        }
    }

    fn remove_connection(&mut self, event_loop: &mut EventLoop, token: IncomingToken) {
        let connection = self.incoming_connections
                             .remove(token)
                             .expect("Can't remove already removed incoming connection");
        self.outgoing_connections
            .remove(connection.outgoing_token())
            .expect("Can't remove already removed outgoing connection");

        event_loop.deregister(connection.incoming_stream())
                  .expect("Can't deregister already deregistered incoming stream");
        event_loop.deregister(connection.outgoing_stream())
                  .expect("Can't deregister already deregistered outgoing stream");
    }
}

impl Handler for Driver {
    type Timeout = ();
    type Message = ();

    fn ready(&mut self, event_loop: &mut EventLoop, token: Token, events: EventSet) {
        if token == Token(0) {
            trace!("Event on token zero");
            return;
        }

        trace!("Event on token {:?}", token);

        match TokenType::from_raw_token(token) {
            TokenType::Listener(token) => self.listener_ready(event_loop, token, events),
            TokenType::Incoming(token) => self.incoming_ready(event_loop, token, events),
            TokenType::Outgoing(token) => self.outgoing_ready(event_loop, token, events),
        }
    }
}
<commit_msg>Handle token index failures a bit more gracefully<commit_after>use std::io::{Result, Error, ErrorKind};
use std::rc::Rc;

use mio;
use mio::{Token, Handler, EventSet, PollOpt};
use mio::tcp::{TcpStream, TcpListener};

use slab::Slab;

use frontend::Frontend;
use connection::{TokenType, ListenerToken, IncomingToken, OutgoingToken, Connection};

type EventLoop = mio::EventLoop<Driver>;

pub struct Driver {
    incoming_connections: Slab<Connection, IncomingToken>,
    outgoing_connections: Slab<Option<IncomingToken>, OutgoingToken>,
    listeners: Slab<Listener, ListenerToken>,
}

struct Listener {
    listener: TcpListener,
    frontend: Rc<Frontend>,
}

impl Driver {
    pub fn new() -> Driver {
        Driver {
            incoming_connections: Slab::new_starting_at(IncomingToken(1), 4096),
            outgoing_connections: Slab::new_starting_at(OutgoingToken(1), 4096),
            listeners: Slab::new_starting_at(ListenerToken(1), 128),
        }
    }

    pub fn register(&mut self, event_loop: &mut EventLoop, frontend: Rc<Frontend>) -> Result<()> {
        for listen_addr in frontend.listen_addrs() {
            let listener = try!(TcpListener::bind(&listen_addr));
            let token = try!(self.listeners
                                 .insert(Listener {
                                     listener: listener,
                                     frontend: frontend.clone(),
                                 })
                                 .map_err(|_| {
                                     Error::new(ErrorKind::Other, "Listener buffer full")
                                 }));

            try!(event_loop.register_opt(&self.listeners.get(token).unwrap().listener,
                                         token.as_raw_token(),
                                         EventSet::readable(),
                                         PollOpt::edge()));

            debug!("Added listener or {:?}", listen_addr);
        }

        Ok(())
    }

    fn listener_ready(&mut self,
                      event_loop: &mut EventLoop,
                      token: ListenerToken,
                      events: EventSet) {
        assert!(events.is_readable());

        if let Some(listener) = self.listeners.get(token) {
            info!("Accepting connection");

            let incoming = match listener.listener.accept() {
                Ok(Some(client)) => client,
                Ok(None) => {
                    warn!("Accept would block");
                    return;
                }
                Err(e) => {
                    error!("Accept error: {}", e);
                    return;
                }
            };

            let backend = listener.frontend.decide_backend();
            let target = backend.borrow_mut().decide_target();

            let outgoing = match TcpStream::connect(&target) {
                Ok(client) => client,
                Err(e) => {
                    error!("Connect error: {}", e);
                    return;
                }
            };

            let outgoing_token = self.outgoing_connections
                                     .insert(None)
                                     .expect("Outgoing buffer full");

            let incoming_token = self.incoming_connections
                                     .insert_with(|incoming_token| {
                                         Connection::new(incoming,
                                                         incoming_token,
                                                         outgoing,
                                                         outgoing_token)
                                     })
                                     .expect("Incoming buffer full");

            self.outgoing_connections[outgoing_token] = Some(incoming_token);

            let connection = self.incoming_connections.get(incoming_token).unwrap();

            event_loop.register_opt(connection.incoming_stream(),
                                    incoming_token.as_raw_token(),
                                    EventSet::all(),
                                    PollOpt::edge())
                      .unwrap();
            event_loop.register_opt(connection.outgoing_stream(),
                                    outgoing_token.as_raw_token(),
                                    EventSet::all(),
                                    PollOpt::edge())
                      .unwrap();
        } else {
            error!("Listener event on unknown token");
        }
    }

    fn incoming_ready(&mut self,
                      event_loop: &mut EventLoop,
                      token: IncomingToken,
                      events: EventSet) {
        let mut remove = false;

        if let Some(mut connection) = self.incoming_connections.get_mut(token) {
            connection.incoming_ready(events);
            connection.tick();

            if connection.is_incoming_closed() {
                remove = true;
            }
        }

        if remove {
            self.remove_connection(event_loop, token);
        }
    }

    fn outgoing_ready(&mut self,
                      event_loop: &mut EventLoop,
                      token: OutgoingToken,
                      events: EventSet) {
        if let Some(&Some(incoming_token)) = self.outgoing_connections.get(token) {
            let mut remove = false;

            if let Some(mut connection) = self.incoming_connections.get_mut(incoming_token) {
                connection.outgoing_ready(events);
                connection.tick();

                if connection.is_outgoing_closed() {
                    remove = true;
                }
            }

            if remove {
                self.remove_connection(event_loop, incoming_token);
            }
        }
    }

    fn remove_connection(&mut self, event_loop: &mut EventLoop, token: IncomingToken) {
        let connection = self.incoming_connections
                             .remove(token)
                             .expect("Can't remove already removed incoming connection");
        self.outgoing_connections
            .remove(connection.outgoing_token())
            .expect("Can't remove already removed outgoing connection");

        event_loop.deregister(connection.incoming_stream())
                  .expect("Can't deregister already deregistered incoming stream");
        event_loop.deregister(connection.outgoing_stream())
                  .expect("Can't deregister already deregistered outgoing stream");
    }
}

impl Handler for Driver {
    type Timeout = ();
    type Message = ();

    fn ready(&mut self, event_loop: &mut EventLoop, token: Token, events: EventSet) {
        if token == Token(0) {
            trace!("Event on token zero");
            return;
        }

        trace!("Event on token {:?}", token);

        match TokenType::from_raw_token(token) {
            TokenType::Listener(token) => self.listener_ready(event_loop, token, events),
            TokenType::Incoming(token) => self.incoming_ready(event_loop, token, events),
            TokenType::Outgoing(token) => self.outgoing_ready(event_loop, token, events),
        }
    }
}
<|endoftext|>


<commit_before>#![allow(unused_variables)]

extern crate glutin_window;
extern crate graphics;
extern crate rebind;
extern crate piston;
extern crate opengl_graphics;
extern crate viewport;

use glutin_window::GlutinWindow;
use graphics::*;
use opengl_graphics::{GlGraphics, OpenGL};
use piston::event_loop::{EventMap, Events};
use piston::input::{
    Event,
    Input,
    Motion,
    RenderArgs,
    RenderEvent,
    UpdateArgs,
    UpdateEvent
};
use piston::input::keyboard::Key;
use piston::input::Button::Keyboard;
use piston::window::{Window, WindowSettings};
use rebind::{Action, InputTranslator, RebindBuilder, Translated};
use std::cell::RefCell;
use std::rc::Rc;

type RcWindow = Rc<RefCell<GlutinWindow>>;
type RcGraphics = Rc<RefCell<GlGraphics>>;
type RgbaColor = [f32; 4];

const RED: RgbaColor = [1.0, 0.0, 0.0, 1.0];
const GREEN: RgbaColor = [0.0, 1.0, 0.0, 1.0];
const BLACK: RgbaColor = [0.0, 0.0, 0.0, 1.0];

struct App {
    window: RcWindow,
    graphics: RcGraphics,
    translator: InputTranslator<CharacterAction>,
    character: Character,
    cursor: VirtualCursor,
    bg_color: RgbaColor
}

impl App {
    fn input(&mut self, input: &Input) {
        if let Some(t) = self.translator.translate(input) {
            match t {
                Translated::Press(action) => {
                    const CHARACTER_WALK_SPEED_INCREMENT: f64 = 400.0;
                    match action {
                        CharacterAction::Jump => {
                            println!("You jumped! Yay!");
                        },
                        CharacterAction::MoveLeft => {
                            if self.character.current_velocity[0] > (self.character.max_velocity[0] * -1.0) {
                                self.character.current_velocity[0] -= CHARACTER_WALK_SPEED_INCREMENT;
                            }
                        },
                        CharacterAction::MoveRight => {
                            if self.character.current_velocity[0] < self.character.max_velocity[0] {
                                self.character.current_velocity[0] += CHARACTER_WALK_SPEED_INCREMENT;
                            }
                        }
                    }
                },
                Translated::Release(_) => {
                    self.character.current_velocity = [0.0, 0.0];
                },
                Translated::Move(Motion::MouseCursor(x, y)) => {
                    self.cursor.position = [x, y];
                },
                _ => { }
            }
        }
    }

    fn update(&mut self, args: &UpdateArgs, window: RcWindow) {
        // we need to pass the window to update (and set the size here) because using
        // the update event from the window events queue is currently broken.
        self.translator.set_size(window.borrow().size());

        // update the character's velocity
        let ctl = self.character.topleft;
        let v = self.character.current_velocity;

        self.character.topleft = [ctl[0] + (v[0] * args.dt), ctl[1] + (v[1] * args.dt)];
    }

    fn render(&mut self, args: &RenderArgs) {
        let mut gl_graphics = self.graphics.borrow_mut();

        // draw the background color
        {
            gl_graphics.draw(args.viewport(), |_, gl| clear(self.bg_color, gl));
        }

        // draw the character
        {
            let square = rectangle::square(self.character.topleft[0],
                                           self.character.topleft[1],
                                           self.character.size);

            gl_graphics.draw(args.viewport(), |c, gl| rectangle(self.character.color, square, c.transform, gl));
        }

        // draw the cursor dot
        {
            let dot = ellipse::circle(self.cursor.position[0],
                                      self.cursor.position[1],
                                      self.cursor.size);
            gl_graphics.draw(args.viewport(), |c, gl| ellipse(self.cursor.color, dot, c.transform, gl));
        }
    }
}

struct Character {
    color: RgbaColor,
    topleft: [f64; 2],
    current_velocity: [f64; 2],
    max_velocity: [f64; 2],
    size: f64
}

impl Character {
    fn new(col: [f32; 4], tl: [f64; 2], sz: f64) -> Self {
        Character {
            color: col,
            topleft: tl,
            current_velocity: [0.0, 0.0],
            max_velocity: [800.0, 0.0],
            size: sz
        }
    }
}

struct VirtualCursor {
    position: [f64; 2],
    color: RgbaColor,
    size: f64
}

impl VirtualCursor {
    fn new() -> Self {
        VirtualCursor {
            position: [0.0, 0.0],
            color: GREEN,
            size: 5.0
        }
    }
}

#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
enum CharacterAction {
    Jump,
    MoveLeft,
    MoveRight
}

impl Action for CharacterAction { }

fn main() {
    const OPENGL: OpenGL = OpenGL::V3_2;
    const WINDOW_SIZE: (u32, u32) = (800, 600);

    let window = WindowSettings::new("calaxite", WINDOW_SIZE)
        .exit_on_esc(true)
        .fullscreen(false)
        .opengl(OPENGL)
        .vsync(true)
        .build()
        .unwrap_or_else(|e| panic!("Could not create window: {}", e));

    let gl_graphics = GlGraphics::new(OPENGL);

    let translator = RebindBuilder::new(WINDOW_SIZE)
        .with_action_mapping(Keyboard(Key::Space), CharacterAction::Jump)
        .with_action_mapping(Keyboard(Key::Left),  CharacterAction::MoveLeft)
        .with_action_mapping(Keyboard(Key::A),     CharacterAction::MoveLeft)
        .with_action_mapping(Keyboard(Key::Right), CharacterAction::MoveRight)
        .with_action_mapping(Keyboard(Key::D),     CharacterAction::MoveRight)
        .build_translator();

    let character = Character::new(RED, [30.0, (WINDOW_SIZE.1 as f64) * 0.85], 50.0);

    let mut app = App {
        window: Rc::new(RefCell::new(window)),
        graphics: Rc::new(RefCell::new(gl_graphics)),
        translator: translator,
        character: character,
        cursor: VirtualCursor::new(),
        bg_color: BLACK
    };

    for e in  app.window.clone().events() {
        let app_window = app.window.clone();
        match e {
            Event::Render(r) => { app.render(&r); },
            Event::Update(u) => { app.update(&u, app_window); },
            Event::Input(i) => { app.input(&i); },
            _ => { }
        }
    }
}
<commit_msg>Simplify window creation unwrapping<commit_after>#![allow(unused_variables)]

extern crate glutin_window;
extern crate graphics;
extern crate rebind;
extern crate piston;
extern crate opengl_graphics;
extern crate viewport;

use glutin_window::GlutinWindow;
use graphics::*;
use opengl_graphics::{GlGraphics, OpenGL};
use piston::event_loop::{EventMap, Events};
use piston::input::{
    Event,
    Input,
    Motion,
    RenderArgs,
    RenderEvent,
    UpdateArgs,
    UpdateEvent
};
use piston::input::keyboard::Key;
use piston::input::Button::Keyboard;
use piston::window::{Window, WindowSettings};
use rebind::{Action, InputTranslator, RebindBuilder, Translated};
use std::cell::RefCell;
use std::rc::Rc;

type RcWindow = Rc<RefCell<GlutinWindow>>;
type RcGraphics = Rc<RefCell<GlGraphics>>;
type RgbaColor = [f32; 4];

const RED: RgbaColor = [1.0, 0.0, 0.0, 1.0];
const GREEN: RgbaColor = [0.0, 1.0, 0.0, 1.0];
const BLACK: RgbaColor = [0.0, 0.0, 0.0, 1.0];

struct App {
    window: RcWindow,
    graphics: RcGraphics,
    translator: InputTranslator<CharacterAction>,
    character: Character,
    cursor: VirtualCursor,
    bg_color: RgbaColor
}

impl App {
    fn input(&mut self, input: &Input) {
        if let Some(t) = self.translator.translate(input) {
            match t {
                Translated::Press(action) => {
                    const CHARACTER_WALK_SPEED_INCREMENT: f64 = 400.0;
                    match action {
                        CharacterAction::Jump => {
                            println!("You jumped! Yay!");
                        },
                        CharacterAction::MoveLeft => {
                            if self.character.current_velocity[0] > (self.character.max_velocity[0] * -1.0) {
                                self.character.current_velocity[0] -= CHARACTER_WALK_SPEED_INCREMENT;
                            }
                        },
                        CharacterAction::MoveRight => {
                            if self.character.current_velocity[0] < self.character.max_velocity[0] {
                                self.character.current_velocity[0] += CHARACTER_WALK_SPEED_INCREMENT;
                            }
                        }
                    }
                },
                Translated::Release(_) => {
                    self.character.current_velocity = [0.0, 0.0];
                },
                Translated::Move(Motion::MouseCursor(x, y)) => {
                    self.cursor.position = [x, y];
                },
                _ => { }
            }
        }
    }

    fn update(&mut self, args: &UpdateArgs, window: RcWindow) {
        // we need to pass the window to update (and set the size here) because using
        // the update event from the window events queue is currently broken.
        self.translator.set_size(window.borrow().size());

        // update the character's velocity
        let ctl = self.character.topleft;
        let v = self.character.current_velocity;

        self.character.topleft = [ctl[0] + (v[0] * args.dt), ctl[1] + (v[1] * args.dt)];
    }

    fn render(&mut self, args: &RenderArgs) {
        let mut gl_graphics = self.graphics.borrow_mut();

        // draw the background color
        {
            gl_graphics.draw(args.viewport(), |_, gl| clear(self.bg_color, gl));
        }

        // draw the character
        {
            let square = rectangle::square(self.character.topleft[0],
                                           self.character.topleft[1],
                                           self.character.size);

            gl_graphics.draw(args.viewport(), |c, gl| rectangle(self.character.color, square, c.transform, gl));
        }

        // draw the cursor dot
        {
            let dot = ellipse::circle(self.cursor.position[0],
                                      self.cursor.position[1],
                                      self.cursor.size);
            gl_graphics.draw(args.viewport(), |c, gl| ellipse(self.cursor.color, dot, c.transform, gl));
        }
    }
}

struct Character {
    color: RgbaColor,
    topleft: [f64; 2],
    current_velocity: [f64; 2],
    max_velocity: [f64; 2],
    size: f64
}

impl Character {
    fn new(col: [f32; 4], tl: [f64; 2], sz: f64) -> Self {
        Character {
            color: col,
            topleft: tl,
            current_velocity: [0.0, 0.0],
            max_velocity: [800.0, 0.0],
            size: sz
        }
    }
}

struct VirtualCursor {
    position: [f64; 2],
    color: RgbaColor,
    size: f64
}

impl VirtualCursor {
    fn new() -> Self {
        VirtualCursor {
            position: [0.0, 0.0],
            color: GREEN,
            size: 5.0
        }
    }
}

#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
enum CharacterAction {
    Jump,
    MoveLeft,
    MoveRight
}

impl Action for CharacterAction { }

fn main() {
    const OPENGL: OpenGL = OpenGL::V3_2;
    const WINDOW_SIZE: (u32, u32) = (800, 600);

    let window = WindowSettings::new("calaxite", WINDOW_SIZE)
        .exit_on_esc(true)
        .fullscreen(false)
        .opengl(OPENGL)
        .vsync(true)
        .build()
        .ok()
        .expect("Could not create window: {}");

    let gl_graphics = GlGraphics::new(OPENGL);

    let translator = RebindBuilder::new(WINDOW_SIZE)
        .with_action_mapping(Keyboard(Key::Space), CharacterAction::Jump)
        .with_action_mapping(Keyboard(Key::Left),  CharacterAction::MoveLeft)
        .with_action_mapping(Keyboard(Key::A),     CharacterAction::MoveLeft)
        .with_action_mapping(Keyboard(Key::Right), CharacterAction::MoveRight)
        .with_action_mapping(Keyboard(Key::D),     CharacterAction::MoveRight)
        .y_motion_inverted(true)
        .build_translator();

    let character = Character::new(RED, [30.0, (WINDOW_SIZE.1 as f64) * 0.85], 50.0);

    let mut app = App {
        window: Rc::new(RefCell::new(window)),
        graphics: Rc::new(RefCell::new(gl_graphics)),
        translator: translator,
        character: character,
        cursor: VirtualCursor::new(),
        bg_color: BLACK
    };

    for e in  app.window.clone().events() {
        let app_window = app.window.clone();
        match e {
            Event::Render(r) => { app.render(&r); },
            Event::Update(u) => { app.update(&u, app_window); },
            Event::Input(i) => { app.input(&i); },
            _ => { }
        }
    }
}
<|endoftext|>


<commit_before>extern crate libc;
extern crate neovim;

use std::old_io::{File, FileMode, FileAccess};

mod ffi {
    pub use libc::{c_int, c_uchar, c_void};
    pub use libc::funcs::posix88::unistd::{close, pipe, read, write};
    pub use libc::types::os::arch::c95::size_t;
}

fn nvim_execute(fd: ffi::c_int, command: &str) {
    let mut arr = neovim::Array::new();
    arr.add_string(command);
    let msg = neovim::serialize_message(1, "vim_command", &arr);
    let msg_ptr = msg.as_slice().as_ptr() as *const ffi::c_void;
    unsafe { ffi::write(fd, msg_ptr, msg.len() as ffi::size_t) };
}

fn receive_message(fd: ffi::c_int) -> Option<neovim::Array> {
    let mut buf : [ffi::c_uchar; 1024] = [0; 1024];
    let n = unsafe { ffi::read(fd, buf.as_mut_ptr() as *mut ffi::c_void, 1024) };
    if n < 0 {
        return None;
    }
    unsafe {
        let v = Vec::from_raw_buf(buf.as_ptr(), n as usize);
        let s = String::from_utf8_unchecked(v);
        Some(neovim::deserialize_message(&s))
    }
}

fn main() {
    // two pairs of anonymous pipes for msgpack-rpc between the logger and nvim
    let mut nvim_log : [ffi::c_int; 2] = [0; 2]; // to nvim from logger
    let mut log_nvim : [ffi::c_int; 2] = [0; 2]; // to logger from nvim
    unsafe {
        ffi::pipe(nvim_log.as_mut_ptr());
        ffi::pipe(log_nvim.as_mut_ptr());
    };

    // listen for events in a separate thread and log them
    ::std::thread::Thread::spawn(move || {
        // listen for bufenter events
        nvim_execute(nvim_log[1], "au BufEnter * call rpcnotify(1, \"bufenter\", bufname(\"\"))");

        // receive messages
        let mut file = File::open_mode(&Path::new("events.log"), FileMode::Append, FileAccess::Write);
        while let Some(recv_arr) = receive_message(log_nvim[0]) {
            if recv_arr.len() > 0 {
                file.write_all(format!("{:?}\n", recv_arr).into_bytes().as_slice());
            }
        }
    });

    // start nvim
    let mut args = Vec::new();
    for arg in ::std::env::args() {
        args.push(arg);
    }
    neovim::main_setup(&args);
    neovim::channel_from_fds(nvim_log[0], log_nvim[1]);
    neovim::main_loop();
}
<commit_msg>Improve function names<commit_after>extern crate libc;
extern crate neovim;

use std::old_io::{File, FileMode, FileAccess};

mod ffi {
    pub use libc::{c_int, c_uchar, c_void};
    pub use libc::funcs::posix88::unistd::{close, pipe, read, write};
    pub use libc::types::os::arch::c95::size_t;
}

fn send_message(fd: ffi::c_int, command: &str) {
    let mut arr = neovim::Array::new();
    arr.add_string(command);
    let msg = neovim::serialize_message(1, "vim_command", &arr);
    let msg_ptr = msg.as_slice().as_ptr() as *const ffi::c_void;
    unsafe { ffi::write(fd, msg_ptr, msg.len() as ffi::size_t) };
}

fn recv_message(fd: ffi::c_int) -> Option<neovim::Array> {
    let mut buf : [ffi::c_uchar; 1024] = [0; 1024];
    let n = unsafe { ffi::read(fd, buf.as_mut_ptr() as *mut ffi::c_void, 1024) };
    if n < 0 {
        return None;
    }
    unsafe {
        let v = Vec::from_raw_buf(buf.as_ptr(), n as usize);
        let s = String::from_utf8_unchecked(v);
        Some(neovim::deserialize_message(&s))
    }
}

fn main() {
    // two pairs of anonymous pipes for msgpack-rpc between the logger and nvim
    let mut nvim_log : [ffi::c_int; 2] = [0; 2]; // to nvim from logger
    let mut log_nvim : [ffi::c_int; 2] = [0; 2]; // to logger from nvim
    unsafe {
        ffi::pipe(nvim_log.as_mut_ptr());
        ffi::pipe(log_nvim.as_mut_ptr());
    };

    // listen for events in a separate thread and log them
    ::std::thread::Thread::spawn(move || {
        // listen for bufenter events
        send_message(nvim_log[1], "au BufEnter * call rpcnotify(1, \"bufenter\", bufname(\"\"))");

        // receive messages
        let mut file = File::open_mode(&Path::new("events.log"), FileMode::Append, FileAccess::Write);
        while let Some(recv_arr) = recv_message(log_nvim[0]) {
            if recv_arr.len() > 0 {
                file.write_all(format!("{:?}\n", recv_arr).into_bytes().as_slice());
            }
        }
    });

    // start nvim
    let mut args = Vec::new();
    for arg in ::std::env::args() {
        args.push(arg);
    }
    neovim::main_setup(&args);
    neovim::channel_from_fds(nvim_log[0], log_nvim[1]);
    neovim::main_loop();
}
<|endoftext|>


<commit_before>//! Complete commands within shells

/// Complete commands within bash
pub mod bash {
    use std::ffi::OsString;
    use std::io::Write;

    use unicode_xid::UnicodeXID;

    #[derive(clap::Subcommand)]
    #[clap(hide = true)]
    #[allow(missing_docs)]
    #[derive(Clone, Debug)]
    pub enum CompleteCommand {
        /// Register shell completions for this program
        Complete(CompleteArgs),
    }

    #[derive(clap::Args)]
    #[clap(group = clap::ArgGroup::new("complete").multiple(true).conflicts_with("register"))]
    #[allow(missing_docs)]
    #[derive(Clone, Debug)]
    pub struct CompleteArgs {
        /// Path to write completion-registration to
        #[clap(long, required = true, parse(from_os_str))]
        register: Option<std::path::PathBuf>,

        #[clap(
            long,
            required = true,
            value_name = "COMP_CWORD",
            hide_short_help = true,
            group = "complete"
        )]
        index: Option<usize>,

        #[clap(
            long,
            required = true,
            value_name = "COMP_CWORD",
            hide_short_help = true,
            group = "complete"
        )]
        ifs: Option<String>,

        #[clap(
            long = "type",
            required = true,
            arg_enum,
            hide_short_help = true,
            group = "complete"
        )]
        comp_type: Option<CompType>,

        #[clap(long, hide_short_help = true, group = "complete")]
        space: bool,

        #[clap(
            long,
            conflicts_with = "space",
            hide_short_help = true,
            group = "complete"
        )]
        no_space: bool,

        #[clap(raw = true, hide_short_help = true, group = "complete")]
        comp_words: Vec<OsString>,
    }

    impl CompleteCommand {
        /// Process the completion request
        pub fn complete(&self, cmd: &mut clap::Command) -> std::convert::Infallible {
            self.try_complete(cmd).unwrap_or_else(|e| e.exit());
            std::process::exit(0)
        }

        /// Process the completion request
        pub fn try_complete(&self, cmd: &mut clap::Command) -> clap::Result<()> {
            debug!("CompleteCommand::try_complete: {:?}", self);
            let CompleteCommand::Complete(args) = self;
            if let Some(out_path) = args.register.as_deref() {
                let mut buf = Vec::new();
                let name = cmd.get_name();
                let bin = cmd.get_bin_name().unwrap_or(cmd.get_name());
                register(name, [bin], bin, &Behavior::default(), &mut buf)?;
                if out_path == std::path::Path::new("-") {
                    std::io::stdout().write(&buf)?;
                } else {
                    if out_path.is_dir() {
                        let out_path = out_path.join(file_name(name));
                        std::fs::write(out_path, buf)?;
                    } else {
                        std::fs::write(out_path, buf)?;
                    }
                }
            } else {
                let index = args.index.unwrap_or_default();
                let comp_type = args.comp_type.unwrap_or_default();
                let space = match (args.space, args.no_space) {
                    (true, false) => Some(true),
                    (false, true) => Some(false),
                    (true, true) => {
                        unreachable!("`--space` and `--no-space` set, clap should prevent this")
                    }
                    (false, false) => None,
                }
                .unwrap();
                let current_dir = std::env::current_dir().ok();
                let completions = complete(
                    cmd,
                    args.comp_words.clone(),
                    index,
                    comp_type,
                    space,
                    current_dir.as_deref(),
                )?;

                let mut buf = Vec::new();
                for completion in &completions {
                    write!(&mut buf, "{}", completion.to_string_lossy())?;
                    write!(&mut buf, "{}", args.ifs.as_deref().unwrap_or("\n"))?;
                }
                std::io::stdout().write(&buf)?;
            }

            Ok(())
        }
    }

    /// The recommended file name for the registration code
    pub fn file_name(name: &str) -> String {
        format!("{}.bash", name)
    }

    /// Define the completion behavior
    pub enum Behavior {
        /// Bare bones behavior
        Minimal,
        /// Fallback to readline behavior when no matches are generated
        Readline,
        /// Customize bash's completion behavior
        Custom(String),
    }

    impl Default for Behavior {
        fn default() -> Self {
            Self::Readline
        }
    }

    /// Generate code to register the dynamic completion
    pub fn register(
        name: &str,
        executables: impl IntoIterator<Item = impl AsRef<str>>,
        completer: &str,
        behavior: &Behavior,
        buf: &mut dyn Write,
    ) -> Result<(), std::io::Error> {
        let escaped_name = name.replace("-", "_");
        debug_assert!(
            escaped_name.chars().all(|c| c.is_xid_continue()),
            "`name` must be an identifier, got `{}`",
            escaped_name
        );
        let mut upper_name = escaped_name.clone();
        upper_name.make_ascii_uppercase();

        let executables = executables
            .into_iter()
            .map(|s| shlex::quote(s.as_ref()).into_owned())
            .collect::<Vec<_>>()
            .join(" ");

        let options = match behavior {
            Behavior::Minimal => "-o nospace -o bashdefault",
            Behavior::Readline => "-o nospace -o default -o bashdefault",
            Behavior::Custom(c) => c.as_str(),
        };

        let completer = shlex::quote(completer);

        let script = r#"
_clap_complete_NAME() {
    local IFS=$'\013'
    local SUPPRESS_SPACE=0
    if compopt +o nospace 2> /dev/null; then
        SUPPRESS_SPACE=1
    fi
    if [[ ${SUPPRESS_SPACE} == 1 ]]; then
        SPACE_ARG="--no-space"
    else
        SPACE_ARG="--space"
    fi
    COMPREPLY=( $("COMPLETER" complete --index ${COMP_CWORD} --type ${COMP_TYPE} ${SPACE_ARG} --ifs="$IFS" -- "${COMP_WORDS[@]}") )
    if [[ $? != 0 ]]; then
        unset COMPREPLY
    elif [[ $SUPPRESS_SPACE == 1 ]] && [[ "${COMPREPLY-}" =~ [=/:]$ ]]; then
        compopt -o nospace
    fi
}
complete OPTIONS -F _clap_complete_NAME EXECUTABLES
"#
        .replace("NAME", &escaped_name)
        .replace("EXECUTABLES", &executables)
        .replace("OPTIONS", options)
        .replace("COMPLETER", &completer)
        .replace("UPPER", &upper_name);

        writeln!(buf, "{}", script)?;
        Ok(())
    }

    /// Type of completion attempted that caused a completion function to be called
    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
    #[non_exhaustive]
    pub enum CompType {
        /// Normal completion
        Normal,
        /// List completions after successive tabs
        Successive,
        /// List alternatives on partial word completion
        Alternatives,
        /// List completions if the word is not unmodified
        Unmodified,
        /// Menu completion
        Menu,
    }

    impl clap::ArgEnum for CompType {
        fn value_variants<'a>() -> &'a [Self] {
            &[
                Self::Normal,
                Self::Successive,
                Self::Alternatives,
                Self::Unmodified,
                Self::Menu,
            ]
        }
        fn to_possible_value<'a>(&self) -> ::std::option::Option<clap::PossibleValue<'a>> {
            match self {
                Self::Normal => {
                    let value = "9";
                    debug_assert_eq!(b'\t'.to_string(), value);
                    Some(
                        clap::PossibleValue::new(value)
                            .alias("normal")
                            .help("Normal completion"),
                    )
                }
                Self::Successive => {
                    let value = "63";
                    debug_assert_eq!(b'?'.to_string(), value);
                    Some(
                        clap::PossibleValue::new(value)
                            .alias("successive")
                            .help("List completions after successive tabs"),
                    )
                }
                Self::Alternatives => {
                    let value = "33";
                    debug_assert_eq!(b'!'.to_string(), value);
                    Some(
                        clap::PossibleValue::new(value)
                            .alias("alternatives")
                            .help("List alternatives on partial word completion"),
                    )
                }
                Self::Unmodified => {
                    let value = "64";
                    debug_assert_eq!(b'@'.to_string(), value);
                    Some(
                        clap::PossibleValue::new(value)
                            .alias("unmodified")
                            .help("List completions if the word is not unmodified"),
                    )
                }
                Self::Menu => {
                    let value = "37";
                    debug_assert_eq!(b'%'.to_string(), value);
                    Some(
                        clap::PossibleValue::new(value)
                            .alias("menu")
                            .help("Menu completion"),
                    )
                }
            }
        }
    }

    impl Default for CompType {
        fn default() -> Self {
            Self::Normal
        }
    }

    /// Complete the command specified
    pub fn complete(
        cmd: &mut clap::Command,
        _args: Vec<std::ffi::OsString>,
        _arg_index: usize,
        _comp_type: CompType,
        _trailing_space: bool,
        current_dir: Option<&std::path::Path>,
    ) -> Result<Vec<std::ffi::OsString>, std::io::Error> {
        cmd.build();

        let current_cmd = &*cmd;
        let completions = complete_new(current_cmd, current_dir)?;

        Ok(completions)
    }

    fn complete_new(
        cmd: &clap::Command,
        current_dir: Option<&std::path::Path>,
    ) -> Result<Vec<std::ffi::OsString>, std::io::Error> {
        let mut completions = Vec::new();

        completions.extend(
            crate::generator::utils::longs_and_visible_aliases(cmd)
                .into_iter()
                .map(|f| format!("--{}", f).into()),
        );
        completions.extend(
            crate::generator::utils::shorts_and_visible_aliases(cmd)
                .into_iter()
                .map(|f| format!("-{}", f).into()),
        );

        let mut positionals = Vec::new();
        let hints = cmd
            .get_positionals()
            .map(|p| p.get_value_hint())
            .map(|h| {
                if h == clap::ValueHint::Unknown {
                    clap::ValueHint::AnyPath
                } else {
                    h
                }
            })
            .collect::<std::collections::HashSet<_>>();
        for hint in hints {
            match hint {
                clap::ValueHint::Unknown => unreachable!("Filtered out"),
                clap::ValueHint::Other => {
                    // Should not complete
                }
                clap::ValueHint::AnyPath => {
                    positionals.extend(complete_path(current_dir, |_| true));
                }
                clap::ValueHint::FilePath => {
                    positionals.extend(complete_path(current_dir, |p| p.is_file()));
                }
                clap::ValueHint::DirPath => {
                    positionals.extend(complete_path(current_dir, |p| p.is_dir()));
                }
                clap::ValueHint::ExecutablePath => {
                    use is_executable::IsExecutable;
                    positionals.extend(complete_path(current_dir, |p| p.is_executable()));
                }
                clap::ValueHint::CommandName
                | clap::ValueHint::CommandString
                | clap::ValueHint::CommandWithArguments
                | clap::ValueHint::Username
                | clap::ValueHint::Hostname
                | clap::ValueHint::Url
                | clap::ValueHint::EmailAddress => {
                    // No completion implementation
                }
                _ => {
                    // Safe-ish fallback
                    positionals.extend(complete_path(current_dir, |_| true));
                }
            }
        }
        positionals.sort();
        positionals.dedup();
        completions.extend(positionals);

        completions.extend(all_subcommands(cmd).into_iter().map(|s| s));

        Ok(completions)
    }

    fn complete_path(
        current_dir: Option<&std::path::Path>,
        is_wanted: impl Fn(&std::path::Path) -> bool,
    ) -> Vec<OsString> {
        let mut completions = Vec::new();

        let current_dir = match current_dir {
            Some(current_dir) => current_dir,
            None => {
                return Vec::new();
            }
        };
        for entry in std::fs::read_dir(current_dir)
            .ok()
            .into_iter()
            .flatten()
            .filter_map(Result::ok)
        {
            let mut path = entry.path();
            if entry.metadata().map(|m| m.is_dir()).unwrap_or(false) {
                path.push(""); // Ensure trailing `/`
                let suggestion = path.strip_prefix(current_dir).unwrap();
                completions.push(suggestion.as_os_str().to_owned());
            } else {
                if is_wanted(&path) {
                    let suggestion = path.strip_prefix(current_dir).unwrap();
                    completions.push(suggestion.as_os_str().to_owned());
                }
            }
        }

        completions
    }

    fn all_subcommands(cmd: &clap::Command) -> Vec<OsString> {
        debug!("all_subcommands");

        let mut scs = crate::generator::utils::all_subcommands(cmd)
            .iter()
            .map(|x| OsString::from(&x.0))
            .collect::<Vec<_>>();
        scs.sort();
        scs.dedup();
        scs
    }
}
<commit_msg>feat(complete): Basic subcommand support<commit_after>//! Complete commands within shells

/// Complete commands within bash
pub mod bash {
    use std::ffi::OsString;
    use std::io::Write;

    use unicode_xid::UnicodeXID;

    #[derive(clap::Subcommand)]
    #[clap(hide = true)]
    #[allow(missing_docs)]
    #[derive(Clone, Debug)]
    pub enum CompleteCommand {
        /// Register shell completions for this program
        Complete(CompleteArgs),
    }

    #[derive(clap::Args)]
    #[clap(group = clap::ArgGroup::new("complete").multiple(true).conflicts_with("register"))]
    #[allow(missing_docs)]
    #[derive(Clone, Debug)]
    pub struct CompleteArgs {
        /// Path to write completion-registration to
        #[clap(long, required = true, parse(from_os_str))]
        register: Option<std::path::PathBuf>,

        #[clap(
            long,
            required = true,
            value_name = "COMP_CWORD",
            hide_short_help = true,
            group = "complete"
        )]
        index: Option<usize>,

        #[clap(
            long,
            required = true,
            value_name = "COMP_CWORD",
            hide_short_help = true,
            group = "complete"
        )]
        ifs: Option<String>,

        #[clap(
            long = "type",
            required = true,
            arg_enum,
            hide_short_help = true,
            group = "complete"
        )]
        comp_type: Option<CompType>,

        #[clap(long, hide_short_help = true, group = "complete")]
        space: bool,

        #[clap(
            long,
            conflicts_with = "space",
            hide_short_help = true,
            group = "complete"
        )]
        no_space: bool,

        #[clap(raw = true, hide_short_help = true, group = "complete")]
        comp_words: Vec<OsString>,
    }

    impl CompleteCommand {
        /// Process the completion request
        pub fn complete(&self, cmd: &mut clap::Command) -> std::convert::Infallible {
            self.try_complete(cmd).unwrap_or_else(|e| e.exit());
            std::process::exit(0)
        }

        /// Process the completion request
        pub fn try_complete(&self, cmd: &mut clap::Command) -> clap::Result<()> {
            debug!("CompleteCommand::try_complete: {:?}", self);
            let CompleteCommand::Complete(args) = self;
            if let Some(out_path) = args.register.as_deref() {
                let mut buf = Vec::new();
                let name = cmd.get_name();
                let bin = cmd.get_bin_name().unwrap_or(cmd.get_name());
                register(name, [bin], bin, &Behavior::default(), &mut buf)?;
                if out_path == std::path::Path::new("-") {
                    std::io::stdout().write(&buf)?;
                } else {
                    if out_path.is_dir() {
                        let out_path = out_path.join(file_name(name));
                        std::fs::write(out_path, buf)?;
                    } else {
                        std::fs::write(out_path, buf)?;
                    }
                }
            } else {
                let index = args.index.unwrap_or_default();
                let comp_type = args.comp_type.unwrap_or_default();
                let space = match (args.space, args.no_space) {
                    (true, false) => Some(true),
                    (false, true) => Some(false),
                    (true, true) => {
                        unreachable!("`--space` and `--no-space` set, clap should prevent this")
                    }
                    (false, false) => None,
                }
                .unwrap();
                let current_dir = std::env::current_dir().ok();
                let completions = complete(
                    cmd,
                    args.comp_words.clone(),
                    index,
                    comp_type,
                    space,
                    current_dir.as_deref(),
                )?;

                let mut buf = Vec::new();
                for completion in &completions {
                    write!(&mut buf, "{}", completion.to_string_lossy())?;
                    write!(&mut buf, "{}", args.ifs.as_deref().unwrap_or("\n"))?;
                }
                std::io::stdout().write(&buf)?;
            }

            Ok(())
        }
    }

    /// The recommended file name for the registration code
    pub fn file_name(name: &str) -> String {
        format!("{}.bash", name)
    }

    /// Define the completion behavior
    pub enum Behavior {
        /// Bare bones behavior
        Minimal,
        /// Fallback to readline behavior when no matches are generated
        Readline,
        /// Customize bash's completion behavior
        Custom(String),
    }

    impl Default for Behavior {
        fn default() -> Self {
            Self::Readline
        }
    }

    /// Generate code to register the dynamic completion
    pub fn register(
        name: &str,
        executables: impl IntoIterator<Item = impl AsRef<str>>,
        completer: &str,
        behavior: &Behavior,
        buf: &mut dyn Write,
    ) -> Result<(), std::io::Error> {
        let escaped_name = name.replace("-", "_");
        debug_assert!(
            escaped_name.chars().all(|c| c.is_xid_continue()),
            "`name` must be an identifier, got `{}`",
            escaped_name
        );
        let mut upper_name = escaped_name.clone();
        upper_name.make_ascii_uppercase();

        let executables = executables
            .into_iter()
            .map(|s| shlex::quote(s.as_ref()).into_owned())
            .collect::<Vec<_>>()
            .join(" ");

        let options = match behavior {
            Behavior::Minimal => "-o nospace -o bashdefault",
            Behavior::Readline => "-o nospace -o default -o bashdefault",
            Behavior::Custom(c) => c.as_str(),
        };

        let completer = shlex::quote(completer);

        let script = r#"
_clap_complete_NAME() {
    local IFS=$'\013'
    local SUPPRESS_SPACE=0
    if compopt +o nospace 2> /dev/null; then
        SUPPRESS_SPACE=1
    fi
    if [[ ${SUPPRESS_SPACE} == 1 ]]; then
        SPACE_ARG="--no-space"
    else
        SPACE_ARG="--space"
    fi
    COMPREPLY=( $("COMPLETER" complete --index ${COMP_CWORD} --type ${COMP_TYPE} ${SPACE_ARG} --ifs="$IFS" -- "${COMP_WORDS[@]}") )
    if [[ $? != 0 ]]; then
        unset COMPREPLY
    elif [[ $SUPPRESS_SPACE == 1 ]] && [[ "${COMPREPLY-}" =~ [=/:]$ ]]; then
        compopt -o nospace
    fi
}
complete OPTIONS -F _clap_complete_NAME EXECUTABLES
"#
        .replace("NAME", &escaped_name)
        .replace("EXECUTABLES", &executables)
        .replace("OPTIONS", options)
        .replace("COMPLETER", &completer)
        .replace("UPPER", &upper_name);

        writeln!(buf, "{}", script)?;
        Ok(())
    }

    /// Type of completion attempted that caused a completion function to be called
    #[derive(Copy, Clone, Debug, PartialEq, Eq)]
    #[non_exhaustive]
    pub enum CompType {
        /// Normal completion
        Normal,
        /// List completions after successive tabs
        Successive,
        /// List alternatives on partial word completion
        Alternatives,
        /// List completions if the word is not unmodified
        Unmodified,
        /// Menu completion
        Menu,
    }

    impl clap::ArgEnum for CompType {
        fn value_variants<'a>() -> &'a [Self] {
            &[
                Self::Normal,
                Self::Successive,
                Self::Alternatives,
                Self::Unmodified,
                Self::Menu,
            ]
        }
        fn to_possible_value<'a>(&self) -> ::std::option::Option<clap::PossibleValue<'a>> {
            match self {
                Self::Normal => {
                    let value = "9";
                    debug_assert_eq!(b'\t'.to_string(), value);
                    Some(
                        clap::PossibleValue::new(value)
                            .alias("normal")
                            .help("Normal completion"),
                    )
                }
                Self::Successive => {
                    let value = "63";
                    debug_assert_eq!(b'?'.to_string(), value);
                    Some(
                        clap::PossibleValue::new(value)
                            .alias("successive")
                            .help("List completions after successive tabs"),
                    )
                }
                Self::Alternatives => {
                    let value = "33";
                    debug_assert_eq!(b'!'.to_string(), value);
                    Some(
                        clap::PossibleValue::new(value)
                            .alias("alternatives")
                            .help("List alternatives on partial word completion"),
                    )
                }
                Self::Unmodified => {
                    let value = "64";
                    debug_assert_eq!(b'@'.to_string(), value);
                    Some(
                        clap::PossibleValue::new(value)
                            .alias("unmodified")
                            .help("List completions if the word is not unmodified"),
                    )
                }
                Self::Menu => {
                    let value = "37";
                    debug_assert_eq!(b'%'.to_string(), value);
                    Some(
                        clap::PossibleValue::new(value)
                            .alias("menu")
                            .help("Menu completion"),
                    )
                }
            }
        }
    }

    impl Default for CompType {
        fn default() -> Self {
            Self::Normal
        }
    }

    /// Complete the command specified
    pub fn complete(
        cmd: &mut clap::Command,
        args: Vec<std::ffi::OsString>,
        arg_index: usize,
        _comp_type: CompType,
        _trailing_space: bool,
        current_dir: Option<&std::path::Path>,
    ) -> Result<Vec<std::ffi::OsString>, std::io::Error> {
        cmd.build();

        let raw_args = clap_lex::RawArgs::new(args.into_iter());
        let mut cursor = raw_args.cursor();
        let mut target_cursor = raw_args.cursor();
        raw_args.seek(
            &mut target_cursor,
            clap_lex::SeekFrom::Start(arg_index as u64),
        );

        // TODO: Multicall support
        if !cmd.is_no_binary_name_set() {
            raw_args.next_os(&mut cursor);
        }

        let mut current_cmd = &*cmd;
        while let Some(arg) = raw_args.next(&mut cursor) {
            if let Ok(value) = arg.to_value() {
                if let Some(next_cmd) = current_cmd.find_subcommand(value) {
                    current_cmd = next_cmd;
                }
            }
            if cursor == target_cursor {
                break;
            }
        }

        let completions = complete_new(current_cmd, current_dir)?;

        Ok(completions)
    }

    fn complete_new(
        cmd: &clap::Command,
        current_dir: Option<&std::path::Path>,
    ) -> Result<Vec<std::ffi::OsString>, std::io::Error> {
        let mut completions = Vec::new();

        completions.extend(
            crate::generator::utils::longs_and_visible_aliases(cmd)
                .into_iter()
                .map(|f| format!("--{}", f).into()),
        );
        completions.extend(
            crate::generator::utils::shorts_and_visible_aliases(cmd)
                .into_iter()
                .map(|f| format!("-{}", f).into()),
        );

        let mut positionals = Vec::new();
        let hints = cmd
            .get_positionals()
            .map(|p| p.get_value_hint())
            .map(|h| {
                if h == clap::ValueHint::Unknown {
                    clap::ValueHint::AnyPath
                } else {
                    h
                }
            })
            .collect::<std::collections::HashSet<_>>();
        for hint in hints {
            match hint {
                clap::ValueHint::Unknown => unreachable!("Filtered out"),
                clap::ValueHint::Other => {
                    // Should not complete
                }
                clap::ValueHint::AnyPath => {
                    positionals.extend(complete_path(current_dir, |_| true));
                }
                clap::ValueHint::FilePath => {
                    positionals.extend(complete_path(current_dir, |p| p.is_file()));
                }
                clap::ValueHint::DirPath => {
                    positionals.extend(complete_path(current_dir, |p| p.is_dir()));
                }
                clap::ValueHint::ExecutablePath => {
                    use is_executable::IsExecutable;
                    positionals.extend(complete_path(current_dir, |p| p.is_executable()));
                }
                clap::ValueHint::CommandName
                | clap::ValueHint::CommandString
                | clap::ValueHint::CommandWithArguments
                | clap::ValueHint::Username
                | clap::ValueHint::Hostname
                | clap::ValueHint::Url
                | clap::ValueHint::EmailAddress => {
                    // No completion implementation
                }
                _ => {
                    // Safe-ish fallback
                    positionals.extend(complete_path(current_dir, |_| true));
                }
            }
        }
        positionals.sort();
        positionals.dedup();
        completions.extend(positionals);

        completions.extend(all_subcommands(cmd).into_iter().map(|s| s));

        Ok(completions)
    }

    fn complete_path(
        current_dir: Option<&std::path::Path>,
        is_wanted: impl Fn(&std::path::Path) -> bool,
    ) -> Vec<OsString> {
        let mut completions = Vec::new();

        let current_dir = match current_dir {
            Some(current_dir) => current_dir,
            None => {
                return Vec::new();
            }
        };
        for entry in std::fs::read_dir(current_dir)
            .ok()
            .into_iter()
            .flatten()
            .filter_map(Result::ok)
        {
            let mut path = entry.path();
            if entry.metadata().map(|m| m.is_dir()).unwrap_or(false) {
                path.push(""); // Ensure trailing `/`
                let suggestion = path.strip_prefix(current_dir).unwrap();
                completions.push(suggestion.as_os_str().to_owned());
            } else {
                if is_wanted(&path) {
                    let suggestion = path.strip_prefix(current_dir).unwrap();
                    completions.push(suggestion.as_os_str().to_owned());
                }
            }
        }

        completions
    }

    fn all_subcommands(cmd: &clap::Command) -> Vec<OsString> {
        debug!("all_subcommands");

        let mut scs = crate::generator::utils::all_subcommands(cmd)
            .iter()
            .map(|x| OsString::from(&x.0))
            .collect::<Vec<_>>();
        scs.sort();
        scs.dedup();
        scs
    }
}
<|endoftext|>