<commit_before>//! TODO Fill in

use cairo::{Format, ImageSurface};
use glib::translate::ToGlibPtr;
use rustwlc::Geometry;
use std::fmt::{self, Display, Formatter};
use std::default::Default;
use std::rc::Rc;
use rlua::{self, Table, Lua, UserData, ToLua, Value, LightUserData};
use super::object::{Object, Objectable};
use super::class::{self, Class};
use super::property::Property;

#[derive(Clone, Debug)]
pub struct DrawableState {
    pub surface: Option<ImageSurface>,
    geo: Geometry,
    // TODO Use this to determine whether we draw this or not
    refreshed: bool,
}

pub struct Drawable<'lua>(Table<'lua>);

impl_objectable!(Drawable, DrawableState);

impl Default for DrawableState {
    fn default() -> Self {
        DrawableState {
            surface: None,
            geo: Geometry::zero(),
            refreshed: true
        }
    }
}

impl <'lua> Drawable<'lua> {
    pub fn new(lua: &Lua) -> rlua::Result<Object> {
        let class = class::class_setup(lua, "drawable")?;
        Ok(Drawable::allocate(lua, class)?.build())
    }

    pub fn get_geometry(&self) -> rlua::Result<Geometry> {
        let drawable = self.state()?;
        Ok(drawable.geo)
    }

    pub fn get_surface(&self) -> rlua::Result<Value<'lua>> {
        let drawable = self.state()?;
        Ok(match drawable.surface {
            None => Value::Nil,
            Some(ref surface) => {
                let stash = surface.to_glib_none();
                let ptr = stash.0 as _;
                // So that it lives _forever_ heheheh.
                ::std::mem::forget(stash);
                Value::LightUserData(LightUserData(ptr))
            }
        })
    }

    /// Sets the geometry, and allocates a new surface.
    pub fn set_geometry(&mut self, geometry: Geometry) -> rlua::Result<()> {
        use rlua::Error::RuntimeError;
        let mut drawable = self.state()?;
        let size_changed = drawable.geo != geometry;
        drawable.geo = geometry;
        if size_changed {
            drawable.surface = None;
            let size = geometry.size;
            if size.w > 0 && size.h > 0 {
                drawable.surface = Some(ImageSurface::create(Format::ARgb32,
                                                        size.w as i32,
                                                        size.h as i32)
                    .map_err(|err| RuntimeError(format!("Could not allocate {:?}", err)))?);
                // TODO emity property::surface
            }
        }
        self.set_state(drawable)
    }
}

impl Display for DrawableState {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Drawable: {:p}", self)
    }
}

impl <'lua> ToLua<'lua> for Drawable<'lua> {
    fn to_lua(self, lua: &'lua Lua) -> rlua::Result<Value<'lua>> {
        self.0.to_lua(lua)
    }
}

impl UserData for DrawableState {}

pub fn init(lua: &Lua) -> rlua::Result<Class> {
    Class::builder(lua, Some(Rc::new(Drawable::new)), None, None)?
        .method("geometry".into(), lua.create_function(geometry))?
        .property(Property::new("surface".into(),
                                None,
                                Some(lua.create_function(get_surface)),
                                None))?
        .save_class("drawable")?
        .build()
}

fn get_surface<'lua>(_: &'lua Lua, table: Table<'lua>) -> rlua::Result<Value<'lua>> {
    let drawable = Drawable::cast(table.clone().into())?;
    drawable.get_surface()
}

fn geometry<'lua>(lua: &'lua Lua, table: Table<'lua>) -> rlua::Result<Table<'lua>> {
    use rustwlc::{Point, Size};
    let drawable = Drawable::cast(table.into())?;
    let geometry = drawable.get_geometry()?;
    let Point { x, y } = geometry.origin;
    let Size { w, h } = geometry.size;
    let table = lua.create_table();
    table.set("x", x)?;
    table.set("y", y)?;
    table.set("width", w)?;
    table.set("height", h)?;
    Ok(table)
}
<commit_msg>Add a geometry method to drawables<commit_after>//! TODO Fill in

use cairo::{Format, ImageSurface};
use glib::translate::ToGlibPtr;
use rustwlc::Geometry;
use std::fmt::{self, Display, Formatter};
use std::default::Default;
use std::rc::Rc;
use rlua::{self, Table, Lua, UserData, ToLua, Value, LightUserData};
use super::object::{Object, Objectable};
use super::class::{self, Class};
use super::property::Property;

#[derive(Clone, Debug)]
pub struct DrawableState {
    pub surface: Option<ImageSurface>,
    geo: Geometry,
    // TODO Use this to determine whether we draw this or not
    refreshed: bool,
}

pub struct Drawable<'lua>(Table<'lua>);

impl_objectable!(Drawable, DrawableState);

impl Default for DrawableState {
    fn default() -> Self {
        DrawableState {
            surface: None,
            geo: Geometry::zero(),
            refreshed: true
        }
    }
}

impl <'lua> Drawable<'lua> {
    pub fn new(lua: &Lua) -> rlua::Result<Object> {
        let class = class::class_setup(lua, "drawable")?;
        let builder = Drawable::allocate(lua, class)?;
        // TODO Do properly
        let table = lua.create_table();
        table.set("geometry", lua.create_function(geometry))?;
        Ok(builder.add_to_meta(table)?.build())
    }

    pub fn get_geometry(&self) -> rlua::Result<Geometry> {
        let drawable = self.state()?;
        Ok(drawable.geo)
    }

    pub fn get_surface(&self) -> rlua::Result<Value<'lua>> {
        let drawable = self.state()?;
        Ok(match drawable.surface {
            None => Value::Nil,
            Some(ref surface) => {
                let stash = surface.to_glib_none();
                let ptr = stash.0 as _;
                // So that it lives _forever_ heheheh.
                ::std::mem::forget(stash);
                Value::LightUserData(LightUserData(ptr))
            }
        })
    }

    /// Sets the geometry, and allocates a new surface.
    pub fn set_geometry(&mut self, geometry: Geometry) -> rlua::Result<()> {
        use rlua::Error::RuntimeError;
        let mut drawable = self.state()?;
        let size_changed = drawable.geo != geometry;
        drawable.geo = geometry;
        if size_changed {
            drawable.surface = None;
            let size = geometry.size;
            if size.w > 0 && size.h > 0 {
                drawable.surface = Some(ImageSurface::create(Format::ARgb32,
                                                        size.w as i32,
                                                        size.h as i32)
                    .map_err(|err| RuntimeError(format!("Could not allocate {:?}", err)))?);
                // TODO emity property::surface
            }
        }
        self.set_state(drawable)
    }
}

impl Display for DrawableState {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Drawable: {:p}", self)
    }
}

impl <'lua> ToLua<'lua> for Drawable<'lua> {
    fn to_lua(self, lua: &'lua Lua) -> rlua::Result<Value<'lua>> {
        self.0.to_lua(lua)
    }
}

impl UserData for DrawableState {}

pub fn init(lua: &Lua) -> rlua::Result<Class> {
    Class::builder(lua, Some(Rc::new(Drawable::new)), None, None)?
        .method("geometry".into(), lua.create_function(geometry))?
        .property(Property::new("surface".into(),
                                None,
                                Some(lua.create_function(get_surface)),
                                None))?
        .save_class("drawable")?
        .build()
}

fn get_surface<'lua>(_: &'lua Lua, table: Table<'lua>) -> rlua::Result<Value<'lua>> {
    let drawable = Drawable::cast(table.clone().into())?;
    drawable.get_surface()
}

fn geometry<'lua>(lua: &'lua Lua, table: Table<'lua>) -> rlua::Result<Table<'lua>> {
    use rustwlc::{Point, Size};
    let drawable = Drawable::cast(table.into())?;
    let geometry = drawable.get_geometry()?;
    let Point { x, y } = geometry.origin;
    let Size { w, h } = geometry.size;
    let table = lua.create_table();
    table.set("x", x)?;
    table.set("y", y)?;
    table.set("width", w)?;
    table.set("height", h)?;
    Ok(table)
}
<|endoftext|>


<commit_before>//! `cargo upgrade`
#![warn(missing_docs, missing_debug_implementations, missing_copy_implementations, trivial_casts,
       trivial_numeric_casts, unsafe_code, unstable_features, unused_import_braces,
       unused_qualifications)]

extern crate cargo_metadata;
extern crate docopt;
#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate serde_derive;
extern crate toml;

use std::collections::HashMap;
use std::io::{self, Write};
use std::path::Path;
use std::process;

extern crate cargo_edit;
use cargo_edit::{get_latest_dependency, Dependency, Manifest};

mod errors {
    error_chain!{
        links {
            CargoEditLib(::cargo_edit::Error, ::cargo_edit::ErrorKind);
        }

        foreign_links {
            // cargo-metadata doesn't (yet) export `ErrorKind`)
            Metadata(::cargo_metadata::Error);
        }
    }
}
use errors::*;

static USAGE: &'static str = r"
Upgrade all dependencies in a manifest file to the latest version.

Usage:
    cargo upgrade [--all] [--dependency <dep>...] [--manifest-path <path>] [options]
    cargo upgrade (-h | --help)
    cargo upgrade (-V | --version)

Options:
    --all                       Upgrade all packages in the workspace.
    -d --dependency <dep>       Specific dependency to upgrade. If this option is used, only the
                                specified dependencies will be upgraded.
    --manifest-path <path>      Path to the manifest to upgrade.
    --allow-prerelease          Include prerelease versions when fetching from crates.io (e.g.
                                '0.6.0-alpha'). Defaults to false.
    -h --help                   Show this help page.
    -V --version                Show version.

Dev, build, and all target dependencies will also be upgraded. Only dependencies from crates.io are
supported. Git/path dependencies will be ignored.

All packages in the workspace will be upgraded if the `--all` flag is supplied. The `--all` flag may
be supplied in the presence of a virtual manifest.
";

/// Docopts input args.
#[derive(Debug, Deserialize)]
struct Args {
    /// `--dependency -d <dep>`
    flag_dependency: Vec<String>,
    /// `--manifest-path <path>`
    flag_manifest_path: Option<String>,
    /// `--version`
    flag_version: bool,
    /// `--all`
    flag_all: bool,
    /// `--allow-prerelease`
    flag_allow_prerelease: bool,
}

fn is_version_dependency(dep: &toml::Value) -> bool {
    if let Some(table) = dep.as_table() {
        !table.contains_key("git") && !table.contains_key("path")
    } else {
        true
    }
}

fn update_manifest(
    manifest_path: &Option<String>,
    only_update: &[String],
    allow_prerelease: bool,
) -> Result<()> {
    let manifest_path = manifest_path.as_ref().map(From::from);
    let mut manifest = Manifest::open(&manifest_path)?;

    for (table_path, table) in manifest.get_sections() {
        for (name, old_value) in &table {
            if (only_update.is_empty() || only_update.contains(name)) &&
                is_version_dependency(old_value)
            {
                let latest_version = get_latest_dependency(name, allow_prerelease)?;

                manifest.update_table_entry(&table_path, &latest_version)?;
            }
        }
    }

    let mut file = Manifest::find_file(&manifest_path)?;
    manifest.write_to_file(&mut file)?;

    Ok(())
}

