<commit_before>//! Open URLs in the web browsers available on a platform.
//!
//! Inspired by the [webbrowser](https://docs.python.org/2/library/webbrowser.html) python library.
//!
//! Currently state of platform support is:
//!
//! * macos => default, as well as browsers listed under [Browser](enum.Browser.html)
//! * windows => default browser only
//! * linux or *bsd => default browser only (uses $BROWSER env var, failing back to xdg-open, gvfs-open and
//! gnome-open, in that order)
//! * android => not supported right now
//! * ios => not supported right now
//!
//! Important note:
//!
//! * This library requires availability of browsers and a graphical environment during runtime
//! * `cargo test` will actually open the browser locally.
//!
//! # Examples
//!
//! ```no_run
//! use webbrowser;
//!
//! if webbrowser::open("http://github.com").is_ok() {
//!     // ...
//! }
//! ```

#[cfg(windows)]
extern crate widestring;
#[cfg(windows)]
extern crate winapi;

use std::default::Default;
use std::io::{Error, ErrorKind, Result};
use std::process::{ExitStatus, Output};
use std::str::FromStr;
use std::{error, fmt};

#[cfg(windows)]
use std::os::windows::process::ExitStatusExt;
#[cfg(windows)]
use std::ptr;

#[cfg(unix)]
use std::os::unix::process::ExitStatusExt;

#[cfg(not(windows))]
use std::process::Command;

#[cfg(windows)]
use widestring::U16CString;

#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
/// Browser types available
pub enum Browser {
    ///Operating system's default browser
    Default,

    ///Mozilla Firefox
    Firefox,

    ///Microsoft's Internet Explorer
    InternetExplorer,

    ///Google Chrome
    Chrome,

    ///Opera
    Opera,

    ///Mac OS Safari
    Safari,

    ///Haiku's WebPositive
    WebPositive,
}

///The Error type for parsing a string into a Browser.
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
pub struct ParseBrowserError;

impl fmt::Display for ParseBrowserError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("Invalid browser given")
    }
}

impl error::Error for ParseBrowserError {
    fn description(&self) -> &str {
        "invalid browser"
    }
}

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

impl fmt::Display for Browser {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Browser::Default => f.write_str("Default"),
            Browser::Firefox => f.write_str("Firefox"),
            Browser::InternetExplorer => f.write_str("Internet Explorer"),
            Browser::Chrome => f.write_str("Chrome"),
            Browser::Opera => f.write_str("Opera"),
            Browser::Safari => f.write_str("Safari"),
            Browser::WebPositive => f.write_str("WebPositive"),
        }
    }
}

impl FromStr for Browser {
    type Err = ParseBrowserError;

    fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
        match s {
            "firefox" => Ok(Browser::Firefox),
            "default" => Ok(Browser::Default),
            "ie" | "internet explorer" | "internetexplorer" => Ok(Browser::InternetExplorer),
            "chrome" => Ok(Browser::Chrome),
            "opera" => Ok(Browser::Opera),
            "safari" => Ok(Browser::Safari),
            "webpositive" => Ok(Browser::WebPositive),
            _ => Err(ParseBrowserError),
        }
    }
}

/// Opens the URL on the default browser of this platform
///
/// Returns Ok(..) so long as the browser invocation was successful. An Err(..) is returned only if
/// there was an error in running the command, or if the browser was not found.
///
/// Equivalent to:
/// ```no_run
/// # use webbrowser::{Browser, open_browser};
/// # let url = "http://example.com";
/// open_browser(Browser::Default, url);
/// ```
///
/// # Examples
/// ```no_run
/// use webbrowser;
///
/// if webbrowser::open("http://github.com").is_ok() {
///     // ...
/// }
/// ```
pub fn open(url: &str) -> Result<Output> {
    open_browser(Browser::Default, url)
}

/// Opens the specified URL on the specific browser (if available) requested. Return semantics are
/// the same as for [open](fn.open.html).
///
/// # Examples
/// ```no_run
/// use webbrowser::{open_browser, Browser};
///
/// if open_browser(Browser::Firefox, "http://github.com").is_ok() {
///     // ...
/// }
/// ```
pub fn open_browser(browser: Browser, url: &str) -> Result<Output> {
    open_browser_internal(browser, url).and_then(|status| {
        if let Some(code) = status.code() {
            if code == 0 {
                Ok(Output {
                    status: ExitStatus::from_raw(0),
                    stdout: vec![],
                    stderr: vec![],
                })
            } else {
                Err(Error::new(
                    ErrorKind::Other,
                    format!("return code {}", code),
                ))
            }
        } else {
            Err(Error::new(ErrorKind::Other, "interrupted by signal"))
        }
    })
}

/// Deal with opening of browsers on Windows, using [`ShellExecuteW`](
/// https://docs.microsoft.com/en-us/windows/desktop/api/shellapi/nf-shellapi-shellexecutew)
/// fucntion.
#[cfg(target_os = "windows")]
#[inline]
fn open_browser_internal(browser: Browser, url: &str) -> Result<ExitStatus> {
    use winapi::shared::winerror::SUCCEEDED;
    use winapi::um::combaseapi::{CoInitializeEx, CoUninitialize};
    use winapi::um::objbase::{COINIT_APARTMENTTHREADED, COINIT_DISABLE_OLE1DDE};
    use winapi::um::shellapi::ShellExecuteW;
    use winapi::um::winuser::SW_SHOWNORMAL;
    match browser {
        Browser::Default => {
            static OPEN: &[u16] = &['o' as u16, 'p' as u16, 'e' as u16, 'n' as u16, 0x0000];
            let url =
                U16CString::from_str(url).map_err(|e| Error::new(ErrorKind::InvalidInput, e))?;
            let code = unsafe {
                let coinitializeex_result = CoInitializeEx(
                    ptr::null_mut(),
                    COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE,
                );
                let code = ShellExecuteW(
                    ptr::null_mut(),
                    OPEN.as_ptr(),
                    url.as_ptr(),
                    ptr::null(),
                    ptr::null(),
                    SW_SHOWNORMAL,
                ) as usize as i32;
                if SUCCEEDED(coinitializeex_result) {
                    CoUninitialize();
                }
                code
            };
            if code > 32 {
                Ok(ExitStatus::from_raw(0))
            } else {
                Err(Error::last_os_error())
            }
        }
        _ => Err(Error::new(
            ErrorKind::NotFound,
            "Only the default browser is supported on this platform right now",
        )),
    }
}

/// Deal with opening of browsers on Mac OS X, using `open` command
#[cfg(target_os = "macos")]
#[inline]
fn open_browser_internal(browser: Browser, url: &str) -> Result<ExitStatus> {
    let mut cmd = Command::new("open");
    match browser {
        Browser::Default => cmd.arg(url).status(),
        _ => {
            let app: Option<&str> = match browser {
                Browser::Firefox => Some("Firefox"),
                Browser::Chrome => Some("Google Chrome"),
                Browser::Opera => Some("Opera"),
                Browser::Safari => Some("Safari"),
                Browser::WebPositive => Some("WebPositive"),
                _ => None,
            };
            match app {
                Some(name) => cmd.arg("-a").arg(name).arg(url).status(),
                None => Err(Error::new(
                    ErrorKind::NotFound,
                    format!("Unsupported browser {:?}", browser),
                )),
            }
        }
    }
}

/// Deal with opening of browsers on Linux and *BSD - currently supports only the default browser
///
/// The mechanism of opening the default browser is as follows:
/// 1. Attempt to use $BROWSER env var if available
/// 2. Attempt to open the url via xdg-open, gvfs-open, gnome-open, open, respectively, whichever works
///    first
#[cfg(any(
    target_os = "linux",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "openbsd",
    target_os = "haiku"
))]
#[inline]
fn open_browser_internal(browser: Browser, url: &str) -> Result<ExitStatus> {
    match browser {
        Browser::Default => open_on_unix_using_browser_env(url)
            .or_else(|_| -> Result<ExitStatus> { Command::new("xdg-open").arg(url).status() })
            .or_else(|r| -> Result<ExitStatus> {
                if let Ok(desktop) = ::std::env::var("XDG_CURRENT_DESKTOP") {
                    if desktop == "KDE" {
                        return Command::new("kioclient").arg("exec").arg(url).status();
                    }
                }
                Err(r) // If either `if` check fails, fall through to the next or_else
            })
            .or_else(|_| -> Result<ExitStatus> { Command::new("gvfs-open").arg(url).status() })
            .or_else(|_| -> Result<ExitStatus> { Command::new("gnome-open").arg(url).status() })
            .or_else(|_| -> Result<ExitStatus> { Command::new("open").arg(url).status() })
            .or_else(|_| -> Result<ExitStatus> {
                Command::new("kioclient").arg("exec").arg(url).status()
            })
            .or_else(|e| -> Result<ExitStatus> {
                if let Ok(_child) = Command::new("x-www-browser").arg(url).spawn() {
                    return Ok(ExitStatusExt::from_raw(0));
                }
                Err(e)
            }),
        _ => Err(Error::new(
            ErrorKind::NotFound,
            "Only the default browser is supported on this platform right now",
        )),
    }
}
#[cfg(any(
    target_os = "linux",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "openbsd",
    target_os = "haiku"
))]
fn open_on_unix_using_browser_env(url: &str) -> Result<ExitStatus> {
    let browsers = ::std::env::var("BROWSER")
        .map_err(|_| -> Error { Error::new(ErrorKind::NotFound, "BROWSER env not set") })?;
    for browser in browsers.split(':') {
        // $BROWSER can contain ':' delimited options, each representing a potential browser command line
        if !browser.is_empty() {
            // each browser command can have %s to represent URL, while %c needs to be replaced
            // with ':' and %% with '%'
            let cmdline = browser
                .replace("%s", url)
                .replace("%c", ":")
                .replace("%%", "%");
            let cmdarr: Vec<&str> = cmdline.split_whitespace().collect();
            let mut cmd = Command::new(&cmdarr[0]);
            if cmdarr.len() > 1 {
                cmd.args(&cmdarr[1..cmdarr.len()]);
            }
            if !browser.contains("%s") {
                // append the url as an argument only if it was not already set via %s
                cmd.arg(url);
            }
            if let Ok(status) = cmd.status() {
                return Ok(status);
            }
        }
    }
    Err(Error::new(
        ErrorKind::NotFound,
        "No valid command in $BROWSER",
    ))
}

#[cfg(not(any(
    target_os = "windows",
    target_os = "macos",
    target_os = "linux",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "openbsd",
    target_os = "haiku"
)))]
compile_error!("Only Windows, Mac OS, Linux, *BSD and Haiku are currently supported");

#[test]
fn test_open_default() {
    assert!(open("http://github.com").is_ok());
    assert!(open("http://github.com?dummy_query1=0&dummy_query2=ｎｏｎａｓｃｉｉ").is_ok());
}

#[test]
#[ignore]
fn test_open_firefox() {
    assert!(open_browser(Browser::Firefox, "http://github.com").is_ok());
}

#[test]
#[ignore]
fn test_open_chrome() {
    assert!(open_browser(Browser::Chrome, "http://github.com").is_ok());
}