fn update_manifest_from_cache(
    manifest_path: &Option<String>,
    only_update: &[String],
    _allow_prerelease: bool,
    new_deps: &HashMap<String, Dependency>,
) -> Result<()> {
    let manifest_path = manifest_path.as_ref().map(From::from);
    let mut manifest = Manifest::open(&manifest_path)?;

    for (table_path, table) in manifest.get_sections() {
        for (name, old_value) in &table {
            if (only_update.is_empty() || only_update.contains(name)) &&
                is_version_dependency(old_value)
            {
                let latest_version = &new_deps[name];

                manifest.update_table_entry(&table_path, latest_version)?;
            }
        }
    }

    let mut file = Manifest::find_file(&manifest_path)?;
    manifest
        .write_to_file(&mut file)
        .chain_err(|| "Failed to write new manifest contents")
}

/// Get a list of the paths of all the (non-virtual) manifests in the workspace.
fn get_workspace_manifests(manifest_path: &Option<String>) -> Result<Vec<String>> {
    Ok(
        cargo_metadata::metadata_deps(manifest_path.as_ref().map(|p| Path::new(p)), true)
            .chain_err(|| "Failed to get metadata")?
            .packages
            .iter()
            .map(|p| p.manifest_path.clone())
            .collect(),
    )
}

/// Look up all current direct crates.io dependencies in the workspace. Then get the latest version
/// for each.
// cargo-metadata: would be nice to have dependency version and type. Also, manifest path more
// accepting
fn get_all_new_deps(manifest_path: &Option<String>) -> Result<HashMap<String, Dependency>> {
    let mut new_deps = HashMap::new();

    cargo_metadata::metadata_deps(manifest_path.as_ref().map(|p| Path::new(p)), true)
        .chain_err(|| "Failed to get metadata")?
        .packages
        .iter()
        .flat_map(|package| package.dependencies.to_owned())
        .map(|dependency| {
            if !new_deps.contains_key(&dependency.name) {
                new_deps.insert(
                    dependency.name.clone(),
                    get_latest_dependency(&dependency.name, false)?,
                );
            }
            Ok(())
        })
        .collect::<Result<Vec<()>>>()?;

    Ok(new_deps)
}

fn update_workspace_manifests(
    manifest_path: &Option<String>,
    only_update: &[String],
    allow_prerelease: bool,
) -> Result<()> {
    let new_deps = get_all_new_deps(manifest_path)?;

    get_workspace_manifests(manifest_path).and_then(|manifests| {
        for manifest in manifests {
            update_manifest_from_cache(&Some(manifest), only_update, allow_prerelease, &new_deps)?
        }

        Ok(())
    })
}

fn main() {
    let args = docopt::Docopt::new(USAGE)
        .and_then(|d| d.deserialize::<Args>())
        .unwrap_or_else(|err| err.exit());

    if args.flag_version {
        println!("cargo-upgrade version {}", env!("CARGO_PKG_VERSION"));
        process::exit(0);
    }

    let output = if args.flag_all {
        update_workspace_manifests(
            &args.flag_manifest_path,
            &args.flag_dependency,
            args.flag_allow_prerelease,
        )
    } else {
        update_manifest(
            &args.flag_manifest_path,
            &args.flag_dependency,
            args.flag_allow_prerelease,
        )
    };

    if let Err(err) = output {
        let mut stderr = io::stderr();

        writeln!(stderr, "Command failed due to unhandled error: {}\n", err).unwrap();

        for e in err.iter().skip(1) {
            writeln!(stderr, "Caused by: {}", e).unwrap();
        }

        if let Some(backtrace) = err.backtrace() {
            writeln!(stderr, "Backtrace: {:?}", backtrace).unwrap();
        }

        process::exit(1);
    }
}
<commit_msg>Allow pre-release correctly<commit_after>//! `cargo upgrade`
#![warn(missing_docs, missing_debug_implementations, missing_copy_implementations, trivial_casts,
       trivial_numeric_casts, unsafe_code, unstable_features, unused_import_braces,
       unused_qualifications)]

extern crate cargo_metadata;
extern crate docopt;
#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate serde_derive;
extern crate toml;

use std::collections::HashMap;
use std::io::{self, Write};
use std::path::Path;
use std::process;

extern crate cargo_edit;
use cargo_edit::{get_latest_dependency, Dependency, Manifest};

mod errors {
    error_chain!{
        links {
            CargoEditLib(::cargo_edit::Error, ::cargo_edit::ErrorKind);
        }

        foreign_links {
            // cargo-metadata doesn't (yet) export `ErrorKind`)
            Metadata(::cargo_metadata::Error);
        }
    }
}
use errors::*;

static USAGE: &'static str = r"
Upgrade all dependencies in a manifest file to the latest version.

Usage:
    cargo upgrade [--all] [--dependency <dep>...] [--manifest-path <path>] [options]
    cargo upgrade (-h | --help)
    cargo upgrade (-V | --version)

Options:
    --all                       Upgrade all packages in the workspace.
    -d --dependency <dep>       Specific dependency to upgrade. If this option is used, only the
                                specified dependencies will be upgraded.
    --manifest-path <path>      Path to the manifest to upgrade.
    --allow-prerelease          Include prerelease versions when fetching from crates.io (e.g.
                                '0.6.0-alpha'). Defaults to false.
    -h --help                   Show this help page.
    -V --version                Show version.

Dev, build, and all target dependencies will also be upgraded. Only dependencies from crates.io are
supported. Git/path dependencies will be ignored.

All packages in the workspace will be upgraded if the `--all` flag is supplied. The `--all` flag may
be supplied in the presence of a virtual manifest.
";

/// Docopts input args.
#[derive(Debug, Deserialize)]
struct Args {
    /// `--dependency -d <dep>`
    flag_dependency: Vec<String>,
    /// `--manifest-path <path>`
    flag_manifest_path: Option<String>,
    /// `--version`
    flag_version: bool,
    /// `--all`
    flag_all: bool,
    /// `--allow-prerelease`
    flag_allow_prerelease: bool,
}

fn is_version_dependency(dep: &toml::Value) -> bool {
    if let Some(table) = dep.as_table() {
        !table.contains_key("git") && !table.contains_key("path")
    } else {
        true
    }
}

fn update_manifest(
    manifest_path: &Option<String>,
    only_update: &[String],
    allow_prerelease: bool,
) -> Result<()> {
    let manifest_path = manifest_path.as_ref().map(From::from);
    let mut manifest = Manifest::open(&manifest_path)?;

    for (table_path, table) in manifest.get_sections() {
        for (name, old_value) in &table {
            if (only_update.is_empty() || only_update.contains(name)) &&
                is_version_dependency(old_value)
            {
                let latest_version = get_latest_dependency(name, allow_prerelease)?;

                manifest.update_table_entry(&table_path, &latest_version)?;
            }
        }
    }

    let mut file = Manifest::find_file(&manifest_path)?;
    manifest.write_to_file(&mut file)?;

    Ok(())
}

fn update_manifest_from_cache(
    manifest_path: &Option<String>,
    only_update: &[String],
    new_deps: &HashMap<String, Dependency>,
) -> Result<()> {
    let manifest_path = manifest_path.as_ref().map(From::from);
    let mut manifest = Manifest::open(&manifest_path)?;

    for (table_path, table) in manifest.get_sections() {
        for (name, old_value) in &table {
            if (only_update.is_empty() || only_update.contains(name)) &&
                is_version_dependency(old_value)
            {
                let latest_version = &new_deps[name];

                manifest.update_table_entry(&table_path, latest_version)?;
            }
        }
    }

    let mut file = Manifest::find_file(&manifest_path)?;
    manifest
        .write_to_file(&mut file)
        .chain_err(|| "Failed to write new manifest contents")
}

/// Get a list of the paths of all the (non-virtual) manifests in the workspace.
fn get_workspace_manifests(manifest_path: &Option<String>) -> Result<Vec<String>> {
    Ok(
        cargo_metadata::metadata_deps(manifest_path.as_ref().map(|p| Path::new(p)), true)
            .chain_err(|| "Failed to get metadata")?
            .packages
            .iter()
            .map(|p| p.manifest_path.clone())
            .collect(),
    )
}

/// Look up all current direct crates.io dependencies in the workspace. Then get the latest version
/// for each.
fn get_all_new_deps(
    manifest_path: &Option<String>,
    allow_prerelease: bool,
) -> Result<HashMap<String, Dependency>> {
    let mut new_deps = HashMap::new();

    cargo_metadata::metadata_deps(manifest_path.as_ref().map(|p| Path::new(p)), true)
        .chain_err(|| "Failed to get metadata")?
        .packages
        .iter()
        .flat_map(|package| package.dependencies.to_owned())
        .map(|dependency| {
            if !new_deps.contains_key(&dependency.name) {
                new_deps.insert(
                    dependency.name.clone(),
                    get_latest_dependency(&dependency.name, allow_prerelease)?,
                );
            }
            Ok(())
        })
        .collect::<Result<Vec<()>>>()?;

    Ok(new_deps)
}

fn update_workspace_manifests(
    manifest_path: &Option<String>,
    only_update: &[String],
    allow_prerelease: bool,
) -> Result<()> {
    let new_deps = get_all_new_deps(manifest_path, allow_prerelease)?;

    get_workspace_manifests(manifest_path).and_then(|manifests| {
        for manifest in manifests {
            update_manifest_from_cache(&Some(manifest), only_update, &new_deps)?
        }

        Ok(())
    })
}

fn main() {
    let args = docopt::Docopt::new(USAGE)
        .and_then(|d| d.deserialize::<Args>())
        .unwrap_or_else(|err| err.exit());

    if args.flag_version {
        println!("cargo-upgrade version {}", env!("CARGO_PKG_VERSION"));
        process::exit(0);
    }

    let output = if args.flag_all {
        update_workspace_manifests(
            &args.flag_manifest_path,
            &args.flag_dependency,
            args.flag_allow_prerelease,
        )
    } else {
        update_manifest(
            &args.flag_manifest_path,
            &args.flag_dependency,
            args.flag_allow_prerelease,
        )
    };

    if let Err(err) = output {
        let mut stderr = io::stderr();

        writeln!(stderr, "Command failed due to unhandled error: {}\n", err).unwrap();

        for e in err.iter().skip(1) {
            writeln!(stderr, "Caused by: {}", e).unwrap();
        }

        if let Some(backtrace) = err.backtrace() {
            writeln!(stderr, "Backtrace: {:?}", backtrace).unwrap();
        }

        process::exit(1);
    }
}
<|endoftext|>


<commit_before>pub mod bitbucket;
pub mod gist;
pub mod hastebin;
pub mod pastebin;
pub mod pastie;
pub mod sprunge;

use bins::error::*;
use bins::network::download::Downloader;
use bins::network::upload::Uploader;
use bins::network;
use bins::{self, Bins, PasteFile};
use hyper::Url;
use linked_hash_map::LinkedHashMap;
use std::collections::HashMap;
use std::iter::repeat;

pub struct Index {
  pub files: LinkedHashMap<String, Url>
}

impl Index {
  fn repeat_str(string: &str, count: usize) -> String {
    repeat(string).take(count).collect()
  }

  pub fn to_string(&self) -> String {
    let header = format!("{} files", self.files.len());
    let separator = Self::repeat_str("-", header.len());
    let mut body = String::from("");
    for (i, (name, url)) in self.files.iter().enumerate() {
      let number = i + 1;
      body.push_str(&format!("{number}. {name}: {url}\n",
                             number = number,
                             name = name,
                             url = url));
    }
    format!("{}\n{}\n\n{}", header, separator, body)
  }

  pub fn parse<S: Into<String>>(string: S) -> Result<Index> {
    let string = string.into();
    let lines: Vec<&str> = string.split('\n').collect();
    if lines.len() < 4 {
      return Err(ErrorKind::InvalidIndexError.into());
    }
    let mut split = lines.iter().skip(3).filter(|s| !s.trim().is_empty()).map(|s| s.split(": ")).collect::<Vec<_>>();
    let names: Vec<String> = some_or_err!(split.iter_mut()
                                            .map(|s| {
                                              s.nth(0)
                                                .map(|n| n.split(' ').skip(1).collect::<Vec<&str>>().join(" "))
                                            })
                                            .collect(),
                                          ErrorKind::InvalidIndexError.into());
    let url_strings: Vec<String> = some_or_err!(split.iter_mut().map(|s| s.nth(0).map(|s| s.to_owned())).collect(),
                                                ErrorKind::InvalidIndexError.into());
    let urls: Vec<Url> = try!(url_strings.into_iter().map(network::parse_url).collect());
    let urls: LinkedHashMap<String, Url> = names.into_iter().zip(urls.into_iter()).collect();
    Ok(Index { files: urls })
  }
}

pub struct RemotePasteFile {
  pub name: String,
  pub url: Url,
  pub contents: Option<String>
}

/// Produce information about HTML content from URLs to HTML content.
pub trait ProduceInfo {
  fn produce_info(&self, bins: &Bins, url: &Url) -> Result<Vec<RemotePasteFile>>;

  fn produce_info_all(&self, bins: &Bins, urls: Vec<&Url>) -> Result<Vec<RemotePasteFile>> {
    let info: Vec<Vec<RemotePasteFile>> = try!(urls.iter().map(|u| self.produce_info(bins, u)).collect());
    Ok(info.into_iter().flat_map(|v| v).collect())
  }
}

impl<T> ProduceInfo for T
  where T: GenerateIndex + ConvertUrlsToRawUrls + Downloader
{
  fn produce_info(&self, bins: &Bins, url: &Url) -> Result<Vec<RemotePasteFile>> {
    let raw_url = try!(self.convert_url_to_raw_url(bins, url));
    let mut res = try!(self.download(&bins, &raw_url));
    let content = try!(network::read_response(&mut res));
    let index = Index::parse(content.clone());
    let mut urls: Vec<RemotePasteFile> = Vec::new();
    match index {
      Ok(ref i) => {
        for (name, url) in i.files.clone().into_iter() {
          urls.push(RemotePasteFile {
            name: name.clone(),
            url: url.clone(),
            contents: None
          });
        }
      }
      Err(ref e) => {
        if let ErrorKind::InvalidIndexError = *e.kind() {} else {
          return Err(e.to_string().into());
        }
        let url = url.clone();
        let name = some_or_err!(url.path_segments().and_then(|s| s.last()),
                                "paste url was a root url".into());
        urls.push(RemotePasteFile {
          name: name.to_owned(),
          url: url.clone(),
          contents: Some(content)
        });
      }
    }
    Ok(urls)
  }
}

/// Produce information about raw content from URLs to HTML content.
pub trait ProduceRawInfo {
  fn produce_raw_info(&self, bins: &Bins, url: &Url) -> Result<Vec<RemotePasteFile>>;

  fn produce_raw_info_all(&self, bins: &Bins, urls: Vec<&Url>) -> Result<Vec<RemotePasteFile>> {
    let info: Vec<Vec<RemotePasteFile>> = try!(urls.iter().map(|u| self.produce_raw_info(bins, u)).collect());
    Ok(info.into_iter().flat_map(|v| v).collect())
  }
}

impl<T> ProduceRawInfo for T
  where T: ConvertUrlsToRawUrls + Downloader + UsesIndices + ProduceInfo
{
  fn produce_raw_info(&self, bins: &Bins, url: &Url) -> Result<Vec<RemotePasteFile>> {
    let info = try!(self.produce_info(bins, url));
    info.into_iter()
      .map(|r| {
        let raw_url = try!(self.convert_url_to_raw_url(bins, &r.url));
        Ok(RemotePasteFile { url: raw_url, ..r })
      })
      .collect()
  }
}

pub trait ConvertUrlsToRawUrls {
  fn convert_url_to_raw_url(&self, bins: &Bins, url: &Url) -> Result<Url>;

  fn convert_urls_to_raw_urls(&self, bins: &Bins, urls: Vec<&Url>) -> Result<Vec<Url>> {
    urls.iter().map(|u| self.convert_url_to_raw_url(bins, u)).collect()
  }
}

/// Produce raw content from a URL to HTML content.
pub trait ProduceRawContent: ProduceRawInfo + ProduceInfo + Downloader {
  fn produce_raw_contents(&self, bins: &Bins, url: &Url) -> Result<String> {
    let raw_info = if bins.arguments.urls {
      try!(self.produce_info(bins, url))
    } else {
      try!(self.produce_raw_info(bins, url))
    };
    let raw_info: Vec<RemotePasteFile> = if raw_info.len() > 1 {
      if !bins.arguments.files.is_empty() {
        let mut map: HashMap<String, RemotePasteFile> =
          raw_info.into_iter().map(|r| (r.name.to_lowercase(), r)).collect();
        try!(bins.arguments
          .files
          .iter()
          .map(|s| map.remove(&s.to_lowercase()).ok_or(format!("file {} not found", s)))
          .collect())
      } else if let Some(ref range) = bins.arguments.range {
        let mut numbered_info: HashMap<usize, RemotePasteFile> = raw_info.into_iter()
          .enumerate()
          .collect();
        try!(range.clone()
          .into_iter()
          .map(|n| numbered_info.remove(&n).ok_or(format!("file {} not found", n)))
          .collect())
      } else if bins.arguments.all {
        raw_info
      } else {
        let names = raw_info.into_iter().map(|r| String::from("  ") + &r.name).collect::<Vec<_>>().join("\n");
        return Err(format!("paste had multiple files, but no behavior was specified on the command \
                            line\n\navailable files:\n{}",
                           names)
          .into());
      }
    } else {
      raw_info
    };
    if bins.arguments.raw_urls || bins.arguments.urls {
      return Ok(raw_info.into_iter().map(|r| r.url.as_str().to_owned()).collect::<Vec<_>>().join("\n"));
    }
    let names: Vec<String> = raw_info.iter().map(|p| p.name.clone()).collect();
    let all_contents: Vec<String> = try!(raw_info.iter()
      .map(|p| {
        match p.contents.clone() {
          Some(contents) => Ok(contents),
          None => self.download(&bins, &p.url).and_then(|mut r| network::read_response(&mut r)),
        }
      })
      .collect());
    let files: LinkedHashMap<String, String> = names.into_iter().zip(all_contents.into_iter()).collect();
    Ok(files.into_iter()
      .map(|(name, content)| {
        PasteFile {
          name: name.clone(),
          data: if bins.arguments.number_lines { number_lines(content.clone()) } else { content.clone() }
        }
      })
      .collect::<Vec<PasteFile>>()
      .join())
  }
}

fn number_lines(string: String) -> String {
  let lines: Vec<&str> = string.split('\n').collect();
  let num_lines = lines.len();
  let zeroes: String = repeat(" ").take(num_lines.to_string().len()).collect();
  lines.into_iter().enumerate().map(|(i, l)| {
    let i = i + 1;
    format!("{}{}  {}", &zeroes[0..zeroes.len() - i.to_string().len()], i, l)
  }).collect::<Vec<_>>().join("\n")
}

/// Produce a URL to HTML content from raw content.
pub trait UploadContent: Uploader {
  fn upload_paste(&self, bins: &Bins, content: PasteFile) -> Result<Url>;
}

impl<T> UploadContent for T
  where T: UploadUrl + Uploader
{
  fn upload_paste(&self, bins: &Bins, content: PasteFile) -> Result<Url> {
    let url = try!(network::parse_url(self.get_upload_url()));
    let mut response = try!(self.upload(&url, bins, &content));
    network::parse_url(try!(network::read_response(&mut response)))
  }
}

/// Produce a URL to HTML content from a batch of raw content.
pub trait UploadBatchContent: UploadContent {
  fn upload_all(&self, bins: &Bins, content: Vec<PasteFile>) -> Result<Url>;
}

impl<T> UploadBatchContent for T
  where T: GenerateIndex + UploadContent
{
  fn upload_all(&self, bins: &Bins, content: Vec<PasteFile>) -> Result<Url> {
    let index = try!(self.generate_index(bins, content));
    self.upload_paste(bins,
                      PasteFile {
                        name: "index.md".to_owned(),
                        data: index.to_string()
                      })
  }
}

pub trait UsesIndices {}

/// Generate an index for multiple files.
pub trait GenerateIndex {
  fn generate_index(&self, bins: &Bins, content: Vec<PasteFile>) -> Result<Index>;
}

impl<T> GenerateIndex for T
  where T: UploadContent + UsesIndices
{
  fn generate_index(&self, bins: &Bins, content: Vec<PasteFile>) -> Result<Index> {
    let names: Vec<String> = (&content).into_iter().map(|p| p.name.clone()).collect();
    let urls: Vec<Url> = try!(content.into_iter().map(|p| self.upload_paste(bins, p)).collect());
    let uploads: LinkedHashMap<String, Url> = names.into_iter().zip(urls.into_iter()).collect();
    Ok(Index { files: uploads })
  }
}

pub trait UploadUrl {
  fn get_upload_url(&self) -> &str;
}

pub trait VerifyUrl {
  fn segments(&self, url: &Url) -> Vec<String> {
    let segments = match url.path_segments() {
      Some(s) => s,
      None => return Vec::new(),
    };
    segments.filter(|s| !s.is_empty()).map(|s| s.to_owned()).collect::<Vec<_>>()
  }

  fn verify_url(&self, url: &Url) -> bool;
}