#[test]
#[ignore]
#[cfg(target_os = "windows")]
fn test_open_internet_explorer() {
    assert!(open_browser(Browser::InternetExplorer, "http://github.com").is_ok());
}

#[test]
#[ignore]
fn test_open_safari() {
    assert!(open_browser(Browser::Safari, "http://github.com").is_ok());
}

#[test]
#[ignore]
fn test_open_webpositive() {
    assert!(open_browser(Browser::WebPositive, "http://github.com").is_ok());
}
<commit_msg>add android support<commit_after>//! Open URLs in the web browsers available on a platform.
//!
//! Inspired by the [webbrowser](https://docs.python.org/2/library/webbrowser.html) python library.
//!
//! Currently state of platform support is:
//!
//! * macos => default, as well as browsers listed under [Browser](enum.Browser.html)
//! * windows => default browser only
//! * linux or *bsd => default browser only (uses $BROWSER env var, failing back to xdg-open, gvfs-open and
//! gnome-open, in that order)
//! * android => not supported right now
//! * ios => not supported right now
//!
//! Important note:
//!
//! * This library requires availability of browsers and a graphical environment during runtime
//! * `cargo test` will actually open the browser locally.
//!
//! # Examples
//!
//! ```no_run
//! use webbrowser;
//!
//! if webbrowser::open("http://github.com").is_ok() {
//!     // ...
//! }
//! ```

#[cfg(windows)]
extern crate widestring;
#[cfg(windows)]
extern crate winapi;

use std::default::Default;
use std::io::{Error, ErrorKind, Result};
use std::process::{ExitStatus, Output};
use std::str::FromStr;
use std::{error, fmt};

#[cfg(windows)]
use std::os::windows::process::ExitStatusExt;
#[cfg(windows)]
use std::ptr;

#[cfg(unix)]
use std::os::unix::process::ExitStatusExt;

#[cfg(not(windows))]
use std::process::Command;

#[cfg(windows)]
use widestring::U16CString;

#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
/// Browser types available
pub enum Browser {
    ///Operating system's default browser
    Default,

    ///Mozilla Firefox
    Firefox,

    ///Microsoft's Internet Explorer
    InternetExplorer,

    ///Google Chrome
    Chrome,

    ///Opera
    Opera,

    ///Mac OS Safari
    Safari,

    ///Haiku's WebPositive
    WebPositive,
}

///The Error type for parsing a string into a Browser.
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
pub struct ParseBrowserError;

impl fmt::Display for ParseBrowserError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("Invalid browser given")
    }
}

impl error::Error for ParseBrowserError {
    fn description(&self) -> &str {
        "invalid browser"
    }
}

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

impl fmt::Display for Browser {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Browser::Default => f.write_str("Default"),
            Browser::Firefox => f.write_str("Firefox"),
            Browser::InternetExplorer => f.write_str("Internet Explorer"),
            Browser::Chrome => f.write_str("Chrome"),
            Browser::Opera => f.write_str("Opera"),
            Browser::Safari => f.write_str("Safari"),
            Browser::WebPositive => f.write_str("WebPositive"),
        }
    }
}

impl FromStr for Browser {
    type Err = ParseBrowserError;

    fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
        match s {
            "firefox" => Ok(Browser::Firefox),
            "default" => Ok(Browser::Default),
            "ie" | "internet explorer" | "internetexplorer" => Ok(Browser::InternetExplorer),
            "chrome" => Ok(Browser::Chrome),
            "opera" => Ok(Browser::Opera),
            "safari" => Ok(Browser::Safari),
            "webpositive" => Ok(Browser::WebPositive),
            _ => Err(ParseBrowserError),
        }
    }
}

/// Opens the URL on the default browser of this platform
///
/// Returns Ok(..) so long as the browser invocation was successful. An Err(..) is returned only if
/// there was an error in running the command, or if the browser was not found.
///
/// Equivalent to:
/// ```no_run
/// # use webbrowser::{Browser, open_browser};
/// # let url = "http://example.com";
/// open_browser(Browser::Default, url);
/// ```
///
/// # Examples
/// ```no_run
/// use webbrowser;
///
/// if webbrowser::open("http://github.com").is_ok() {
///     // ...
/// }
/// ```
pub fn open(url: &str) -> Result<Output> {
    open_browser(Browser::Default, url)
}

/// Opens the specified URL on the specific browser (if available) requested. Return semantics are
/// the same as for [open](fn.open.html).
///
/// # Examples
/// ```no_run
/// use webbrowser::{open_browser, Browser};
///
/// if open_browser(Browser::Firefox, "http://github.com").is_ok() {
///     // ...
/// }
/// ```
pub fn open_browser(browser: Browser, url: &str) -> Result<Output> {
    open_browser_internal(browser, url).and_then(|status| {
        if let Some(code) = status.code() {
            if code == 0 {
                Ok(Output {
                    status: ExitStatus::from_raw(0),
                    stdout: vec![],
                    stderr: vec![],
                })
            } else {
                Err(Error::new(
                    ErrorKind::Other,
                    format!("return code {}", code),
                ))
            }
        } else {
            Err(Error::new(ErrorKind::Other, "interrupted by signal"))
        }
    })
}

/// Deal with opening of browsers on Windows, using [`ShellExecuteW`](
/// https://docs.microsoft.com/en-us/windows/desktop/api/shellapi/nf-shellapi-shellexecutew)
/// fucntion.
#[cfg(target_os = "windows")]
#[inline]
fn open_browser_internal(browser: Browser, url: &str) -> Result<ExitStatus> {
    use winapi::shared::winerror::SUCCEEDED;
    use winapi::um::combaseapi::{CoInitializeEx, CoUninitialize};
    use winapi::um::objbase::{COINIT_APARTMENTTHREADED, COINIT_DISABLE_OLE1DDE};
    use winapi::um::shellapi::ShellExecuteW;
    use winapi::um::winuser::SW_SHOWNORMAL;
    match browser {
        Browser::Default => {
            static OPEN: &[u16] = &['o' as u16, 'p' as u16, 'e' as u16, 'n' as u16, 0x0000];
            let url =
                U16CString::from_str(url).map_err(|e| Error::new(ErrorKind::InvalidInput, e))?;
            let code = unsafe {
                let coinitializeex_result = CoInitializeEx(
                    ptr::null_mut(),
                    COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE,
                );
                let code = ShellExecuteW(
                    ptr::null_mut(),
                    OPEN.as_ptr(),
                    url.as_ptr(),
                    ptr::null(),
                    ptr::null(),
                    SW_SHOWNORMAL,
                ) as usize as i32;
                if SUCCEEDED(coinitializeex_result) {
                    CoUninitialize();
                }
                code
            };
            if code > 32 {
                Ok(ExitStatus::from_raw(0))
            } else {
                Err(Error::last_os_error())
            }
        }
        _ => Err(Error::new(
            ErrorKind::NotFound,
            "Only the default browser is supported on this platform right now",
        )),
    }
}

/// Deal with opening of browsers on Android
#[cfg(target_os = "android")]
#[inline]
fn open_browser_internal(browser: Browser, url: &str) -> Result<ExitStatus> {
    Command::new("am").arg("start").arg("--user").arg("0").arg("-a").arg("android.intent.action.VIEW").arg("-d").arg(url).status()
}

/// Deal with opening of browsers on Mac OS X, using `open` command
#[cfg(target_os = "macos")]
#[inline]
fn open_browser_internal(browser: Browser, url: &str) -> Result<ExitStatus> {
    let mut cmd = Command::new("open");
    match browser {
        Browser::Default => cmd.arg(url).status(),
        _ => {
            let app: Option<&str> = match browser {
                Browser::Firefox => Some("Firefox"),
                Browser::Chrome => Some("Google Chrome"),
                Browser::Opera => Some("Opera"),
                Browser::Safari => Some("Safari"),
                Browser::WebPositive => Some("WebPositive"),
                _ => None,
            };
            match app {
                Some(name) => cmd.arg("-a").arg(name).arg(url).status(),
                None => Err(Error::new(
                    ErrorKind::NotFound,
                    format!("Unsupported browser {:?}", browser),
                )),
            }
        }
    }
}

/// Deal with opening of browsers on Linux and *BSD - currently supports only the default browser
///
/// The mechanism of opening the default browser is as follows:
/// 1. Attempt to use $BROWSER env var if available
/// 2. Attempt to open the url via xdg-open, gvfs-open, gnome-open, open, respectively, whichever works
///    first
#[cfg(any(
    target_os = "linux",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "openbsd",
    target_os = "haiku"
))]
#[inline]
fn open_browser_internal(browser: Browser, url: &str) -> Result<ExitStatus> {
    match browser {
        Browser::Default => open_on_unix_using_browser_env(url)
            .or_else(|_| -> Result<ExitStatus> { Command::new("xdg-open").arg(url).status() })
            .or_else(|r| -> Result<ExitStatus> {
                if let Ok(desktop) = ::std::env::var("XDG_CURRENT_DESKTOP") {
                    if desktop == "KDE" {
                        return Command::new("kioclient").arg("exec").arg(url).status();
                    }
                }
                Err(r) // If either `if` check fails, fall through to the next or_else
            })
            .or_else(|_| -> Result<ExitStatus> { Command::new("gvfs-open").arg(url).status() })
            .or_else(|_| -> Result<ExitStatus> { Command::new("gnome-open").arg(url).status() })
            .or_else(|_| -> Result<ExitStatus> { Command::new("open").arg(url).status() })
            .or_else(|_| -> Result<ExitStatus> {
                Command::new("kioclient").arg("exec").arg(url).status()
            })
            .or_else(|e| -> Result<ExitStatus> {
                if let Ok(_child) = Command::new("x-www-browser").arg(url).spawn() {
                    return Ok(ExitStatusExt::from_raw(0));
                }
                Err(e)
            }),
        _ => Err(Error::new(
            ErrorKind::NotFound,
            "Only the default browser is supported on this platform right now",
        )),
    }
}
#[cfg(any(
    target_os = "linux",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "openbsd",
    target_os = "haiku"
))]
fn open_on_unix_using_browser_env(url: &str) -> Result<ExitStatus> {
    let browsers = ::std::env::var("BROWSER")
        .map_err(|_| -> Error { Error::new(ErrorKind::NotFound, "BROWSER env not set") })?;
    for browser in browsers.split(':') {
        // $BROWSER can contain ':' delimited options, each representing a potential browser command line
        if !browser.is_empty() {
            // each browser command can have %s to represent URL, while %c needs to be replaced
            // with ':' and %% with '%'
            let cmdline = browser
                .replace("%s", url)
                .replace("%c", ":")
                .replace("%%", "%");
            let cmdarr: Vec<&str> = cmdline.split_whitespace().collect();
            let mut cmd = Command::new(&cmdarr[0]);
            if cmdarr.len() > 1 {
                cmd.args(&cmdarr[1..cmdarr.len()]);
            }
            if !browser.contains("%s") {
                // append the url as an argument only if it was not already set via %s
                cmd.arg(url);
            }
            if let Ok(status) = cmd.status() {
                return Ok(status);
            }
        }
    }
    Err(Error::new(
        ErrorKind::NotFound,
        "No valid command in $BROWSER",
    ))
}

#[cfg(not(any(
    target_os = "android",
    target_os = "windows",
    target_os = "macos",
    target_os = "linux",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "openbsd",
    target_os = "haiku"
)))]
compile_error!("Only Windows, Mac OS, Linux, *BSD and Haiku are currently supported");

#[test]
fn test_open_default() {
    assert!(open("http://github.com").is_ok());
    assert!(open("http://github.com?dummy_query1=0&dummy_query2=ｎｏｎａｓｃｉｉ").is_ok());
}

#[test]
#[ignore]
fn test_open_firefox() {
    assert!(open_browser(Browser::Firefox, "http://github.com").is_ok());
}

#[test]
#[ignore]
fn test_open_chrome() {
    assert!(open_browser(Browser::Chrome, "http://github.com").is_ok());
}

#[test]
#[ignore]
#[cfg(target_os = "windows")]
fn test_open_internet_explorer() {
    assert!(open_browser(Browser::InternetExplorer, "http://github.com").is_ok());
}

#[test]
#[ignore]
fn test_open_safari() {
    assert!(open_browser(Browser::Safari, "http://github.com").is_ok());
}

#[test]
#[ignore]
fn test_open_webpositive() {
    assert!(open_browser(Browser::WebPositive, "http://github.com").is_ok());
}
<|endoftext|>


<commit_before>#![feature(asm, lang_items, unboxed_closures)]

extern crate spinlock;
extern crate scheduler;

mod multiboot;
mod init;

#[no_mangle]
pub fn main(multiboot: *const multiboot::Information) {

    unsafe{
        init::frame_stack(multiboot);
        scheduler::init();
        enable_interrupts();
    };

    print!("test\n\niuaeiae");
    let x = Box::new(5i);

    let y = 0xb8000 as *mut u64;
    unsafe{*y = 0xffffffffffffffff};

    print!("test");
    print!("test\n");
    println!("newline {}", x);

    scheduler::spawn(|| print!("I'm #1!\n"));

    fn test(name: &str) {
        loop {
            let mut x = 0u;
            for i in range(0,100000) {
                x = i;
            }
            print!("{}", name);
        }
    }

    scheduler::spawn(|| test("1"));
    scheduler::spawn(|| test("2"));
    scheduler::spawn(|| test("3"));
    scheduler::spawn(|| test("4"));
    scheduler::spawn(|| test("5"));
    scheduler::spawn(|| test("6"));

    test("m");

    loop{}
    panic!("end of os!");
}

/* Interrupt Handlers */

extern {
//    fn send_eoi(interrupt_number: u64) -> int; //return value to mark rax as used
}

unsafe fn out_byte(port: u16, data: u8) {
    asm!("outb %al, %dx" :: "{dx}"(port), "{al}"(data) :: "volatile");
}
unsafe fn in_byte(port: u16) -> u8 {
    let ret: u8;
    asm!("inb %dx, %al" : "={al}"(ret) : "{dx}"(port) :: "volatile");
    ret
}
unsafe fn enable_interrupts() {
    asm!("sti" :::: "volatile");
}
unsafe fn disable_interrupts() {
    asm!("cli" :::: "volatile");
}

unsafe fn send_eoi(interrupt_number: u64) {
    unsafe fn send_master_eoi() {out_byte(0x20, 0x20)}
    unsafe fn send_slave_eoi() {out_byte(0xA0, 0x20)}

    match interrupt_number {
        i if i >= 40 => {
            send_slave_eoi();
            send_master_eoi();
        },
        32...40 => send_master_eoi(),
        _ => {},
    }
}

#[no_mangle]
pub extern "C" fn interrupt_handler(interrupt_number: u64, error_code: u64, rsp:uint) {
    match interrupt_number {
        13 if error_code != 0 => panic!(
            "General Protection Fault: Segment error at segment 0x{:x}", error_code),
        32 => {},
        33 => print!("k"),
        50 => panic!("out of memory"),
        66 => println!("ending thread..."),
        _ => panic!("unknown interrupt! number: {}, error_code: {:x}",interrupt_number, error_code),
    };
    unsafe{send_eoi(interrupt_number)};

    //TODO enable interrupts

    //TODO move interrupt numbers to own crate (yield() etc)

    match interrupt_number {
        32 => unsafe{scheduler::reschedule(rsp)},
        66 => unsafe{scheduler::schedule()},
        _ => {},
    }
}

#[no_mangle]
pub extern "C" fn pagefault_handler(address: u64, error_code: u64, rsp:uint) {
    panic!("page fault: address: {:x}, error_code: {:b}, rsp: {:x}", address, error_code, rsp);
}

#[cfg(not(test))]
#[lang = "stack_exhausted"]
extern fn stack_exhausted() {panic!("stack exhausted");}

#[cfg(not(test))]
#[lang = "eh_personality"]
extern fn eh_personality() {unimplemented!();}

#[no_mangle]
#[allow(non_snake_case)]
pub extern "C" fn _Unwind_Resume() -> ! {
    unimplemented!();
}
<commit_msg>only print 20 times in test (and end thread afterwards)<commit_after>#![feature(asm, lang_items, unboxed_closures)]

extern crate spinlock;
extern crate scheduler;

mod multiboot;
mod init;

#[no_mangle]
pub fn main(multiboot: *const multiboot::Information) {

    unsafe{
        init::frame_stack(multiboot);
        scheduler::init();
        enable_interrupts();
    };

    print!("test\n\niuaeiae");
    let x = Box::new(5i);

    let y = 0xb8000 as *mut u64;
    unsafe{*y = 0xffffffffffffffff};

    print!("test");
    print!("test\n");
    println!("newline {}", x);

    scheduler::spawn(|| print!("I'm #1!\n"));

    fn test(name: &str) {
        for j in 0..20 {
            let mut x = 0u;
            for i in range(0,100000) {
                x = i;
            }
            print!("{}", name);
        }
    }

    scheduler::spawn(|| test("1"));
    scheduler::spawn(|| test("2"));
    scheduler::spawn(|| test("3"));
    scheduler::spawn(|| test("4"));
    scheduler::spawn(|| test("5"));
    scheduler::spawn(|| test("6"));

    loop{
        test("m");
    }
    panic!("end of os!");
}

/* Interrupt Handlers */

extern {
//    fn send_eoi(interrupt_number: u64) -> int; //return value to mark rax as used
}

unsafe fn out_byte(port: u16, data: u8) {
    asm!("outb %al, %dx" :: "{dx}"(port), "{al}"(data) :: "volatile");
}
unsafe fn in_byte(port: u16) -> u8 {
    let ret: u8;
    asm!("inb %dx, %al" : "={al}"(ret) : "{dx}"(port) :: "volatile");
    ret
}
unsafe fn enable_interrupts() {
    asm!("sti" :::: "volatile");
}
unsafe fn disable_interrupts() {
    asm!("cli" :::: "volatile");
}

unsafe fn send_eoi(interrupt_number: u64) {
    unsafe fn send_master_eoi() {out_byte(0x20, 0x20)}
    unsafe fn send_slave_eoi() {out_byte(0xA0, 0x20)}

    match interrupt_number {
        i if i >= 40 => {
            send_slave_eoi();
            send_master_eoi();
        },
        32...40 => send_master_eoi(),
        _ => {},
    }
}

#[no_mangle]
pub extern "C" fn interrupt_handler(interrupt_number: u64, error_code: u64, rsp:uint) {
    match interrupt_number {
        13 if error_code != 0 => panic!(
            "General Protection Fault: Segment error at segment 0x{:x}", error_code),
        32 => {},
        33 => print!("k"),
        50 => panic!("out of memory"),
        66 => println!("ending thread..."),
        _ => panic!("unknown interrupt! number: {}, error_code: {:x}",interrupt_number, error_code),
    };
    unsafe{send_eoi(interrupt_number)};

    //TODO enable interrupts

    //TODO move interrupt numbers to own crate (yield() etc)

    match interrupt_number {
        32 => unsafe{scheduler::reschedule(rsp)},
        66 => unsafe{scheduler::schedule()},
        _ => {},
    }
}

#[no_mangle]
pub extern "C" fn pagefault_handler(address: u64, error_code: u64, rsp:uint) {
    panic!("page fault: address: {:x}, error_code: {:b}, rsp: {:x}", address, error_code, rsp);
}

#[cfg(not(test))]
#[lang = "stack_exhausted"]
extern fn stack_exhausted() {panic!("stack exhausted");}

#[cfg(not(test))]
#[lang = "eh_personality"]
extern fn eh_personality() {unimplemented!();}

#[no_mangle]
#[allow(non_snake_case)]
pub extern "C" fn _Unwind_Resume() -> ! {
    unimplemented!();
}
<|endoftext|>


<commit_before>/*!
Compile-time dimensional analysis for various unit systems using Rust's type system.

Its goal is to provide zero cost unit safety while requiring minimal effort from the programmer.

For a short introduction and some examples, check out the [readme on
GitHub](https://github.com/paholg/dimensioned).

# Use

The recommended way to import this crate is with the rename

```rust
extern crate dimensioned as dim;
```

and then either import the module for the unit system you will be using,

```rust
# extern crate dimensioned as dim;
# fn main() {
use dim::si;
let x = 3.0 * si::M;
# }
```

or import the constants and/or types that you care about,

```rust
# extern crate dimensioned as dim;
# fn main() {
use dim::si::{Meter, M, S};
let x: Meter<f64> = 3.0 * M;
let t = 2.0 * S;
# }
```

For clarity, all the examples contained herein will import unit system modules.

# How it works

When a unit system is created, say the SI system, a struct with two parameters is made; in this
case `SI<V, U>`. The first parameter, `V`, is for the value type -- it can be anything, and is the
value to which we are giving units. The second parameter, `U`, is where the magic happens. It
represents the units, and is a type-level array of type-level numbers. Now, these guys have some
pretty ugly type signatures, so I will express the array as the macro which maps to it, `tarr![]`,
and the type numbers as their aliases; `N1` for -1, `Z0` for 0, `P1` for 1, etc.

The SI system has seven base units, so that's how many elements the type array needs to have. The
order is the order in which the base units were defined, and the values of the type numbers are the
power to which each unit is raised. For example, the first three SI units, in order, are `Meter`,
`Kilogram`, `Second`, so the following aliases exist:

```ignore
type Meter<V>    = SI<V, tarr![P1, Z0, Z0, Z0, Z0, Z0, Z0]>;
type Kilogram<V> = SI<V, tarr![Z0, P1, Z0, Z0, Z0, Z0, Z0]>;
type Second<V>   = SI<V, tarr![Z0, Z0, P1, Z0, Z0, Z0, Z0]>;
type Newton<V>   = SI<V, tarr![Z1, Z1, N2, Z0, Z0, Z0, Z0]>;
...
```

In addition to creating the unit system struct, `SI`, the type aliases, and constants for each,
various traits are implemented for the unit system. These include arithmetic operations, which work
as follows.

If you try to add a `Meter<f64>` to a `Second<f64>`, then you will get a compiler error because
they have different types and so addition is not defined. Multiplication, on the other hand, is
defined, and results in a normal multiplication of the value types, and the unit powers added.

So, multiplying `Meter<f64>` by `Second<f64>` gives `SI<f64, tarr![P1, Z0, P1, Z0, Z0, Z0,
Z0]>`.

## Example

```rust
extern crate dimensioned as dim;

use dim::si;

fn main() {
    let x = 3.0 * si::M;
    let t = 2.0 * si::S;
    assert_eq!("6 m*s", &format!("{}", x*t));
}
```

That's basically it. All of the dimensional safety comes from whether things typecheck, and from
performing type-level arithmetic, thanks to the [typenum](http://paholg.com/typenum/)
crate. Pretty much everything else is for ergonomics.

*/