/// A bin, which can upload content in raw form and download content in raw and HTML form.
pub trait Bin: Sync + ProduceInfo + ProduceRawContent + UploadBatchContent + VerifyUrl {
  fn get_name(&self) -> &str;

  fn get_domain(&self) -> &str;
}

trait Join {
  fn join(&self) -> String;
}

impl Join for Vec<PasteFile> {
  fn join(&self) -> String {
    if self.len() == 1 {
      self.get(0).expect("len() == 1, but no first element").data.clone()
    } else {
      self.into_iter().map(|p| format!("--- {} ---\n\n{}", p.name, p.data)).collect::<Vec<String>>().join("\n\n")
    }
  }
}

lazy_static! {
  pub static ref BINS: Vec<Box<Bin>> = {
      vec![
        Box::new(bins::Gist::new()),
        Box::new(bins::Bitbucket::new()),
        Box::new(bins::Sprunge::new()),
        Box::new(bins::Hastebin::new()),
        Box::new(bins::Pastebin::new()),
        Box::new(bins::Pastie::new())
      ]
  };
}

pub fn get_bin_names<'a>() -> Vec<&'a str> {
  BINS.iter().map(|e| e.get_name()).collect()
}

pub fn get_bin_by_name(name: &str) -> Option<&Box<Bin>> {
  BINS.iter().find(|e| e.get_name().to_lowercase() == name.to_lowercase())
}

pub fn get_bin_by_domain(domain: &str) -> Option<&Box<Bin>> {
  BINS.iter().find(|e| e.get_domain().to_lowercase() == domain.to_lowercase())
}
<commit_msg>style: use rustfmt<commit_after>pub mod bitbucket;
pub mod gist;
pub mod hastebin;
pub mod pastebin;
pub mod pastie;
pub mod sprunge;

use bins::error::*;
use bins::network::download::Downloader;
use bins::network::upload::Uploader;
use bins::network;
use bins::{self, Bins, PasteFile};
use hyper::Url;
use linked_hash_map::LinkedHashMap;
use std::collections::HashMap;
use std::iter::repeat;

pub struct Index {
  pub files: LinkedHashMap<String, Url>
}

impl Index {
  fn repeat_str(string: &str, count: usize) -> String {
    repeat(string).take(count).collect()
  }

  pub fn to_string(&self) -> String {
    let header = format!("{} files", self.files.len());
    let separator = Self::repeat_str("-", header.len());
    let mut body = String::from("");
    for (i, (name, url)) in self.files.iter().enumerate() {
      let number = i + 1;
      body.push_str(&format!("{number}. {name}: {url}\n",
                             number = number,
                             name = name,
                             url = url));
    }
    format!("{}\n{}\n\n{}", header, separator, body)
  }

  pub fn parse<S: Into<String>>(string: S) -> Result<Index> {
    let string = string.into();
    let lines: Vec<&str> = string.split('\n').collect();
    if lines.len() < 4 {
      return Err(ErrorKind::InvalidIndexError.into());
    }
    let mut split = lines.iter().skip(3).filter(|s| !s.trim().is_empty()).map(|s| s.split(": ")).collect::<Vec<_>>();
    let names: Vec<String> = some_or_err!(split.iter_mut()
                                            .map(|s| {
                                              s.nth(0)
                                                .map(|n| n.split(' ').skip(1).collect::<Vec<&str>>().join(" "))
                                            })
                                            .collect(),
                                          ErrorKind::InvalidIndexError.into());
    let url_strings: Vec<String> = some_or_err!(split.iter_mut().map(|s| s.nth(0).map(|s| s.to_owned())).collect(),
                                                ErrorKind::InvalidIndexError.into());
    let urls: Vec<Url> = try!(url_strings.into_iter().map(network::parse_url).collect());
    let urls: LinkedHashMap<String, Url> = names.into_iter().zip(urls.into_iter()).collect();
    Ok(Index { files: urls })
  }
}

pub struct RemotePasteFile {
  pub name: String,
  pub url: Url,
  pub contents: Option<String>
}

/// Produce information about HTML content from URLs to HTML content.
pub trait ProduceInfo {
  fn produce_info(&self, bins: &Bins, url: &Url) -> Result<Vec<RemotePasteFile>>;

  fn produce_info_all(&self, bins: &Bins, urls: Vec<&Url>) -> Result<Vec<RemotePasteFile>> {
    let info: Vec<Vec<RemotePasteFile>> = try!(urls.iter().map(|u| self.produce_info(bins, u)).collect());
    Ok(info.into_iter().flat_map(|v| v).collect())
  }
}

impl<T> ProduceInfo for T
  where T: GenerateIndex + ConvertUrlsToRawUrls + Downloader
{
  fn produce_info(&self, bins: &Bins, url: &Url) -> Result<Vec<RemotePasteFile>> {
    let raw_url = try!(self.convert_url_to_raw_url(bins, url));
    let mut res = try!(self.download(&bins, &raw_url));
    let content = try!(network::read_response(&mut res));
    let index = Index::parse(content.clone());
    let mut urls: Vec<RemotePasteFile> = Vec::new();
    match index {
      Ok(ref i) => {
        for (name, url) in i.files.clone().into_iter() {
          urls.push(RemotePasteFile {
            name: name.clone(),
            url: url.clone(),
            contents: None
          });
        }
      }
      Err(ref e) => {
        if let ErrorKind::InvalidIndexError = *e.kind() {} else {
          return Err(e.to_string().into());
        }
        let url = url.clone();
        let name = some_or_err!(url.path_segments().and_then(|s| s.last()),
                                "paste url was a root url".into());
        urls.push(RemotePasteFile {
          name: name.to_owned(),
          url: url.clone(),
          contents: Some(content)
        });
      }
    }
    Ok(urls)
  }
}

/// Produce information about raw content from URLs to HTML content.
pub trait ProduceRawInfo {
  fn produce_raw_info(&self, bins: &Bins, url: &Url) -> Result<Vec<RemotePasteFile>>;

  fn produce_raw_info_all(&self, bins: &Bins, urls: Vec<&Url>) -> Result<Vec<RemotePasteFile>> {
    let info: Vec<Vec<RemotePasteFile>> = try!(urls.iter().map(|u| self.produce_raw_info(bins, u)).collect());
    Ok(info.into_iter().flat_map(|v| v).collect())
  }
}

impl<T> ProduceRawInfo for T
  where T: ConvertUrlsToRawUrls + Downloader + UsesIndices + ProduceInfo
{
  fn produce_raw_info(&self, bins: &Bins, url: &Url) -> Result<Vec<RemotePasteFile>> {
    let info = try!(self.produce_info(bins, url));
    info.into_iter()
      .map(|r| {
        let raw_url = try!(self.convert_url_to_raw_url(bins, &r.url));
        Ok(RemotePasteFile { url: raw_url, ..r })
      })
      .collect()
  }
}

pub trait ConvertUrlsToRawUrls {
  fn convert_url_to_raw_url(&self, bins: &Bins, url: &Url) -> Result<Url>;

  fn convert_urls_to_raw_urls(&self, bins: &Bins, urls: Vec<&Url>) -> Result<Vec<Url>> {
    urls.iter().map(|u| self.convert_url_to_raw_url(bins, u)).collect()
  }
}

/// Produce raw content from a URL to HTML content.
pub trait ProduceRawContent: ProduceRawInfo + ProduceInfo + Downloader {
  fn produce_raw_contents(&self, bins: &Bins, url: &Url) -> Result<String> {
    let raw_info = if bins.arguments.urls {
      try!(self.produce_info(bins, url))
    } else {
      try!(self.produce_raw_info(bins, url))
    };
    let raw_info: Vec<RemotePasteFile> = if raw_info.len() > 1 {
      if !bins.arguments.files.is_empty() {
        let mut map: HashMap<String, RemotePasteFile> =
          raw_info.into_iter().map(|r| (r.name.to_lowercase(), r)).collect();
        try!(bins.arguments
          .files
          .iter()
          .map(|s| map.remove(&s.to_lowercase()).ok_or(format!("file {} not found", s)))
          .collect())
      } else if let Some(ref range) = bins.arguments.range {
        let mut numbered_info: HashMap<usize, RemotePasteFile> = raw_info.into_iter()
          .enumerate()
          .collect();
        try!(range.clone()
          .into_iter()
          .map(|n| numbered_info.remove(&n).ok_or(format!("file {} not found", n)))
          .collect())
      } else if bins.arguments.all {
        raw_info
      } else {
        let names = raw_info.into_iter().map(|r| String::from("  ") + &r.name).collect::<Vec<_>>().join("\n");
        return Err(format!("paste had multiple files, but no behavior was specified on the command \
                            line\n\navailable files:\n{}",
                           names)
          .into());
      }
    } else {
      raw_info
    };
    if bins.arguments.raw_urls || bins.arguments.urls {
      return Ok(raw_info.into_iter().map(|r| r.url.as_str().to_owned()).collect::<Vec<_>>().join("\n"));
    }
    let names: Vec<String> = raw_info.iter().map(|p| p.name.clone()).collect();
    let all_contents: Vec<String> = try!(raw_info.iter()
      .map(|p| {
        match p.contents.clone() {
          Some(contents) => Ok(contents),
          None => self.download(&bins, &p.url).and_then(|mut r| network::read_response(&mut r)),
        }
      })
      .collect());
    let files: LinkedHashMap<String, String> = names.into_iter().zip(all_contents.into_iter()).collect();
    Ok(files.into_iter()
      .map(|(name, content)| {
        PasteFile {
          name: name.clone(),
          data: if bins.arguments.number_lines {
            number_lines(content.clone())
          } else {
            content.clone()
          }
        }
      })
      .collect::<Vec<PasteFile>>()
      .join())
  }
}

fn number_lines(string: String) -> String {
  let lines: Vec<&str> = string.split('\n').collect();
  let num_lines = lines.len();
  let zeroes: String = repeat(" ").take(num_lines.to_string().len()).collect();
  lines.into_iter()
    .enumerate()
    .map(|(i, l)| {
      let i = i + 1;
      format!("{}{}  {}",
              &zeroes[0..zeroes.len() - i.to_string().len()],
              i,
              l)
    })
    .collect::<Vec<_>>()
    .join("\n")
}

/// Produce a URL to HTML content from raw content.
pub trait UploadContent: Uploader {
  fn upload_paste(&self, bins: &Bins, content: PasteFile) -> Result<Url>;
}

impl<T> UploadContent for T
  where T: UploadUrl + Uploader
{
  fn upload_paste(&self, bins: &Bins, content: PasteFile) -> Result<Url> {
    let url = try!(network::parse_url(self.get_upload_url()));
    let mut response = try!(self.upload(&url, bins, &content));
    network::parse_url(try!(network::read_response(&mut response)))
  }
}

/// Produce a URL to HTML content from a batch of raw content.
pub trait UploadBatchContent: UploadContent {
  fn upload_all(&self, bins: &Bins, content: Vec<PasteFile>) -> Result<Url>;
}

impl<T> UploadBatchContent for T
  where T: GenerateIndex + UploadContent
{
  fn upload_all(&self, bins: &Bins, content: Vec<PasteFile>) -> Result<Url> {
    let index = try!(self.generate_index(bins, content));
    self.upload_paste(bins,
                      PasteFile {
                        name: "index.md".to_owned(),
                        data: index.to_string()
                      })
  }
}

pub trait UsesIndices {}

/// Generate an index for multiple files.
pub trait GenerateIndex {
  fn generate_index(&self, bins: &Bins, content: Vec<PasteFile>) -> Result<Index>;
}

impl<T> GenerateIndex for T
  where T: UploadContent + UsesIndices
{
  fn generate_index(&self, bins: &Bins, content: Vec<PasteFile>) -> Result<Index> {
    let names: Vec<String> = (&content).into_iter().map(|p| p.name.clone()).collect();
    let urls: Vec<Url> = try!(content.into_iter().map(|p| self.upload_paste(bins, p)).collect());
    let uploads: LinkedHashMap<String, Url> = names.into_iter().zip(urls.into_iter()).collect();
    Ok(Index { files: uploads })
  }
}

pub trait UploadUrl {
  fn get_upload_url(&self) -> &str;
}

pub trait VerifyUrl {
  fn segments(&self, url: &Url) -> Vec<String> {
    let segments = match url.path_segments() {
      Some(s) => s,
      None => return Vec::new(),
    };
    segments.filter(|s| !s.is_empty()).map(|s| s.to_owned()).collect::<Vec<_>>()
  }

  fn verify_url(&self, url: &Url) -> bool;
}

/// A bin, which can upload content in raw form and download content in raw and HTML form.
pub trait Bin: Sync + ProduceInfo + ProduceRawContent + UploadBatchContent + VerifyUrl {
  fn get_name(&self) -> &str;

  fn get_domain(&self) -> &str;
}

trait Join {
  fn join(&self) -> String;
}

impl Join for Vec<PasteFile> {
  fn join(&self) -> String {
    if self.len() == 1 {
      self.get(0).expect("len() == 1, but no first element").data.clone()
    } else {
      self.into_iter().map(|p| format!("--- {} ---\n\n{}", p.name, p.data)).collect::<Vec<String>>().join("\n\n")
    }
  }
}

lazy_static! {
  pub static ref BINS: Vec<Box<Bin>> = {
      vec![
        Box::new(bins::Gist::new()),
        Box::new(bins::Bitbucket::new()),
        Box::new(bins::Sprunge::new()),
        Box::new(bins::Hastebin::new()),
        Box::new(bins::Pastebin::new()),
        Box::new(bins::Pastie::new())
      ]
  };
}

pub fn get_bin_names<'a>() -> Vec<&'a str> {
  BINS.iter().map(|e| e.get_name()).collect()
}

pub fn get_bin_by_name(name: &str) -> Option<&Box<Bin>> {
  BINS.iter().find(|e| e.get_name().to_lowercase() == name.to_lowercase())
}

pub fn get_bin_by_domain(domain: &str) -> Option<&Box<Bin>> {
  BINS.iter().find(|e| e.get_domain().to_lowercase() == domain.to_lowercase())
}
<|endoftext|>


<commit_before>use libimagrt::runtime::Runtime;

pub fn create(rt: &Runtime) {
}

<commit_msg>Implement Store::create() interface<commit_after>use std::collections::BTreeMap;
use std::path::PathBuf;
use std::io::stdin;
use std::fs::OpenOptions;
use std::result::Result as RResult;
use std::io::Read;
use std::ops::DerefMut;
use std::str::Split;

use clap::ArgMatches;
use toml::Table;
use toml::Value;

use libimagrt::runtime::Runtime;
use libimagstore::store::Entry;
use libimagstore::store::EntryHeader;
use libimagutil::key_value_split::IntoKeyValue;

use error::StoreError;
use error::StoreErrorKind;
use util::build_entry_path;

type Result<T> = RResult<T, StoreError>;

pub fn create(rt: &Runtime) {
    rt.cli()
        .subcommand_matches("create")
        .map(|scmd| {
            debug!("Found 'create' subcommand...");

            // unwrap is safe as value is required
            let path = build_entry_path(rt, scmd.value_of("path").unwrap());
            debug!("path = {:?}", path);

            scmd.subcommand_matches("entry")
                .map(|entry| create_from_cli_spec(rt, scmd, &path))
                .ok_or(()) // hackythehackhack
                .map_err(|_| {
                    create_from_source(rt, scmd, &path)
                        .unwrap_or_else(|e| debug!("Error building Entry: {:?}", e))
                });
        });
}

fn create_from_cli_spec(rt: &Runtime, matches: &ArgMatches, path: &PathBuf) -> Result<()> {
    let content = matches.subcommand_matches("entry")
        .map(|entry_subcommand| {
            debug!("Found entry subcommand, parsing content");
            entry_subcommand
                .value_of("content")
                .map(String::from)
                .unwrap_or_else(|| {
                    entry_subcommand
                        .value_of("content-from")
                        .map(|src| entry_from_raw(src))
                        .unwrap_or(String::new())
                })
        })
        .unwrap_or_else(|| {
            debug!("Didn't find entry subcommand, getting raw content");
            matches.value_of("from-raw")
                .map(|raw_src| entry_from_raw(raw_src))
                .unwrap_or(String::new())
        });

    debug!("Got content with len = {}", content.len());

    rt.store()
        .create(PathBuf::from(path))
        .map(|mut element| {
            {
                let mut e_content = element.get_content_mut();
                *e_content = content;
                debug!("New content set");
            }
            {
                let mut e_header  = element.get_header_mut();
                matches.subcommand_matches("entry")
                    .map(|entry_matches| {
                        *e_header = build_toml_header(entry_matches, EntryHeader::new());
                        debug!("New header set");
                    });
            }
        })
        .map_err(|e| StoreError::new(StoreErrorKind::BackendError, Some(Box::new(e))))
}

fn create_from_source(rt: &Runtime, matches: &ArgMatches, path: &PathBuf) -> Result<()> {
    let content = matches
        .value_of("from-raw")
        .ok_or(StoreError::new(StoreErrorKind::NoCommandlineCall, None))
        .map(|raw_src| entry_from_raw(raw_src));

    if content.is_err() {
        return content.map(|_| ());
    }
    let content = content.unwrap();
    debug!("Content with len = {}", content.len());

    Entry::from_str(path.clone(), &content[..])
        .map(|mut new_e| {
            rt.store()
                .create(path.clone())
                .map(|mut old_e| {
                    *old_e.deref_mut() = new_e;
                });

            debug!("Entry build");
        })
        .map_err(|serr| StoreError::new(StoreErrorKind::BackendError, Some(Box::new(serr))))
}

fn entry_from_raw(raw_src: &str) -> String {
    let mut content = String::new();
    if raw_src == "-" {
        debug!("Reading entry from stdin");
        let res = stdin().read_to_string(&mut content);
        debug!("Read {:?} bytes", res);
    } else {
        debug!("Reading entry from file at {:?}", raw_src);
        OpenOptions::new()
            .read(true)
            .write(false)
            .create(false)
            .open(raw_src)
            .and_then(|mut f| f.read_to_string(&mut content));
    }
    content
}

fn build_toml_header(matches: &ArgMatches, header: EntryHeader) -> EntryHeader {
    if let Some(headerspecs) = matches.values_of("header") {
        let mut main = BTreeMap::new();
        for tpl in headerspecs.into_iter().filter_map(|hs| String::from(hs).into_kv()) {
            let (key, value) = tpl.into();
            let mut split = key.split(".");
            let current = split.next();
            if current.is_some() {
                insert_key_into(String::from(current.unwrap()), &mut split, value, &mut main);
            }
        }
    }
    header
}

fn insert_key_into(current: String,
                   rest_path: &mut Split<&str>,
                   value: String,
                   map: &mut BTreeMap<String, Value>) {
    let next = rest_path.next();

    if next.is_none() {
        map.insert(current, parse_value(value));
    } else {
        if map.contains_key(&current) {
            match map.get_mut(&current).unwrap() {
                &mut Value::Table(ref mut t) => {
                    insert_key_into(String::from(next.unwrap()), rest_path, value, t);
                },
                _ => unreachable!(),
            }
        } else {
            let mut submap = BTreeMap::new();
            insert_key_into(String::from(next.unwrap()), rest_path, value, &mut submap);
            map.insert(current, Value::Table(submap));
        }
    }
}

fn parse_value(value: String) -> Value {
    fn is_ary(v: &String) -> bool {
        v.chars().next() == Some('[') && v.chars().last() == Some(']') && v.len() >= 3
    }

    if value == "true" {
        Value::Boolean(true)
    } else if value == "false" {
        Value::Boolean(false)
    } else if is_ary(&value) {
        let sub = &value[1..(value.len()-1)];
        Value::Array(sub.split(",").map(|v| parse_value(String::from(v))).collect())
    } else {
        Value::String(value)
    }
}

<|endoftext|>


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

#![forbid(unsafe_code)]

use std::{borrow::Cow, collections::HashSet, fmt::Display, io, path::PathBuf, process};

use anyhow::Error;
use clap::{crate_authors, crate_version, Arg, ArgMatches, Command};
use itertools::Itertools;
use mdbook::{book::Book, preprocess::CmdPreprocessor};
use proc_macro2::Span;
use syn::{spanned::Spanned, Expr, __private::ToTokens};

static LONG_ABOUT: &str =
    "This is an mdbook preprocessor tailored for autocxx code examples. Autocxx
code examples don't fit well 'mdbook test' or even alternatives such as
'skeptic' or 'doc_comment' for these reasons:

a) A single code example consists of both Rust and C++ code. They must be
   linked into a separate executable, i.e. we must make one executable per
   doc test.
b) The code examples must be presented/formatted nicely with suitable
   separate blocks for the Rust and C++ code.
c) mdbook test is not good at handling doctests which have dependencies.

This preprocessor will find code snippets like this:
```rust,autocxx
autocxx_integration_tests::doctest(
\" /* any C++ implementation code */\",
\" /* C++ header code */\",
{
/* complete Rust code including 'main' */
)
```

and will build and run them, while emitting better formatted markdown blocks
for subsequent preprocessors and renderers.
";