#![doc(html_logo_url = "https://raw.githubusercontent.com/paholg/dimensioned/master/favicon.png",
       html_favicon_url = "https://raw.githubusercontent.com/paholg/dimensioned/master/favicon.png",
       html_root_url = "http://paholg.com/dimensioned")]

#![no_std]

#![warn(missing_docs)]

#![cfg_attr(feature = "oibit", feature(optin_builtin_traits))]
#![cfg_attr(feature = "spec", feature(specialization))]

#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

#![allow(unknown_lints)]
#![deny(clippy)]
#![allow(type_complexity, float_cmp, useless_attribute, doc_markdown)]

// Macro debugging
// #![feature(trace_macros)]
// trace_macros!(true);

pub extern crate typenum;

// Copied from typenum so that users don't have to import typenum for the make_units macro to work.
// Only change is the paths.
/// Construct a type-level array of type-level integers
///
/// # Example
/// ```rust
/// #[macro_use]
/// extern crate dimensioned as dim;
/// #[macro_use]
/// extern crate generic_array;
///
/// use dim::typenum::consts::*;
/// type TArr = tarr![P3, P2, N5, N8, P2];
///
/// fn main() {
///     use dim::array::ToGA;
///     let x = TArr::to_ga();
///     let y = arr![isize; 3, 2, -5, -8, 2];
///
///     assert_eq!(x, y);
/// }
/// ```
#[macro_export]
macro_rules! tarr {
    () => ( $crate::typenum::ATerm );
    ($n:ty) => ( $crate::typenum::TArr<$n, $crate::typenum::ATerm> );
    ($n:ty,) => ( $crate::typenum::TArr<$n, $crate::typenum::ATerm> );
    ($n:ty, $($tail:ty),+) => ( $crate::typenum::TArr<$n, tarr![$($tail),+]> );
    ($n:ty, $($tail:ty),+,) => ( $crate::typenum::TArr<$n, tarr![$($tail),+]> );
}

// Get a warning without this. If it's fixed, remove `useless_attribute` from clippy allow list
#[allow(unused_imports)]
#[macro_use]
pub extern crate generic_array;

#[macro_use] mod make_units;
mod fmt;

include!(concat!(env!("OUT_DIR"), "/unit_systems.rs"));
pub mod traits;
pub mod dimensions;
pub mod conversion;
pub mod array;
pub mod f32prefixes;
pub mod f64prefixes;

pub use traits::*;
pub use unit_systems::{si, ucum, cgs, mks, fps};

// Used for the make_units macro
#[doc(hidden)]
pub mod dimcore {
    pub use core::{marker, fmt, ops, mem, f32, f64};
}
<commit_msg>Fix doc typo<commit_after>/*!
Compile-time dimensional analysis for various unit systems using Rust's type system.

Its goal is to provide zero cost unit safety while requiring minimal effort from the programmer.

For a short introduction and some examples, check out the [readme on
GitHub](https://github.com/paholg/dimensioned).

# Use

The recommended way to import this crate is with the rename

```rust
extern crate dimensioned as dim;
```

and then either import the module for the unit system you will be using,

```rust
# extern crate dimensioned as dim;
# fn main() {
use dim::si;
let x = 3.0 * si::M;
# }
```

or import the constants and/or types that you care about,

```rust
# extern crate dimensioned as dim;
# fn main() {
use dim::si::{Meter, M, S};
let x: Meter<f64> = 3.0 * M;
let t = 2.0 * S;
# }
```

For clarity, all the examples contained herein will import unit system modules.

# How it works

When a unit system is created, say the SI system, a struct with two parameters is made; in this
case `SI<V, U>`. The first parameter, `V`, is for the value type -- it can be anything, and is the
value to which we are giving units. The second parameter, `U`, is where the magic happens. It
represents the units, and is a type-level array of type-level numbers. Now, these guys have some
pretty ugly type signatures, so I will express the array as the macro which maps to it, `tarr![]`,
and the type numbers as their aliases; `N1` for -1, `Z0` for 0, `P1` for 1, etc.

The SI system has seven base units, so that's how many elements the type array needs to have. The
order is the order in which the base units were defined, and the values of the type numbers are the
power to which each unit is raised. For example, the first three SI units, in order, are `Meter`,
`Kilogram`, `Second`, so the following aliases exist:

```ignore
type Meter<V>    = SI<V, tarr![P1, Z0, Z0, Z0, Z0, Z0, Z0]>;
type Kilogram<V> = SI<V, tarr![Z0, P1, Z0, Z0, Z0, Z0, Z0]>;
type Second<V>   = SI<V, tarr![Z0, Z0, P1, Z0, Z0, Z0, Z0]>;
type Newton<V>   = SI<V, tarr![P1, P1, N2, Z0, Z0, Z0, Z0]>;
...
```

In addition to creating the unit system struct, `SI`, the type aliases, and constants for each,
various traits are implemented for the unit system. These include arithmetic operations, which work
as follows.

If you try to add a `Meter<f64>` to a `Second<f64>`, then you will get a compiler error because
they have different types and so addition is not defined. Multiplication, on the other hand, is
defined, and results in a normal multiplication of the value types, and the unit powers added.

So, multiplying `Meter<f64>` by `Second<f64>` gives `SI<f64, tarr![P1, Z0, P1, Z0, Z0, Z0,
Z0]>`.

## Example

```rust
extern crate dimensioned as dim;

use dim::si;

fn main() {
    let x = 3.0 * si::M;
    let t = 2.0 * si::S;
    assert_eq!("6 m*s", &format!("{}", x*t));
}
```

That's basically it. All of the dimensional safety comes from whether things typecheck, and from
performing type-level arithmetic, thanks to the [typenum](http://paholg.com/typenum/)
crate. Pretty much everything else is for ergonomics.

*/

#![doc(html_logo_url = "https://raw.githubusercontent.com/paholg/dimensioned/master/favicon.png",
       html_favicon_url = "https://raw.githubusercontent.com/paholg/dimensioned/master/favicon.png",
       html_root_url = "http://paholg.com/dimensioned")]

#![no_std]

#![warn(missing_docs)]

#![cfg_attr(feature = "oibit", feature(optin_builtin_traits))]
#![cfg_attr(feature = "spec", feature(specialization))]

#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

#![allow(unknown_lints)]
#![deny(clippy)]
#![allow(type_complexity, float_cmp, useless_attribute, doc_markdown)]

// Macro debugging
// #![feature(trace_macros)]
// trace_macros!(true);

pub extern crate typenum;

// Copied from typenum so that users don't have to import typenum for the make_units macro to work.
// Only change is the paths.
/// Construct a type-level array of type-level integers
///
/// # Example
/// ```rust
/// #[macro_use]
/// extern crate dimensioned as dim;
/// #[macro_use]
/// extern crate generic_array;
///
/// use dim::typenum::consts::*;
/// type TArr = tarr![P3, P2, N5, N8, P2];
///
/// fn main() {
///     use dim::array::ToGA;
///     let x = TArr::to_ga();
///     let y = arr![isize; 3, 2, -5, -8, 2];
///
///     assert_eq!(x, y);
/// }
/// ```
#[macro_export]
macro_rules! tarr {
    () => ( $crate::typenum::ATerm );
    ($n:ty) => ( $crate::typenum::TArr<$n, $crate::typenum::ATerm> );
    ($n:ty,) => ( $crate::typenum::TArr<$n, $crate::typenum::ATerm> );
    ($n:ty, $($tail:ty),+) => ( $crate::typenum::TArr<$n, tarr![$($tail),+]> );
    ($n:ty, $($tail:ty),+,) => ( $crate::typenum::TArr<$n, tarr![$($tail),+]> );
}

// Get a warning without this. If it's fixed, remove `useless_attribute` from clippy allow list
#[allow(unused_imports)]
#[macro_use]
pub extern crate generic_array;

#[macro_use] mod make_units;
mod fmt;

include!(concat!(env!("OUT_DIR"), "/unit_systems.rs"));
pub mod traits;
pub mod dimensions;
pub mod conversion;
pub mod array;
pub mod f32prefixes;
pub mod f64prefixes;

pub use traits::*;
pub use unit_systems::{si, ucum, cgs, mks, fps};

// Used for the make_units macro
#[doc(hidden)]
pub mod dimcore {
    pub use core::{marker, fmt, ops, mem, f32, f64};
}
<|endoftext|>


<commit_before>//! TODO
//!
//! * clarify terminology (Wiring, Option, Object).

#![feature(get_type_id)]
#![feature(box_syntax)]

#[macro_use] extern crate qdowncast;
extern crate qindex_multi;

#[macro_use] mod macros;

use qdowncast::QDowncastable;
use qindex_multi::MultiIndexable;

use std::any::{Any, TypeId};
use std::borrow::Borrow;
use std::collections::btree_map::{self, BTreeMap};
use std::ops::{Deref, DerefMut, Index, IndexMut};

// ++++++++++++++++++++ OptionReflect ++++++++++++++++++++

/// Our `Reflect`-derivative.
/// This supplies necessary information for retrieving an object at compile-time.
pub trait OptionReflect: Any {
    fn option_name() -> &'static str;
}

// ++++++++++++++++++++ GetObjectError ++++++++++++++++++++