fn main() {
    let matches = Command::new("autocxx-mdbook-preprocessor")
        .version(crate_version!())
        .author(crate_authors!())
        .about("Expands and tests code examples in the autocxx book.")
        .long_about(LONG_ABOUT)
        .subcommand(
            Command::new("supports")
                .arg(Arg::new("renderer").required(true))
                .about("Whether a given renderer is supported by this preprocessor"),
        )
        .arg(
            Arg::new("skip_tests")
                .short('s')
                .help("Skip running doctests"),
        )
        .arg(
            Arg::new("manifest_dir")
            .long("manifest-dir")
            .help("Path to directory containing outermost autocxx Cargo.toml; necessary for trybuild to build test code successfully")
            .default_value_os(calculate_cargo_dir().as_os_str())
        )
        .get_matches();
    if let Some(supports_matches) = matches.subcommand_matches("supports") {
        // Only do our preprocessing and testing for the html renderer, not linkcheck.
        if supports_matches.value_of("renderer") == Some("html") {
            process::exit(0);
        } else {
            process::exit(1);
        }
    }
    preprocess(&matches).unwrap();
}

fn calculate_cargo_dir() -> PathBuf {
    let mut path = std::env::current_exe().unwrap();
    for _ in 0..3 {
        path = path.parent().map(|p| p.to_path_buf()).unwrap_or(path);
    }
    path.join("integration-tests")
}

fn preprocess(args: &ArgMatches) -> Result<(), Error> {
    let (_, mut book) = CmdPreprocessor::parse_input(io::stdin())?;

    let mut test_cases = Vec::new();

    Book::for_each_mut(&mut book, |sec| {
        if let mdbook::BookItem::Chapter(chapter) = sec {
            let filename = chapter
                .path
                .as_ref()
                .map(|pb| pb.to_string_lossy())
                .unwrap_or_default()
                .to_string();
            chapter.content = substitute_chapter(&chapter.content, &filename, &mut test_cases);
        }
    });

    // Now run any test cases we accumulated.
    let mut any_fails = false;
    if !args.is_present("skip_tests") {
        let num_tests = test_cases.len();
        for (counter, case) in test_cases.into_iter().enumerate() {
            eprintln!(
                "Running doctest {}/{} at {}",
                counter + 1,
                num_tests,
                &case.location
            );
            let passed = autocxx_integration_tests::doctest(
                &case.cpp,
                &case.hdr,
                case.rs.to_token_stream(),
                args.value_of_os("manifest_dir").unwrap(),
            )
            .is_ok();
            eprintln!(
                "Doctest {}/{} at {} {}.",
                counter + 1,
                num_tests,
                &case.location,
                if passed { "passed" } else { "failed" }
            );
            any_fails = any_fails || !passed;
        }
    }
    if any_fails {
        panic!("One or more tests failed.");
    }

    serde_json::to_writer(io::stdout(), &book)?;

    Ok(())
}

fn substitute_chapter(chapter: &str, filename: &str, test_cases: &mut Vec<TestCase>) -> String {
    let mut state = ChapterParseState::Start;
    let mut out = Vec::new();
    for (line_no, line) in chapter.lines().enumerate() {
        let line_type = recognize_line(line);
        let mut push_line = true;
        state = match state {
            ChapterParseState::Start => match line_type {
                LineType::CodeBlockStart | LineType::CodeBlockEnd => {
                    ChapterParseState::OtherCodeBlock
                }
                LineType::CodeBlockStartAutocxx(block_flags) => {
                    push_line = false;
                    ChapterParseState::OurCodeBlock(block_flags, Vec::new())
                }
                LineType::Misc => ChapterParseState::Start,
            },
            ChapterParseState::OtherCodeBlock => match line_type {
                LineType::CodeBlockEnd => ChapterParseState::Start,
                LineType::Misc => ChapterParseState::OtherCodeBlock,
                _ => panic!("Found confusing conflicting block markers"),
            },
            ChapterParseState::OurCodeBlock(flags, mut lines) => match line_type {
                LineType::Misc => {
                    push_line = false;
                    lines.push(line.to_string());
                    ChapterParseState::OurCodeBlock(flags, lines)
                }
                LineType::CodeBlockEnd => {
                    let location = MiniSpan {
                        filename: filename.to_string(),
                        start_line: line_no - lines.len(),
                    };
                    out.extend(handle_code_block(flags, lines, location, test_cases));
                    push_line = false;
                    ChapterParseState::Start
                }
                _ => panic!("Found something unexpected in one of our code blocks"),
            },
        };
        if push_line {
            out.push(line.to_string());
        }
    }

    out.join("\n")
}

/// Like `proc_macro2::Span` but only has the starting line. For basic
/// diagnostics.
struct MiniSpan {
    filename: String,
    start_line: usize,
}

impl Display for MiniSpan {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{} line {}", self.filename, self.start_line)
    }
}

struct TestCase {
    cpp: String,
    hdr: String,
    rs: syn::File,
    location: MiniSpan,
}

enum ChapterParseState {
    Start,
    OtherCodeBlock,
    OurCodeBlock(HashSet<String>, Vec<String>), // have found rust,autocxx
}

enum LineType {
    CodeBlockStart,
    CodeBlockStartAutocxx(HashSet<String>),
    CodeBlockEnd,
    Misc,
}

fn code_block_flags(line: &str) -> HashSet<String> {
    let line = &line[3..];
    line.split(',').map(|s| s.to_string()).collect()
}

fn recognize_line(line: &str) -> LineType {
    if line.starts_with("```") && line.len() > 3 {
        let flags = code_block_flags(line);
        if flags.contains("autocxx") {
            LineType::CodeBlockStartAutocxx(flags)
        } else {
            LineType::CodeBlockStart
        }
    } else if line == "```" {
        LineType::CodeBlockEnd
    } else {
        LineType::Misc
    }
}

fn handle_code_block(
    flags: HashSet<String>,
    lines: Vec<String>,
    location: MiniSpan,
    test_cases: &mut Vec<TestCase>,
) -> impl Iterator<Item = String> {
    let input_str = lines.join("\n");
    let fn_call = syn::parse_str::<syn::Expr>(&input_str)
        .unwrap_or_else(|_| panic!("Unable to parse outer function at {}", location));
    let fn_call = match fn_call {
        Expr::Call(expr) => expr,
        _ => panic!("Parsing unexpected"),
    };
    let mut args_iter = fn_call.args.iter();
    let cpp = extract_span(&lines, args_iter.next().unwrap().span());
    let hdr = extract_span(&lines, args_iter.next().unwrap().span());
    let rs = extract_span(&lines, args_iter.next().unwrap().span());
    let mut output = vec![
        "#### C++ header:".to_string(),
        "```cpp".to_string(),
        hdr.to_string(),
        "```".to_string(),
    ];
    if !cpp.is_empty() && !flags.contains("hidecpp") {
        output.push("#### C++ implementation:".to_string());
        output.push("```cpp".to_string());
        output.push(cpp.to_string());
        output.push("```".to_string());
    }
    output.push("#### Rust:".to_string());
    output.push("```rust,noplayground".to_string());
    output.push(escape_hexathorpes(&rs).to_string());
    output.push("```".to_string());

    // Don't run the test cases yet, because we want the preprocessor to spot
    // basic formatting errors before getting into the time consuming business of
    // running tests.
    if !flags.contains("nocompile") {
        test_cases.push(TestCase {
            cpp: unescape_quotes(&cpp),
            hdr: unescape_quotes(&hdr),
            rs: syn::parse_file(&rs)
                .unwrap_or_else(|_| panic!("Unable to parse code at {}", location)),
            location,
        });
    }

    output.into_iter()
}

fn extract_span(text: &[String], span: Span) -> Cow<str> {
    let start_line = span.start().line - 1;
    let start_col = span.start().column;
    let end_line = span.end().line - 1;
    let end_col = span.end().column;
    if start_line == end_line {
        Cow::Borrowed(&text[start_line][start_col + 1..end_col - 1])
    } else {
        let start_subset = &text[start_line][start_col + 1..];
        let end_subset = &text[end_line][..end_col - 1];
        let mid_lines = &text[start_line + 1..end_line];
        Cow::Owned(
            std::iter::once(start_subset.to_string())
                .chain(mid_lines.iter().cloned())
                .chain(std::iter::once(end_subset.to_string()))
                .join("\n"),
        )
    }
}

fn escape_hexathorpes(input: &str) -> Cow<str> {
    let re = regex::Regex::new(r"(?m)^(?P<ws>\s*)#(?P<c>.*)").unwrap();
    re.replace_all(input, "$ws##$c")
}

fn unescape_quotes(input: &str) -> String {
    input.replace("\\\"", "\"")
}
<commit_msg>Single mdbook test mode, better diagnostics.<commit_after>// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![forbid(unsafe_code)]

use std::{borrow::Cow, collections::HashSet, fmt::Display, io, path::PathBuf, process};

use anyhow::Error;
use clap::{crate_authors, crate_version, Arg, ArgMatches, Command};
use itertools::Itertools;
use mdbook::{book::Book, preprocess::CmdPreprocessor};
use proc_macro2::Span;
use syn::{spanned::Spanned, Expr, __private::ToTokens};

static LONG_ABOUT: &str =
    "This is an mdbook preprocessor tailored for autocxx code examples. Autocxx
code examples don't fit well 'mdbook test' or even alternatives such as
'skeptic' or 'doc_comment' for these reasons:

a) A single code example consists of both Rust and C++ code. They must be
   linked into a separate executable, i.e. we must make one executable per
   doc test.
b) The code examples must be presented/formatted nicely with suitable
   separate blocks for the Rust and C++ code.
c) mdbook test is not good at handling doctests which have dependencies.

This preprocessor will find code snippets like this:
```rust,autocxx
autocxx_integration_tests::doctest(
\" /* any C++ implementation code */\",
\" /* C++ header code */\",
{
/* complete Rust code including 'main' */
)
```

and will build and run them, while emitting better formatted markdown blocks
for subsequent preprocessors and renderers.
";

fn main() {
    let matches = Command::new("autocxx-mdbook-preprocessor")
        .version(crate_version!())
        .author(crate_authors!())
        .about("Expands and tests code examples in the autocxx book.")
        .long_about(LONG_ABOUT)
        .subcommand(
            Command::new("supports")
                .arg(Arg::new("renderer").required(true))
                .about("Whether a given renderer is supported by this preprocessor"),
        )
        .arg(
            Arg::new("skip_tests")
                .short('s')
                .help("Skip running doctests"),
        )
        .arg(
            Arg::new("manifest_dir")
            .long("manifest-dir")
            .help("Path to directory containing outermost autocxx Cargo.toml; necessary for trybuild to build test code successfully")
            .default_value_os(calculate_cargo_dir().as_os_str())
        )
        .get_matches();
    if let Some(supports_matches) = matches.subcommand_matches("supports") {
        // Only do our preprocessing and testing for the html renderer, not linkcheck.
        if supports_matches.value_of("renderer") == Some("html") {
            process::exit(0);
        } else {
            process::exit(1);
        }
    }
    preprocess(&matches).unwrap();
}

fn calculate_cargo_dir() -> PathBuf {
    let mut path = std::env::current_exe().unwrap();
    for _ in 0..3 {
        path = path.parent().map(|p| p.to_path_buf()).unwrap_or(path);
    }
    path.join("integration-tests")
}