#[derive(Debug, Clone)]
pub enum GetObjectError<'a> {
    TypeMismatch{ option_name: &'a str, expected: TypeId, found: TypeId },
    MissingOption(&'a str),
}

impl<'a> GetObjectError<'a> {
    pub fn type_mismatch<Expected>(found: TypeId) -> GetObjectError<'static>
        where Expected: OptionReflect
    {
        GetObjectError::TypeMismatch{
            option_name: Expected::option_name(),
            expected: TypeId::of::<Expected>(),
            found: found,
        }
    }
}

pub type GetObjectResult<'a, T> = Result<T, GetObjectError<'a>>;

// ++++++++++++++++++++ Entry ++++++++++++++++++++

/// TODO expose this to the user?
enum Entry<Obj: Any + ?Sized> {
    /// A single alternative option. Is always wired to exactly one object.
    Single(Box<Obj>),

    /// A option with zero or more alternatives. May be wired to one of its objects.
    Multi{
        wired: Option<usize>,
        alternatives: Vec<(String, Box<Obj>)>,
    }
}

impl<Obj: Any + ?Sized> Entry<Obj> {
    fn has_alternative(&self, alt_name: &str) -> bool {
        match self {
            &Entry::Multi{ ref alternatives, .. } => {
                alternatives.iter().any(|e| &*e.0 == alt_name)
            }
            _ => false,
        }
    }

    fn add_alternative(&mut self, alt_name: String, obj: Box<Obj>){
        assert!(!self.has_alternative(&alt_name),
                "Alternative '{}' already exists,", alt_name);

        match self {
            &mut Entry::Multi{ ref mut alternatives, .. } => {
                alternatives.push((alt_name, obj));
            }
            _ => {
                panic!("Can't add alternative '{}' to single alternative option", alt_name);
            }
        }
    }

    fn wire_alternative(&mut self, alt_name: &str) {
        assert!(self.has_alternative(&alt_name),
                "Can't wire missing alternative '{}'", alt_name);

        match self {
            &mut Entry::Multi{ ref mut wired, ref alternatives } => {
                *wired = Some(alternatives.iter().position(|e| &*e.0 == alt_name).unwrap());
            }
            _ => { unreachable!() }
        }
    }

    fn object_ref(&self) -> Option<&Obj> {
        match self {
            &Entry::Single(ref obj) => Some(&**obj),
            &Entry::Multi{ wired, ref alternatives } => match wired {
                Some(idx) => Some(&*alternatives[idx].1),
                None => None,
            }
        }
    }

    fn object_mut(&mut self) -> Option<&mut Obj> {
        match self {
            &mut Entry::Single(ref mut obj) => Some(&mut**obj),
            &mut Entry::Multi{ wired, ref mut alternatives } => match wired {
                Some(idx) => Some(&mut*alternatives[idx].1),
                None => None,
            }
        }
    }
}

// ++++++++++++++++++++ Register ++++++++++++++++++++

pub struct Register<Obj: Any + ?Sized> {
    entrys: BTreeMap<String, Entry<Obj>>,
}

impl<Obj: Any + ?Sized> Register<Obj> {

    /// Try to get the object wired to option `opt_name` immutably.
    pub fn get_object(&self, opt_name: &str) -> Option<&Obj> {
        self.entrys.get(opt_name).and_then(|entry| entry.object_ref())
    }

    /// Try to get the object wired to option `opt_name` mutably.
    pub fn get_object_mut(&mut self, opt_name: &str) -> Option<&mut Obj> {
        self.entrys.get_mut(opt_name).and_then(|entry| entry.object_mut())
    }

    pub fn get<T>(&self) -> GetObjectResult<&T>
        where T: OptionReflect, Obj: QDowncastable<T>
    {
        match self.get_object(T::option_name()) {
            Some(base) => {
                let ty = (&*base).get_type_id();
                match QDowncastable::downcast_ref(base) {
                    Some(ret) => Ok(ret),
                    None => Err(GetObjectError::type_mismatch::<T>(ty))
                }
            }
            None => Err(GetObjectError::MissingOption(T::option_name()))
        }
    }

    pub fn get_mut<T>(&mut self) -> GetObjectResult<&mut T>
        where T: OptionReflect, Obj: QDowncastable<T>
    {
        match self.get_object_mut(T::option_name()) {
            Some(base) => {
                let ty = (&*base).get_type_id();
                match QDowncastable::downcast_mut(base) {
                    Some(ret) => Ok(ret),
                    None => Err(GetObjectError::type_mismatch::<T>(ty))
                }
            }
            None => Err(GetObjectError::MissingOption(T::option_name()))
        }
    }

    pub fn iter(&self) -> Iter<Obj> {
        Iter{ entrys: self.entrys.iter() }
    }
    pub fn iter_mut(&mut self) -> IterMut<Obj> {
        IterMut{ entrys: self.entrys.iter_mut() }
    }
}

#[derive(Clone)]
pub struct Iter<'a, Obj: Any + ?Sized> {
    entrys: btree_map::Iter<'a, String, Entry<Obj>>,
}

impl<'a, Obj: Any + ?Sized> Iterator for Iter<'a, Obj> {
    type Item = (&'a str, &'a Obj);
    fn next(&mut self) -> Option<Self::Item> {
        match self.entrys.next() {
            Some((opt_name, entry)) => match entry.object_ref() {
                Some(obj) => Some((&opt_name, obj)),
                None => self.next(),
            },
            None => None,
        }
    }
}

pub struct IterMut<'a, Obj: Any + ?Sized> {
    entrys: btree_map::IterMut<'a, String, Entry<Obj>>,
}

impl<'a, Obj: Any + ?Sized> Iterator for IterMut<'a, Obj> {
    type Item = (&'a str, &'a mut Obj);
    fn next(&mut self) -> Option<Self::Item> {
        match self.entrys.next() {
            Some((opt_name, entry)) => match entry.object_mut() {
                Some(obj) => Some((&opt_name, obj)),
                None => self.next(),
            },
            None => None,
        }
    }
}

impl<'a, Obj: Any + ?Sized> IntoIterator for &'a Register<Obj> {
    type Item = <Self::IntoIter as Iterator>::Item;
    type IntoIter = Iter<'a, Obj>;
    fn into_iter(self) -> Self::IntoIter { self.iter() }
}

impl<'a, Obj: Any + ?Sized> IntoIterator for &'a mut Register<Obj> {
    type Item = <Self::IntoIter as Iterator>::Item;
    type IntoIter = IterMut<'a, Obj>;
    fn into_iter(self) -> Self::IntoIter { self.iter_mut() }
}

impl<'a, Str, Obj: ?Sized> Index<&'a Str> for Register<Obj>
    where Str: Ord + Borrow<str>, Obj: Any
{
    type Output = Obj;
    fn index(&self, name: &'a Str) -> &Self::Output {
        self.get_object(name.borrow()).unwrap()
    }
}

impl<'a, Str, Obj: ?Sized> IndexMut<&'a Str> for Register<Obj>
    where Str: Ord + Borrow<str>, Obj: Any
{
    fn index_mut(&mut self, name: &'a Str) -> &mut Self::Output {
        self.get_object_mut(name.borrow()).unwrap()
    }
}

unsafe impl<'a, Str, Obj: ?Sized> MultiIndexable<&'a Str> for Register<Obj>
    where Str: Ord + Borrow<str>, Obj: Any
{}

// ++++++++++++++++++++ RegisterModifier ++++++++++++++++++++

pub struct RegisterModifier<Obj: Any + ?Sized>(Register<Obj>);

impl<Obj: Any + ?Sized> Deref for RegisterModifier<Obj> {
    type Target = Register<Obj>;
    fn deref(&self) -> &Self::Target { &self.0 }
}
impl<Obj: Any + ?Sized> DerefMut for RegisterModifier<Obj> {
    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}

// TODO: remove duplicated code
impl<Obj: Any + ?Sized> RegisterModifier<Obj> {
    pub fn new() -> RegisterModifier<Obj> {
        RegisterModifier(Register{ entrys: BTreeMap::new() })
    }

    pub fn unwrap(self) -> Register<Obj> { self.0 }

    pub fn add_single(&mut self, name: String, obj: Box<Obj>){
        assert!(self.entrys.contains_key(&name), "Option '{}' already exists!", &name);

        self.entrys.insert(name, Entry::Single(obj));
    }

    pub fn add_option(&mut self, name: String){
        assert!(self.entrys.contains_key(&name), "Option '{}' already exists!", &name);

        self.entrys.insert(name, Entry::Multi{
            wired: None, alternatives: Vec::new()
        });
    }

    pub fn add_alternative(&mut self, opt_name: &str, alt_name: String, obj: Box<Obj>){
        let entry = self.entrys.get_mut(opt_name);
        let entry = entry.expect(&format!("Option '{}' doesn't exist", &opt_name));

        entry.add_alternative(alt_name, obj);
    }

    pub fn wire_alternative(&mut self, opt_name: &str, alt_name: &str){
        let entry = self.entrys.get_mut(opt_name);
        let entry = entry.expect(&format!("Option '{}' doesn't exist", &opt_name));

        entry.wire_alternative(alt_name);
    }
}

<commit_msg>add DefaultBase<commit_after>//! TODO
//!
//! * clarify terminology (Wiring, Option, Object).

#![feature(get_type_id)]
#![feature(box_syntax)]

#[macro_use] extern crate qdowncast;
extern crate qindex_multi;

#[macro_use] mod macros;

use qdowncast::QDowncastable;
use qindex_multi::MultiIndexable;

use std::any::{Any, TypeId};
use std::borrow::Borrow;
use std::collections::btree_map::{self, BTreeMap};
use std::ops::{Deref, DerefMut, Index, IndexMut};

// ++++++++++++++++++++ OptionReflect ++++++++++++++++++++

/// Our `Reflect`-derivative.
/// This supplies necessary information for retrieving an object at compile-time.
pub trait OptionReflect: Any {
    fn option_name() -> &'static str;
}

// ++++++++++++++++++++ GetObjectError ++++++++++++++++++++