fn preprocess(args: &ArgMatches) -> Result<(), Error> {
    let (_, mut book) = CmdPreprocessor::parse_input(io::stdin())?;

    let mut test_cases = Vec::new();

    Book::for_each_mut(&mut book, |sec| {
        if let mdbook::BookItem::Chapter(chapter) = sec {
            let filename = chapter
                .path
                .as_ref()
                .map(|pb| pb.to_string_lossy())
                .unwrap_or_default()
                .to_string();
            chapter.content = substitute_chapter(&chapter.content, &filename, &mut test_cases);
        }
    });

    // Now run any test cases we accumulated.
    let mut any_fails = false;
    if !args.is_present("skip_tests") {
        let num_tests = test_cases.len();
        for (counter, case) in test_cases.into_iter().enumerate() {
            if let Ok(test) = std::env::var("RUST_MDBOOK_SINGLE_TEST") {
                let desired_id: usize = test.parse().unwrap();
                if desired_id != (counter + 1) {
                    continue;
                }
            }
            eprintln!(
                "Running doctest {}/{} at {}",
                counter + 1,
                num_tests,
                &case.location
            );
            let r = autocxx_integration_tests::doctest(
                &case.cpp,
                &case.hdr,
                case.rs.to_token_stream(),
                args.value_of_os("manifest_dir").unwrap(),
            );
            let (failed, msg) = match r {
                Ok(_) => (false, "passed".to_string()),
                Err(e) => (true, format!("failed: {:?}", e)),
            };
            eprintln!(
                "Doctest {}/{} at {} {}.",
                counter + 1,
                num_tests,
                &case.location,
                msg
            );
            any_fails = any_fails || failed;
        }
    }
    if any_fails {
        panic!("One or more tests failed.");
    }

    serde_json::to_writer(io::stdout(), &book)?;

    Ok(())
}

fn substitute_chapter(chapter: &str, filename: &str, test_cases: &mut Vec<TestCase>) -> String {
    let mut state = ChapterParseState::Start;
    let mut out = Vec::new();
    for (line_no, line) in chapter.lines().enumerate() {
        let line_type = recognize_line(line);
        let mut push_line = true;
        state = match state {
            ChapterParseState::Start => match line_type {
                LineType::CodeBlockStart | LineType::CodeBlockEnd => {
                    ChapterParseState::OtherCodeBlock
                }
                LineType::CodeBlockStartAutocxx(block_flags) => {
                    push_line = false;
                    ChapterParseState::OurCodeBlock(block_flags, Vec::new())
                }
                LineType::Misc => ChapterParseState::Start,
            },
            ChapterParseState::OtherCodeBlock => match line_type {
                LineType::CodeBlockEnd => ChapterParseState::Start,
                LineType::Misc => ChapterParseState::OtherCodeBlock,
                _ => panic!("Found confusing conflicting block markers"),
            },
            ChapterParseState::OurCodeBlock(flags, mut lines) => match line_type {
                LineType::Misc => {
                    push_line = false;
                    lines.push(line.to_string());
                    ChapterParseState::OurCodeBlock(flags, lines)
                }
                LineType::CodeBlockEnd => {
                    let location = MiniSpan {
                        filename: filename.to_string(),
                        start_line: line_no - lines.len(),
                    };
                    out.extend(handle_code_block(flags, lines, location, test_cases));
                    push_line = false;
                    ChapterParseState::Start
                }
                _ => panic!("Found something unexpected in one of our code blocks"),
            },
        };
        if push_line {
            out.push(line.to_string());
        }
    }

    out.join("\n")
}

/// Like `proc_macro2::Span` but only has the starting line. For basic
/// diagnostics.
struct MiniSpan {
    filename: String,
    start_line: usize,
}

impl Display for MiniSpan {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{} line {}", self.filename, self.start_line)
    }
}

struct TestCase {
    cpp: String,
    hdr: String,
    rs: syn::File,
    location: MiniSpan,
}

enum ChapterParseState {
    Start,
    OtherCodeBlock,
    OurCodeBlock(HashSet<String>, Vec<String>), // have found rust,autocxx
}

enum LineType {
    CodeBlockStart,
    CodeBlockStartAutocxx(HashSet<String>),
    CodeBlockEnd,
    Misc,
}

fn code_block_flags(line: &str) -> HashSet<String> {
    let line = &line[3..];
    line.split(',').map(|s| s.to_string()).collect()
}

fn recognize_line(line: &str) -> LineType {
    if line.starts_with("```") && line.len() > 3 {
        let flags = code_block_flags(line);
        if flags.contains("autocxx") {
            LineType::CodeBlockStartAutocxx(flags)
        } else {
            LineType::CodeBlockStart
        }
    } else if line == "```" {
        LineType::CodeBlockEnd
    } else {
        LineType::Misc
    }
}

fn handle_code_block(
    flags: HashSet<String>,
    lines: Vec<String>,
    location: MiniSpan,
    test_cases: &mut Vec<TestCase>,
) -> impl Iterator<Item = String> {
    let input_str = lines.join("\n");
    let fn_call = syn::parse_str::<syn::Expr>(&input_str)
        .unwrap_or_else(|_| panic!("Unable to parse outer function at {}", location));
    let fn_call = match fn_call {
        Expr::Call(expr) => expr,
        _ => panic!("Parsing unexpected"),
    };
    let mut args_iter = fn_call.args.iter();
    let cpp = extract_span(&lines, args_iter.next().unwrap().span());
    let hdr = extract_span(&lines, args_iter.next().unwrap().span());
    let rs = extract_span(&lines, args_iter.next().unwrap().span());
    let mut output = vec![
        "#### C++ header:".to_string(),
        "```cpp".to_string(),
        hdr.to_string(),
        "```".to_string(),
    ];
    if !cpp.is_empty() && !flags.contains("hidecpp") {
        output.push("#### C++ implementation:".to_string());
        output.push("```cpp".to_string());
        output.push(cpp.to_string());
        output.push("```".to_string());
    }
    output.push("#### Rust:".to_string());
    output.push("```rust,noplayground".to_string());
    output.push(escape_hexathorpes(&rs).to_string());
    output.push("```".to_string());

    // Don't run the test cases yet, because we want the preprocessor to spot
    // basic formatting errors before getting into the time consuming business of
    // running tests.
    if !flags.contains("nocompile") {
        test_cases.push(TestCase {
            cpp: unescape_quotes(&cpp),
            hdr: unescape_quotes(&hdr),
            rs: syn::parse_file(&rs)
                .unwrap_or_else(|_| panic!("Unable to parse code at {}", location)),
            location,
        });
    }

    output.into_iter()
}

fn extract_span(text: &[String], span: Span) -> Cow<str> {
    let start_line = span.start().line - 1;
    let start_col = span.start().column;
    let end_line = span.end().line - 1;
    let end_col = span.end().column;
    if start_line == end_line {
        Cow::Borrowed(&text[start_line][start_col + 1..end_col - 1])
    } else {
        let start_subset = &text[start_line][start_col + 1..];
        let end_subset = &text[end_line][..end_col - 1];
        let mid_lines = &text[start_line + 1..end_line];
        Cow::Owned(
            std::iter::once(start_subset.to_string())
                .chain(mid_lines.iter().cloned())
                .chain(std::iter::once(end_subset.to_string()))
                .join("\n"),
        )
    }
}

fn escape_hexathorpes(input: &str) -> Cow<str> {
    let re = regex::Regex::new(r"(?m)^(?P<ws>\s*)#(?P<c>.*)").unwrap();
    re.replace_all(input, "$ws##$c")
}

fn unescape_quotes(input: &str) -> String {
    input.replace("\\\"", "\"")
}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Support for the 8259 Programmable Interrupt Controller.
//!
//! The 8259 PIC controls all of our IO IRQs. The PIC recieves IRQ requests
//! and feeds them to the CPU in order. An x86 system typically has two PICs,
//! PIC1 and PIC2, each of which provides 8 IRQs. Two PICs provide us with 15
//! unique IRQs, because one interrupt on the leader PIC is linked to interrupts
//! on the follower PIC.
//!
//! Mistakes were made, and Intel boneheadedly decided that it was a good idea
//! to map the vector offset of PIC1 to 8, so that it maps to interrupts
//! 8 ... 15 in the IDT. This conflicts with some of the interrupt numbers
//! used by CPU exceptions. Therefore, we must remap the PIC vectors so that
//! PIC1 starts at 32 and PIC2 at 40.

use super::super::Port;
use spin::Mutex;
use core::mem::transmute;

/// Starting offset for PIC1
const OFFSET: u8 = 0x20;

/// Commands to send to the PIC
#[repr(u8)]
#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
enum Command { Mode8086 = 0x01
             , Init     = 0x11
             , EndIRQ   = 0x20
             , ReadIRR  = 0x0a
             , ReadISR  = 0x0b
             }

/// List of IRQs on the x86.
///
/// See here for more info:
/// https://en.wikibooks.org/wiki/X86_Assembly/Programmable_Interrupt_Controller
#[repr(u8)]
#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
pub enum IRQ { /// System timer IRQ
               Timer        = 0 + OFFSET
             , /// PS/2 keyboard controller
               PS2Keyboard  = 1 + OFFSET
             , /// PIC2 cascade IRQ
               Cascade      = 2 + OFFSET
             , /// COM2 serial port
               COM2         = 3 + OFFSET
             , /// COM1 serial port
               COM1         = 4 + OFFSET
             , /// Line printer 2
               LPT2         = 5 + OFFSET
             , /// Floppy disc controller
               Floppy       = 6 + OFFSET
             , /// Line printer 1
               LPT1         = 7 + OFFSET
             , /// CMOS clock
               RTCTimer     = 8 + OFFSET
             , /// PS/2 mouse controller
               PS2Mouse     = 12 + OFFSET
             , /// Floating-point Coprocessor
               FPU          = 13 + OFFSET
             , /// ATA channel 1
               PrimaryATA   = 14 + OFFSET
             , /// ATA channel 2
               SecondaryATA = 15 + OFFSET
             }

struct PIC {
    /// The base offset to which interrupts on this PIC are mapped
    offset: u8
  , /// The port on the CPU that sends commands to this PIC.
    command_port: Port
  , /// The port that sends and recieves data from the PIC
    data_port: Port
}

impl PIC {

    const fn leader() -> PIC {
        unsafe {
            PIC { offset: OFFSET
                , command_port: Port::new(0x20)
                , data_port: Port::new(0x21)
                }
        }
    }

    const fn follower() -> PIC {
        unsafe {
            PIC { offset: OFFSET + 8
                , command_port: Port::new(0xA0)
                , data_port: Port::new(0xA1)
                }
        }
    }

    #[inline]
    fn is_leader(&self) -> bool {
        self.offset == OFFSET
    }

    #[inline]
    fn send_command(&self, command: Command) {
        unsafe {
            self.command_port
                .out8(command as u8)
        }
    }