#[derive(Debug, Clone)]
pub enum GetObjectError<'a> {
    TypeMismatch{ option_name: &'a str, expected: TypeId, found: TypeId },
    MissingOption(&'a str),
}

impl<'a> GetObjectError<'a> {
    pub fn type_mismatch<Expected>(found: TypeId) -> GetObjectError<'static>
        where Expected: OptionReflect
    {
        GetObjectError::TypeMismatch{
            option_name: Expected::option_name(),
            expected: TypeId::of::<Expected>(),
            found: found,
        }
    }
}

pub type GetObjectResult<'a, T> = Result<T, GetObjectError<'a>>;

// ++++++++++++++++++++ Entry ++++++++++++++++++++

/// TODO expose this to the user?
enum Entry<Obj: Any + ?Sized> {
    /// A single alternative option. Is always wired to exactly one object.
    Single(Box<Obj>),

    /// A option with zero or more alternatives. May be wired to one of its objects.
    Multi{
        wired: Option<usize>,
        alternatives: Vec<(String, Box<Obj>)>,
    }
}

impl<Obj: Any + ?Sized> Entry<Obj> {
    fn has_alternative(&self, alt_name: &str) -> bool {
        match self {
            &Entry::Multi{ ref alternatives, .. } => {
                alternatives.iter().any(|e| &*e.0 == alt_name)
            }
            _ => false,
        }
    }

    fn add_alternative(&mut self, alt_name: String, obj: Box<Obj>){
        assert!(!self.has_alternative(&alt_name),
                "Alternative '{}' already exists,", alt_name);

        match self {
            &mut Entry::Multi{ ref mut alternatives, .. } => {
                alternatives.push((alt_name, obj));
            }
            _ => {
                panic!("Can't add alternative '{}' to single alternative option", alt_name);
            }
        }
    }

    fn wire_alternative(&mut self, alt_name: &str) {
        assert!(self.has_alternative(&alt_name),
                "Can't wire missing alternative '{}'", alt_name);

        match self {
            &mut Entry::Multi{ ref mut wired, ref alternatives } => {
                *wired = Some(alternatives.iter().position(|e| &*e.0 == alt_name).unwrap());
            }
            _ => { unreachable!() }
        }
    }

    fn object_ref(&self) -> Option<&Obj> {
        match self {
            &Entry::Single(ref obj) => Some(&**obj),
            &Entry::Multi{ wired, ref alternatives } => match wired {
                Some(idx) => Some(&*alternatives[idx].1),
                None => None,
            }
        }
    }

    fn object_mut(&mut self) -> Option<&mut Obj> {
        match self {
            &mut Entry::Single(ref mut obj) => Some(&mut**obj),
            &mut Entry::Multi{ wired, ref mut alternatives } => match wired {
                Some(idx) => Some(&mut*alternatives[idx].1),
                None => None,
            }
        }
    }
}

// ++++++++++++++++++++ Register ++++++++++++++++++++

pub trait DefaultBase: Any {}
impl<T: DefaultBase + ?Sized> DefaultBase for Box<T> {}

pub struct Register<Obj: Any + ?Sized = DefaultBase> {
    entrys: BTreeMap<String, Entry<Obj>>,
}

impl<Obj: Any + ?Sized> Register<Obj> {

    /// Try to get the object wired to option `opt_name` immutably.
    pub fn get_object(&self, opt_name: &str) -> Option<&Obj> {
        self.entrys.get(opt_name).and_then(|entry| entry.object_ref())
    }

    /// Try to get the object wired to option `opt_name` mutably.
    pub fn get_object_mut(&mut self, opt_name: &str) -> Option<&mut Obj> {
        self.entrys.get_mut(opt_name).and_then(|entry| entry.object_mut())
    }

    pub fn get<T>(&self) -> GetObjectResult<&T>
        where T: OptionReflect, Obj: QDowncastable<T>
    {
        match self.get_object(T::option_name()) {
            Some(base) => {
                let ty = (&*base).get_type_id();
                match QDowncastable::downcast_ref(base) {
                    Some(ret) => Ok(ret),
                    None => Err(GetObjectError::type_mismatch::<T>(ty))
                }
            }
            None => Err(GetObjectError::MissingOption(T::option_name()))
        }
    }

    pub fn get_mut<T>(&mut self) -> GetObjectResult<&mut T>
        where T: OptionReflect, Obj: QDowncastable<T>
    {
        match self.get_object_mut(T::option_name()) {
            Some(base) => {
                let ty = (&*base).get_type_id();
                match QDowncastable::downcast_mut(base) {
                    Some(ret) => Ok(ret),
                    None => Err(GetObjectError::type_mismatch::<T>(ty))
                }
            }
            None => Err(GetObjectError::MissingOption(T::option_name()))
        }
    }

    pub fn iter(&self) -> Iter<Obj> {
        Iter{ entrys: self.entrys.iter() }
    }
    pub fn iter_mut(&mut self) -> IterMut<Obj> {
        IterMut{ entrys: self.entrys.iter_mut() }
    }
}

#[derive(Clone)]
pub struct Iter<'a, Obj: Any + ?Sized = DefaultBase> {
    entrys: btree_map::Iter<'a, String, Entry<Obj>>,
}

impl<'a, Obj: Any + ?Sized> Iterator for Iter<'a, Obj> {
    type Item = (&'a str, &'a Obj);
    fn next(&mut self) -> Option<Self::Item> {
        match self.entrys.next() {
            Some((opt_name, entry)) => match entry.object_ref() {
                Some(obj) => Some((&opt_name, obj)),
                None => self.next(),
            },
            None => None,
        }
    }
}

pub struct IterMut<'a, Obj: Any + ?Sized = DefaultBase> {
    entrys: btree_map::IterMut<'a, String, Entry<Obj>>,
}

impl<'a, Obj: Any + ?Sized> Iterator for IterMut<'a, Obj> {
    type Item = (&'a str, &'a mut Obj);
    fn next(&mut self) -> Option<Self::Item> {
        match self.entrys.next() {
            Some((opt_name, entry)) => match entry.object_mut() {
                Some(obj) => Some((&opt_name, obj)),
                None => self.next(),
            },
            None => None,
        }
    }
}

impl<'a, Obj: Any + ?Sized> IntoIterator for &'a Register<Obj> {
    type Item = <Self::IntoIter as Iterator>::Item;
    type IntoIter = Iter<'a, Obj>;
    fn into_iter(self) -> Self::IntoIter { self.iter() }
}

impl<'a, Obj: Any + ?Sized> IntoIterator for &'a mut Register<Obj> {
    type Item = <Self::IntoIter as Iterator>::Item;
    type IntoIter = IterMut<'a, Obj>;
    fn into_iter(self) -> Self::IntoIter { self.iter_mut() }
}

impl<'a, Str, Obj: ?Sized> Index<&'a Str> for Register<Obj>
    where Str: Ord + Borrow<str>, Obj: Any
{
    type Output = Obj;
    fn index(&self, name: &'a Str) -> &Self::Output {
        self.get_object(name.borrow()).unwrap()
    }
}

impl<'a, Str, Obj: ?Sized> IndexMut<&'a Str> for Register<Obj>
    where Str: Ord + Borrow<str>, Obj: Any
{
    fn index_mut(&mut self, name: &'a Str) -> &mut Self::Output {
        self.get_object_mut(name.borrow()).unwrap()
    }
}

unsafe impl<'a, Str, Obj: ?Sized> MultiIndexable<&'a Str> for Register<Obj>
    where Str: Ord + Borrow<str>, Obj: Any
{}

// ++++++++++++++++++++ RegisterModifier ++++++++++++++++++++

pub struct RegisterModifier<Obj: Any + ?Sized = DefaultBase>(Register<Obj>);

impl<Obj: Any + ?Sized> Deref for RegisterModifier<Obj> {
    type Target = Register<Obj>;
    fn deref(&self) -> &Self::Target { &self.0 }
}
impl<Obj: Any + ?Sized> DerefMut for RegisterModifier<Obj> {
    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}

// TODO: remove duplicated code
impl<Obj: Any + ?Sized> RegisterModifier<Obj> {
    pub fn new() -> RegisterModifier<Obj> {
        RegisterModifier(Register{ entrys: BTreeMap::new() })
    }

    pub fn unwrap(self) -> Register<Obj> { self.0 }

    pub fn add_single(&mut self, name: String, obj: Box<Obj>){
        assert!(self.entrys.contains_key(&name), "Option '{}' already exists!", &name);

        self.entrys.insert(name, Entry::Single(obj));
    }

    pub fn add_option(&mut self, name: String){
        assert!(self.entrys.contains_key(&name), "Option '{}' already exists!", &name);

        self.entrys.insert(name, Entry::Multi{
            wired: None, alternatives: Vec::new()
        });
    }

    pub fn add_alternative(&mut self, opt_name: &str, alt_name: String, obj: Box<Obj>){
        let entry = self.entrys.get_mut(opt_name);
        let entry = entry.expect(&format!("Option '{}' doesn't exist", &opt_name));

        entry.add_alternative(alt_name, obj);
    }

    pub fn wire_alternative(&mut self, opt_name: &str, alt_name: &str){
        let entry = self.entrys.get_mut(opt_name);
        let entry = entry.expect(&format!("Option '{}' doesn't exist", &opt_name));

        entry.wire_alternative(alt_name);
    }
}

<|endoftext|>


<commit_before>#![warn(missing_docs)]

//! # Pascal strings in Rust.
//!
//! A `PascalString`, or `ShortString` is a String which stores its data on the stack. Because of this, it has
//! a fixed maximum size, which cannot be changed. Traditionally, the size of a `PascalString` is 256 bytes -
//! the first byte stores the length, which means that each remaining byte is indexable using only that byte.
//!
//! This is a very niche string type - generally, you are better off using `std::string::String`, or the
//! `AsciiString` type from the `ascii` crate if you need an ascii string. They have no upper size limit, and
//! are cheaper to pass around as they are only 64 bytes on the stack. Generally, you should only use `PascalString` if:
//!
//! * You know that you absolutely, certainly cannot do without heap allocation.
//! * You need to store your string data inline into your `struct` type - for example if you will allocate a bunch
//!   into a pool allocator, and cannot afford the heap fragmentation.
//! * You will keep, allocate, and deallocate a *lot* of short strings in your program.

extern crate ascii;

mod pascal_str;
mod pascal_string;

const PASCAL_STRING_BUF_SIZE: usize = 255;

pub use pascal_str::{Chars, CharsMut, PascalStr};
pub use pascal_string::{IntoChars, PascalString, PascalStringAppendError, PascalStringCreateError, AsciiError};

#[cfg(test)]
mod tests {
    use super::*;
    use ascii::*;
    use std::borrow::Cow;
    use std::ffi::{CStr, CString};

    #[test]
    fn test_string_creation() {
        let test = "Hello, my world!".to_owned();
        let test_pascal = PascalString::from(&test).unwrap();
        assert_eq!(&test, test_pascal.as_str());

        let too_many_bytes = vec![12u8; 256];
        assert!(match PascalString::from(&too_many_bytes) {
            Err(PascalStringCreateError::InputTooLong) => true,
            _ => false
        });
    }

    #[test]
    fn test_character_append() {
        let mut string = PascalString::new();
        assert!(string.try_push('h').is_ok());
        string.push('e');
        string.push(76u8);
        string.push('l');
        string.push(AsciiChar::L);
        string.push('o');
        string.push(AsciiChar::Null);

        assert_eq!(string.as_str(), "heLlLo\0");
    }

    #[test]
    fn test_string_append() {
        let mut string = PascalString::new();
        string.push_str("Hola, ");
        string.push_str("Senor!");
        assert_eq!(string.as_str(), "Hola, Senor!");
    }

    #[test]
    fn test_string_indexing_and_char_iteration() {
        {
            let string = PascalString::from("q").unwrap();
            assert_eq!(string[0u8], AsciiChar::q);
        }

        {
            let string2 = PascalString::from("WASD").unwrap();
            let mut iter = string2.chars();
            assert_eq!(iter.next(), Some(&AsciiChar::W));
            assert_eq!(iter.next(), Some(&AsciiChar::A));
            assert_eq!(iter.next(), Some(&AsciiChar::S));
            assert_eq!(iter.next(), Some(&AsciiChar::D));
        }
    }

    #[test]
    fn test_as_cstr() {
        {
            let msg = "I am your favourite cookie monster >:-)\0";
            let pstr = PascalString::from(&msg).unwrap();
            let cstr = CStr::from_bytes_with_nul(msg.as_bytes()).unwrap();
            let pstr_as_cstr = pstr.as_cstr().unwrap();
            assert!(match pstr_as_cstr {
                Cow::Borrowed(_) => true,
                _ => false
            });
            assert_eq!(&*pstr_as_cstr, cstr);
        }

        {
            let oversized = ['l'; 255];
            let string_oversized = {
                let mut s = String::new();
                for i in 0..oversized.len() {
                    s.push(oversized[i]);
                }
                s
            };
            let pstr_oversized = PascalString::from_fixed_ascii_array(255, oversized).unwrap();
            let cstr_from_pstr_oversized = pstr_oversized.as_cstr().unwrap();
            let cstr_from_string_oversized = CString::new(string_oversized).unwrap();
            assert!(match cstr_from_pstr_oversized {
                Cow::Owned(_) => true,
                _ => false
            });
            assert_eq!(cstr_from_pstr_oversized.into_owned(), cstr_from_string_oversized);
        }

        {
            let has_interior_null = "lol\0hi";
            let pstr = PascalString::from(&has_interior_null).unwrap();
            let err = match pstr.as_cstr() {
                Err(err) => err,
                _ => panic!("incorrect result")
            };
            assert_eq!(err.interior_null_index(), 3);
        }
    }
}
<commit_msg>Use u8::MAX instead of literal value<commit_after>#![warn(missing_docs)]

//! # Pascal strings in Rust.
//!
//! A `PascalString`, or `ShortString` is a String which stores its data on the stack. Because of this, it has
//! a fixed maximum size, which cannot be changed. Traditionally, the size of a `PascalString` is 256 bytes -
//! the first byte stores the length, which means that each remaining byte is indexable using only that byte.
//!
//! This is a very niche string type - generally, you are better off using `std::string::String`, or the
//! `AsciiString` type from the `ascii` crate if you need an ascii string. They have no upper size limit, and
//! are cheaper to pass around as they are only 64 bytes on the stack. Generally, you should only use `PascalString` if:
//!
//! * You know that you absolutely, certainly cannot do without heap allocation.
//! * You need to store your string data inline into your `struct` type - for example if you will allocate a bunch
//!   into a pool allocator, and cannot afford the heap fragmentation.
//! * You will keep, allocate, and deallocate a *lot* of short strings in your program.

extern crate ascii;

mod pascal_str;
mod pascal_string;

pub use pascal_str::{Chars, CharsMut, PascalStr};
pub use pascal_string::{IntoChars, PascalString, PascalStringAppendError, PascalStringCreateError, AsciiError};
use std::u8;

const PASCAL_STRING_BUF_SIZE: usize = u8::MAX as usize;

#[cfg(test)]
mod tests {
    use super::*;
    use ascii::*;
    use std::borrow::Cow;
    use std::ffi::{CStr, CString};

    #[test]
    fn test_string_creation() {
        let test = "Hello, my world!".to_owned();
        let test_pascal = PascalString::from(&test).unwrap();
        assert_eq!(&test, test_pascal.as_str());

        let too_many_bytes = vec![12u8; 256];
        assert!(match PascalString::from(&too_many_bytes) {
            Err(PascalStringCreateError::InputTooLong) => true,
            _ => false
        });
    }

    #[test]
    fn test_character_append() {
        let mut string = PascalString::new();
        assert!(string.try_push('h').is_ok());
        string.push('e');
        string.push(76u8);
        string.push('l');
        string.push(AsciiChar::L);
        string.push('o');
        string.push(AsciiChar::Null);

        assert_eq!(string.as_str(), "heLlLo\0");
    }

    #[test]
    fn test_string_append() {
        let mut string = PascalString::new();
        string.push_str("Hola, ");
        string.push_str("Senor!");
        assert_eq!(string.as_str(), "Hola, Senor!");
    }

    #[test]
    fn test_string_indexing_and_char_iteration() {
        {
            let string = PascalString::from("q").unwrap();
            assert_eq!(string[0u8], AsciiChar::q);
        }

        {
            let string2 = PascalString::from("WASD").unwrap();
            let mut iter = string2.chars();
            assert_eq!(iter.next(), Some(&AsciiChar::W));
            assert_eq!(iter.next(), Some(&AsciiChar::A));
            assert_eq!(iter.next(), Some(&AsciiChar::S));
            assert_eq!(iter.next(), Some(&AsciiChar::D));
        }
    }

    #[test]
    fn test_as_cstr() {
        {
            let msg = "I am your favourite cookie monster >:-)\0";
            let pstr = PascalString::from(&msg).unwrap();
            let cstr = CStr::from_bytes_with_nul(msg.as_bytes()).unwrap();
            let pstr_as_cstr = pstr.as_cstr().unwrap();
            assert!(match pstr_as_cstr {
                Cow::Borrowed(_) => true,
                _ => false
            });
            assert_eq!(&*pstr_as_cstr, cstr);
        }

        {
            let oversized = ['l'; 255];
            let string_oversized = {
                let mut s = String::new();
                for i in 0..oversized.len() {
                    s.push(oversized[i]);
                }
                s
            };
            let pstr_oversized = PascalString::from_fixed_ascii_array(255, oversized).unwrap();
            let cstr_from_pstr_oversized = pstr_oversized.as_cstr().unwrap();
            let cstr_from_string_oversized = CString::new(string_oversized).unwrap();
            assert!(match cstr_from_pstr_oversized {
                Cow::Owned(_) => true,
                _ => false
            });
            assert_eq!(cstr_from_pstr_oversized.into_owned(), cstr_from_string_oversized);
        }

        {
            let has_interior_null = "lol\0hi";
            let pstr = PascalString::from(&has_interior_null).unwrap();
            let err = match pstr.as_cstr() {
                Err(err) => err,
                _ => panic!("incorrect result")
            };
            assert_eq!(err.interior_null_index(), 3);
        }
    }
}
<|endoftext|>


<commit_before>//! Traits that map BLAS accelerated types (e.g. `f32`) to their respective BLAS calls (e.g.
//! `saxpy`)
//!
//! This library provides no safe wrappers around the foreign BLAS functions.
//!
//! If you are looking for a BLAS accelerated linear algebra, check out [linalg].
//!
//! [linalg]: https://github.com/japaric/linalg.rs

#![deny(missing_docs)]
#![deny(warnings)]
#![feature(libc)]

extern crate complex;
extern crate libc;

mod ffi;

pub mod axpy;
pub mod copy;
pub mod dot;
pub mod gemm;
pub mod gemv;
pub mod nrm2;
pub mod scal;

/// Transpose matrix before operation?
#[derive(Clone, Copy)]
#[repr(i8)]
pub enum Transpose {
    /// Don't transpose
    No = 110, // 'n'
    /// Transpose
    Yes = 116,  // 't'
}

/// Types with `axpy` acceleration
pub trait Axpy {
    /// Returns the foreign `axpy` function
    fn axpy() -> axpy::Fn<Self>;
}

/// Types with `copy` acceleration
pub trait Copy {
    /// Returns the foreign `copy` function
    fn copy() -> copy::Fn<Self>;
}

/// Types with `dot` acceleration
pub trait Dot {
    /// Returns the foreign `dot` function
    fn dot() -> dot::Fn<Self>;
}

/// Types with `gemm` acceleration
pub trait Gemm {
    /// Returns the foreign `gemm` function
    fn gemm() -> gemm::Fn<Self>;
}

/// Types with `gemv` acceleration
pub trait Gemv {
    /// Returns the foreign `gemv` function
    fn gemv() -> gemv::Fn<Self>;
}

/// Types with `nrm2` acceleration
pub trait Nrm2 {
    /// The type of `nrm2`'s return value
    type Output;

    /// Returns the foreign `nrm2` function
    fn nrm2() -> nrm2::Fn<Self, Self::Output>;
}

/// Types with `scal` acceleration
pub trait Scal<Alpha=Self> {
    /// Returns the foreign `scal` function
    fn scal() -> scal::Fn<Alpha, Self>;
}

/// The integer used by the BLAS library
// TODO Handle 64-bit BLAS
#[allow(non_camel_case_types)]
pub type blasint = ::libc::c_int;
<commit_msg>feat: add Debug implementation to the Transpose enum<commit_after>//! Traits that map BLAS accelerated types (e.g. `f32`) to their respective BLAS calls (e.g.
//! `saxpy`)
//!
//! This library provides no safe wrappers around the foreign BLAS functions.
//!
//! If you are looking for a BLAS accelerated linear algebra, check out [linalg].
//!
//! [linalg]: https://github.com/japaric/linalg.rs

#![deny(missing_docs)]
#![deny(warnings)]
#![feature(libc)]

extern crate complex;
extern crate libc;

mod ffi;

pub mod axpy;
pub mod copy;
pub mod dot;
pub mod gemm;
pub mod gemv;
pub mod nrm2;
pub mod scal;

/// Transpose matrix before operation?
#[derive(Clone, Copy, Debug)]
#[repr(i8)]
pub enum Transpose {
    /// Don't transpose
    No = 110, // 'n'
    /// Transpose
    Yes = 116,  // 't'
}

/// Types with `axpy` acceleration
pub trait Axpy {
    /// Returns the foreign `axpy` function
    fn axpy() -> axpy::Fn<Self>;
}

/// Types with `copy` acceleration
pub trait Copy {
    /// Returns the foreign `copy` function
    fn copy() -> copy::Fn<Self>;
}

/// Types with `dot` acceleration
pub trait Dot {
    /// Returns the foreign `dot` function
    fn dot() -> dot::Fn<Self>;
}

/// Types with `gemm` acceleration
pub trait Gemm {
    /// Returns the foreign `gemm` function
    fn gemm() -> gemm::Fn<Self>;
}

/// Types with `gemv` acceleration
pub trait Gemv {
    /// Returns the foreign `gemv` function
    fn gemv() -> gemv::Fn<Self>;
}

/// Types with `nrm2` acceleration
pub trait Nrm2 {
    /// The type of `nrm2`'s return value
    type Output;

    /// Returns the foreign `nrm2` function
    fn nrm2() -> nrm2::Fn<Self, Self::Output>;
}

/// Types with `scal` acceleration
pub trait Scal<Alpha=Self> {
    /// Returns the foreign `scal` function
    fn scal() -> scal::Fn<Alpha, Self>;
}

/// The integer used by the BLAS library
// TODO Handle 64-bit BLAS
#[allow(non_camel_case_types)]
pub type blasint = ::libc::c_int;
<|endoftext|>


<commit_before>extern crate chrono;
extern crate copy_dir;
#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate log;
extern crate regex;
#[macro_use]
extern crate serde_derive;
extern crate shlex;
extern crate tempdir;

#[cfg(test)]
extern crate toml;

pub mod errors;
mod config;
#[macro_use]
pub mod helpers;

use std::path::Path;
use std::fs::{self, File};
use std::io::Write;
use tempdir::TempDir;
use regex::Regex;
use chrono::Local;

use errors::*;
pub use config::Config;

const RUST_GITHUB_REPO: &'static str = "https://github.com/rust-lang/rust";

/// The main entrypoint of the program.
///
/// This will:
///
/// 1. Update the rust repo to the latest version
/// 2. Tell rustbuild to build the docs by tweaking its `config.toml`
/// 3. Iterate through each crate in `src/` and build its docs with
///    `./x.py doc src/libfoo`
/// 4. Upload the generated documentation to github pages if there were
///    no errors or if `config.error_handling.upload_with_errors` is set.
pub fn run(cfg: Config) -> Result<()> {
    info!("Starting documentation generation");

    if !cfg.stages.skip_git_update {
        update_rust_repo(&cfg.rust_dir).chain_err(|| "Failed to update the rust repo")?;
    }
    setup_rustbuild_config_file(&cfg.rust_dir)
        .chain_err(|| "Couldn't make sure the config.toml is set properly")?;

    let crates = find_internal_crates(&cfg.rust_dir)
        .chain_err(|| "Unable to get a list of the internal crates")?;

    let mut errors = Vec::new();

    info!("Generating documentation");
    for krate in crates {
        if let Err(e) = generate_docs(&krate, &cfg.rust_dir) {
            if cfg.error_handling.fail_fast {
                return Err(e);
            } else {
                warn!("Error generating docs for {}, {}", krate, e);
                errors.push((krate, e));
            }
        }
    }

    if !cfg.stages.skip_upload && (errors.is_empty() || cfg.error_handling.upload_with_errors) {
        upload_docs(&cfg.rust_dir, &cfg.git_repo).chain_err(|| "Uploading docs failed")?;
    }

    if errors.is_empty() {
        info!("Documentation generation completed successfully");
        Ok(())
    } else {
        info!(
            "Documentation generation completed with {} errors",
            errors.len()
        );

        debug!("Crates which errored:");
        for &(ref krate, _) in &errors {
            debug!("  {}", krate);
        }

        Err(ErrorKind::DocGeneration(errors).into())
    }
}