    #[inline]
    fn send_data(&self, data: u8) {
        unsafe {
            self.data_port
                .out8(data)
        }
    }

    #[inline]
    fn initialize(&self) {
        self.send_command(Command::Init)
    }

    #[inline]
    fn read_isr(&self) -> u8 {
        self.send_command(Command::ReadISR);
        unsafe { self.data_port.in8() }
    }

    #[inline]
    fn read_irr(&self) -> u8 {
        self.send_command(Command::ReadIRR);
        unsafe { self.data_port.in8() }
    }

}

trait IRQHandler {
    /// Returns whether or not this handler handles the given IRQ
    fn handles(&self, irq: IRQ) -> bool;
    /// End an interrupt request
    fn end_interrupt(&self, irq: IRQ);
}

impl IRQHandler for PIC {

    fn handles(&self, irq: IRQ) -> bool {
        self.offset <= (irq as u8) && (irq as u8) < self.offset + 8
    }

    #[allow(unused_must_use)]
    fn end_interrupt(&self, irq: IRQ) {
        self.send_command(Command::EndIRQ)
    }
}

/// A pair of PICs in cascade mode.
///
/// This is the standard configuration on all modern x86 systems.
struct BothPICs (PIC, PIC);

impl BothPICs {
    const fn new() -> Self {
        BothPICs (PIC::leader(), PIC::follower())
    }

    /// Initialize the system's PICs.
    pub unsafe fn initialize(&mut self) {
        print!(" . . Initializing PICs");
        let wait_port = Port::new(0x80);
        let wait = || { wait_port.out8(0); };
        // helper macro to avoid writing repetitive code
        macro_rules! send {
            (pic0 => $data:expr) => {
                self.0.send_data($data)
                wait();
            };
            (pic1 => $data:expr) => {
                self.1.send_data($data)
                wait();
            };
        }

        // Read the default interrupt masks from PIC1 and PIC2
        let (saved_mask1, saved_mask2)
            = (self.0.data_port.in8(), self.1.data_port.in8());

        // Send both PICs the 'initialize' command.
        self.0.initialize(); wait();
        self.1.initialize(); wait();

        // Each PIC then expects us to send it the following:
        // 1. the PIC's new vector offset
        send!(pic0 => self.0.offset);
        send!(pic1 => self.1.offset);
        // 2. number to configure PIC cascading
        send!(pic0 => 0x04);
        send!(pic1 => 0x02);
        // 3. command for 8086 mode
        send!(pic0 => Command::Mode8086 as u8);
        send!(pic1 => Command::Mode8086 as u8);
        // 4. finally, the mask we saved earlier
        send!(pic0 => saved_mask1);
        send!(pic1 => saved_mask2);
        println!("        [ OK ]");
    }
}

impl IRQHandler for BothPICs {

    fn handles(&self, irq: IRQ) -> bool {
        self.0.handles(irq) ||
        self.1.handles(irq)
    }

    fn end_interrupt(&self, irq: IRQ) {
        if self.1.handles(irq) {
            self.1.end_interrupt(irq);
        }
        self.0.end_interrupt(irq);
    }

}

/// Global PIC instance and mutex
static PICS: Mutex<BothPICs>
    = Mutex::new(BothPICs::new());

/// Initialize the system's Programmable Interrupt Controller
pub unsafe fn initialize() {
    PICS.lock()
        .initialize()
}

/// If an interrupt is being handled by the PICs, end that interrupt.
///
/// This is called by the interrupt handler at the end of all interrupts.
/// If the interrupt is not a PIC interrupt, it silently does nothing.
pub unsafe fn end_pic_interrupt(interrupt_id: u8) {
    let pics = PICS.lock();
    let irq: IRQ = transmute(interrupt_id);

    if pics.handles(irq) {
        pics.end_interrupt(irq)
    }
}
<commit_msg>[x86_all] Initialising the PICs logs to the boot log<commit_after>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Support for the 8259 Programmable Interrupt Controller.
//!
//! The 8259 PIC controls all of our IO IRQs. The PIC recieves IRQ requests
//! and feeds them to the CPU in order. An x86 system typically has two PICs,
//! PIC1 and PIC2, each of which provides 8 IRQs. Two PICs provide us with 15
//! unique IRQs, because one interrupt on the leader PIC is linked to interrupts
//! on the follower PIC.
//!
//! Mistakes were made, and Intel boneheadedly decided that it was a good idea
//! to map the vector offset of PIC1 to 8, so that it maps to interrupts
//! 8 ... 15 in the IDT. This conflicts with some of the interrupt numbers
//! used by CPU exceptions. Therefore, we must remap the PIC vectors so that
//! PIC1 starts at 32 and PIC2 at 40.

use super::super::Port;
use spin::Mutex;
use core::mem::transmute;

/// Starting offset for PIC1
const OFFSET: u8 = 0x20;

/// Commands to send to the PIC
#[repr(u8)]
#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
enum Command { Mode8086 = 0x01
             , Init     = 0x11
             , EndIRQ   = 0x20
             , ReadIRR  = 0x0a
             , ReadISR  = 0x0b
             }

/// List of IRQs on the x86.
///
/// See here for more info:
/// https://en.wikibooks.org/wiki/X86_Assembly/Programmable_Interrupt_Controller
#[repr(u8)]
#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
pub enum IRQ { /// System timer IRQ
               Timer        = 0 + OFFSET
             , /// PS/2 keyboard controller
               PS2Keyboard  = 1 + OFFSET
             , /// PIC2 cascade IRQ
               Cascade      = 2 + OFFSET
             , /// COM2 serial port
               COM2         = 3 + OFFSET
             , /// COM1 serial port
               COM1         = 4 + OFFSET
             , /// Line printer 2
               LPT2         = 5 + OFFSET
             , /// Floppy disc controller
               Floppy       = 6 + OFFSET
             , /// Line printer 1
               LPT1         = 7 + OFFSET
             , /// CMOS clock
               RTCTimer     = 8 + OFFSET
             , /// PS/2 mouse controller
               PS2Mouse     = 12 + OFFSET
             , /// Floating-point Coprocessor
               FPU          = 13 + OFFSET
             , /// ATA channel 1
               PrimaryATA   = 14 + OFFSET
             , /// ATA channel 2
               SecondaryATA = 15 + OFFSET
             }

struct PIC {
    /// The base offset to which interrupts on this PIC are mapped
    offset: u8
  , /// The port on the CPU that sends commands to this PIC.
    command_port: Port
  , /// The port that sends and recieves data from the PIC
    data_port: Port
}

impl PIC {

    const fn leader() -> PIC {
        unsafe {
            PIC { offset: OFFSET
                , command_port: Port::new(0x20)
                , data_port: Port::new(0x21)
                }
        }
    }

    const fn follower() -> PIC {
        unsafe {
            PIC { offset: OFFSET + 8
                , command_port: Port::new(0xA0)
                , data_port: Port::new(0xA1)
                }
        }
    }

    #[inline]
    fn is_leader(&self) -> bool {
        self.offset == OFFSET
    }

    #[inline]
    fn send_command(&self, command: Command) {
        unsafe {
            self.command_port
                .out8(command as u8)
        }
    }

    #[inline]
    fn send_data(&self, data: u8) {
        unsafe {
            self.data_port
                .out8(data)
        }
    }

    #[inline]
    fn initialize(&self) {
        self.send_command(Command::Init)
    }

    #[inline]
    fn read_isr(&self) -> u8 {
        self.send_command(Command::ReadISR);
        unsafe { self.data_port.in8() }
    }

    #[inline]
    fn read_irr(&self) -> u8 {
        self.send_command(Command::ReadIRR);
        unsafe { self.data_port.in8() }
    }

}

trait IRQHandler {
    /// Returns whether or not this handler handles the given IRQ
    fn handles(&self, irq: IRQ) -> bool;
    /// End an interrupt request
    fn end_interrupt(&self, irq: IRQ);
}

impl IRQHandler for PIC {

    fn handles(&self, irq: IRQ) -> bool {
        self.offset <= (irq as u8) && (irq as u8) < self.offset + 8
    }

    #[allow(unused_must_use)]
    fn end_interrupt(&self, irq: IRQ) {
        self.send_command(Command::EndIRQ)
    }
}

/// A pair of PICs in cascade mode.
///
/// This is the standard configuration on all modern x86 systems.
struct BothPICs (PIC, PIC);

impl BothPICs {
    const fn new() -> Self {
        BothPICs (PIC::leader(), PIC::follower())
    }

    /// Initialize the system's PICs.
    pub unsafe fn initialize(&mut self) {
        let wait_port = Port::new(0x80);
        let wait = || { wait_port.out8(0); };
        // helper macro to avoid writing repetitive code
        macro_rules! send {
            (pic0 => $data:expr) => {
                self.0.send_data($data)
                wait();
            };
            (pic1 => $data:expr) => {
                self.1.send_data($data)
                wait();
            };
        }

        // Read the default interrupt masks from PIC1 and PIC2
        let (saved_mask1, saved_mask2)
            = (self.0.data_port.in8(), self.1.data_port.in8());

        // Send both PICs the 'initialize' command.
        self.0.initialize(); wait();
        self.1.initialize(); wait();

        // Each PIC then expects us to send it the following:
        // 1. the PIC's new vector offset
        send!(pic0 => self.0.offset);
        send!(pic1 => self.1.offset);
        // 2. number to configure PIC cascading
        send!(pic0 => 0x04);
        send!(pic1 => 0x02);
        // 3. command for 8086 mode
        send!(pic0 => Command::Mode8086 as u8);
        send!(pic1 => Command::Mode8086 as u8);
        // 4. finally, the mask we saved earlier
        send!(pic0 => saved_mask1);
        send!(pic1 => saved_mask2);
    }
}

impl IRQHandler for BothPICs {

    fn handles(&self, irq: IRQ) -> bool {
        self.0.handles(irq) ||
        self.1.handles(irq)
    }

    fn end_interrupt(&self, irq: IRQ) {
        if self.1.handles(irq) {
            self.1.end_interrupt(irq);
        }
        self.0.end_interrupt(irq);
    }

}

/// Global PIC instance and mutex
static PICS: Mutex<BothPICs>
    = Mutex::new(BothPICs::new());

/// Initialize the system's Programmable Interrupt Controller
pub unsafe fn initialize() {
    PICS.lock()
        .initialize();
    println!("{:<38}{:>40}", " . . Initializing PICs", "[ OKAY ]")

}

/// If an interrupt is being handled by the PICs, end that interrupt.
///
/// This is called by the interrupt handler at the end of all interrupts.
/// If the interrupt is not a PIC interrupt, it silently does nothing.
pub unsafe fn end_pic_interrupt(interrupt_id: u8) {
    let pics = PICS.lock();
    let irq: IRQ = transmute(interrupt_id);

    if pics.handles(irq) {
        pics.end_interrupt(irq)
    }
}
<|endoftext|>