/// Generate the documentation for the specified library in a local checkout
/// of the rust repo.
fn generate_docs(name: &str, root: &Path) -> Result<()> {
    debug!("Generating docs for {}", name);
    cmd!(in root, "./x.py doc --stage 1 -v {}", name)?;

    Ok(())
}

fn upload_docs(root: &Path, git_repo: &str) -> Result<()> {
    // FIXME: should probably not hard-code this...
    let target = "x86_64-unknown-linux-gnu";

    let docs_dir = root.join("build").join(target).join("crate-docs");

    if !docs_dir.exists() {
        bail!(
            "Couldn't find {} ... were any docs even generated?",
            docs_dir.display()
        );
    }

    let temp = TempDir::new("rustc-internal-docs")?;
    cmd!("git clone {} {}", git_repo, temp.path().display())?;
    cmd!(in temp.path(), "git checkout gh-pages")?;

    debug!("Copying generated docs to {}", temp.path().display());
    for entry in docs_dir.read_dir()? {
        if let Ok(entry) = entry {
            helpers::recursive_copy(temp.path(), entry.path())?;
        }
    }

    // Make a page to redirect people to rustc/index.html if it doesn't
    // already exist
    let index = temp.path().join("index.html");
    if !index.exists() {
        let redirect =
            r#"<meta http-equiv="refresh" content="0; URL=rustc/index.html">"#;
        File::create(index)?.write_all(redirect.as_bytes())?;
    }

    debug!("Pushing to GitHub pages");
    cmd!(in temp.path(), "git add .")?;
    cmd!(in temp.path(), r#"git commit -m "update {}""#, Local::now())?;
    cmd!(in temp.path(), "git push origin gh-pages")?;
    debug!("Docs uploaded");

    Ok(())
}

/// Do a `git clone` or `git pull` to make sure the Rust repo is up to date.
fn update_rust_repo(root: &Path) -> Result<()> {
    if !root.exists() {
        info!("Rust directory not found, cloning into {}", root.display());
        cmd!("git clone {} {}", RUST_GITHUB_REPO, root.display())?;
    } else {
        info!("Updating rust checkout");
        cmd!(in root, "git pull origin master --ff-only")?;
    }

    info!("Rust directory is up to date");
    Ok(())
}

/// Copy across the template config file from `src/bootstrap/config.toml.example`
/// if it doesn't already exist, and make sure we tell rustbuild to build *all*
/// docs.
fn setup_rustbuild_config_file(root: &Path) -> Result<()> {
    debug!("Making sure config.toml is set up correctly");

    let config_file = root.join("config.toml");
    let template = root.join("config.toml.example");

    if !config_file.exists() {
        fs::copy(&template, &config_file).chain_err(|| {
            format!("Couldn't copy the template config ({})", template.display())
        })?;
    }

    let mut config_contents = helpers::read_file(&config_file)?;

    let patterns = vec![
        (
            Regex::new("^#?compiler-docs .*$").unwrap(),
            "compiler-docs = true",
        ),
        (Regex::new("^#?docs .*$").unwrap(), "docs = true"),
    ];

    for (pattern, replace) in patterns {
        config_contents = pattern.replace(&config_contents, replace).into_owned();
    }

    debug!("Config file: {}", config_file.display());

    File::create(&config_file)
        .expect("Config file should already exist")
        .write_all(config_contents.as_bytes())
        .chain_err(|| "Couldn't write to config.toml")?;

    Ok(())
}

fn find_internal_crates(root: &Path) -> Result<Vec<String>> {
    debug!("Generating a list of internal crates to document");
    let output = cmd!(in root, "./x.py doc --help --verbose")?;

    let mut crates = Vec::new();

    let crate_pattern = Regex::new(r"\./x\.py doc (src/[^\s]+)").unwrap();

    for line in String::from_utf8_lossy(&output.stdout).lines() {
        if let Some(matches) = crate_pattern.captures(&line) {
            let name = &matches[1];
            crates.push(name.to_string());
        }
    }

    debug!("Found {} crates: {}", crates.len(), crates.join(", "));
    Ok(crates)
}
<commit_msg>Used rsync for copying generated docs to the upload directory<commit_after>extern crate chrono;
extern crate copy_dir;
#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate log;
extern crate regex;
#[macro_use]
extern crate serde_derive;
extern crate shlex;
extern crate tempdir;

#[cfg(test)]
extern crate toml;

pub mod errors;
mod config;
#[macro_use]
pub mod helpers;

use std::path::Path;
use std::fs::{self, File};
use std::io::Write;
use tempdir::TempDir;
use regex::Regex;
use chrono::Local;

use errors::*;
pub use config::Config;

const RUST_GITHUB_REPO: &'static str = "https://github.com/rust-lang/rust";

/// The main entrypoint of the program.
///
/// This will:
///
/// 1. Update the rust repo to the latest version
/// 2. Tell rustbuild to build the docs by tweaking its `config.toml`
/// 3. Iterate through each crate in `src/` and build its docs with
///    `./x.py doc src/libfoo`
/// 4. Upload the generated documentation to github pages if there were
///    no errors or if `config.error_handling.upload_with_errors` is set.
pub fn run(cfg: Config) -> Result<()> {
    info!("Starting documentation generation");

    if !cfg.stages.skip_git_update {
        update_rust_repo(&cfg.rust_dir).chain_err(|| "Failed to update the rust repo")?;
    }
    setup_rustbuild_config_file(&cfg.rust_dir)
        .chain_err(|| "Couldn't make sure the config.toml is set properly")?;

    let crates = find_internal_crates(&cfg.rust_dir)
        .chain_err(|| "Unable to get a list of the internal crates")?;

    let mut errors = Vec::new();

    info!("Generating documentation");
    for krate in crates {
        if let Err(e) = generate_docs(&krate, &cfg.rust_dir) {
            if cfg.error_handling.fail_fast {
                return Err(e);
            } else {
                warn!("Error generating docs for {}, {}", krate, e);
                errors.push((krate, e));
            }
        }
    }

    if !cfg.stages.skip_upload && (errors.is_empty() || cfg.error_handling.upload_with_errors) {
        upload_docs(&cfg.rust_dir, &cfg.git_repo).chain_err(|| "Uploading docs failed")?;
    }

    if errors.is_empty() {
        info!("Documentation generation completed successfully");
        Ok(())
    } else {
        info!(
            "Documentation generation completed with {} errors",
            errors.len()
        );

        debug!("Crates which errored:");
        for &(ref krate, _) in &errors {
            debug!("  {}", krate);
        }

        Err(ErrorKind::DocGeneration(errors).into())
    }
}

/// Generate the documentation for the specified library in a local checkout
/// of the rust repo.
fn generate_docs(name: &str, root: &Path) -> Result<()> {
    debug!("Generating docs for {}", name);
    cmd!(in root, "./x.py doc --stage 1 -v {}", name)?;

    Ok(())
}

fn upload_docs(root: &Path, git_repo: &str) -> Result<()> {
    // FIXME: should probably not hard-code this...
    let target = "x86_64-unknown-linux-gnu";

    let docs_dir = root.join("build").join(target).join("crate-docs");

    if !docs_dir.exists() {
        bail!(
            "Couldn't find {} ... were any docs even generated?",
            docs_dir.display()
        );
    }

    let temp = TempDir::new("rustc-internal-docs")?;
    cmd!("git clone {} {}", git_repo, temp.path().display())?;
    cmd!(in temp.path(), "git checkout gh-pages")?;

    debug!("Copying generated docs to {}", temp.path().display());
    cmd!(
        "rsync -a {}/ {}/",
        docs_dir.display(),
        temp.path().display()
    )?;

    // Make a page to redirect people to rustc/index.html if it doesn't
    // already exist
    let index = temp.path().join("index.html");
    if !index.exists() {
        let redirect =
            r#"<meta http-equiv="refresh" content="0; URL=rustc/index.html">"#;
        File::create(index)?.write_all(redirect.as_bytes())?;
    }

    debug!("Pushing to GitHub pages");
    cmd!(in temp.path(), "git add .")?;
    cmd!(in temp.path(), r#"git commit -m "updated documentation at {}""#, Local::now())?;
    cmd!(in temp.path(), "git push origin gh-pages")?;
    debug!("Docs uploaded");

    Ok(())
}

/// Do a `git clone` or `git pull` to make sure the Rust repo is up to date.
fn update_rust_repo(root: &Path) -> Result<()> {
    if !root.exists() {
        info!("Rust directory not found, cloning into {}", root.display());
        cmd!("git clone {} {}", RUST_GITHUB_REPO, root.display())?;
    } else {
        info!("Updating rust checkout");
        cmd!(in root, "git pull origin master --ff-only")?;
    }

    info!("Rust directory is up to date");
    Ok(())
}

/// Copy across the template config file from `src/bootstrap/config.toml.example`
/// if it doesn't already exist, and make sure we tell rustbuild to build *all*
/// docs.
fn setup_rustbuild_config_file(root: &Path) -> Result<()> {
    debug!("Making sure config.toml is set up correctly");

    let config_file = root.join("config.toml");
    let template = root.join("config.toml.example");

    if !config_file.exists() {
        fs::copy(&template, &config_file).chain_err(|| {
            format!("Couldn't copy the template config ({})", template.display())
        })?;
    }

    let mut config_contents = helpers::read_file(&config_file)?;

    let patterns = vec![
        (
            Regex::new("^#?compiler-docs .*$").unwrap(),
            "compiler-docs = true",
        ),
        (Regex::new("^#?docs .*$").unwrap(), "docs = true"),
    ];

    for (pattern, replace) in patterns {
        config_contents = pattern.replace(&config_contents, replace).into_owned();
    }

    debug!("Config file: {}", config_file.display());

    File::create(&config_file)
        .expect("Config file should already exist")
        .write_all(config_contents.as_bytes())
        .chain_err(|| "Couldn't write to config.toml")?;

    Ok(())
}

fn find_internal_crates(root: &Path) -> Result<Vec<String>> {
    debug!("Generating a list of internal crates to document");
    let output = cmd!(in root, "./x.py doc --help --verbose")?;

    let mut crates = Vec::new();

    let crate_pattern = Regex::new(r"\./x\.py doc (src/[^\s]+)").unwrap();

    for line in String::from_utf8_lossy(&output.stdout).lines() {
        if let Some(matches) = crate_pattern.captures(&line) {
            let name = &matches[1];
            crates.push(name.to_string());
        }
    }

    debug!("Found {} crates: {}", crates.len(), crates.join(", "));
    Ok(crates)
}
<|endoftext|>