<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::fs;
use std::fs::File;
use std::io::{Read, Write};
use std::path;

use bindgen::config::{Config, Language};
use bindgen::ir::{Constant, Function, ItemContainer, Static};
use bindgen::writer::{Source, SourceWriter};

/// A bindings header that can be written.
pub struct Bindings {
    config: Config,
    globals: Vec<Static>,
    constants: Vec<Constant>,
    items: Vec<ItemContainer>,
    functions: Vec<Function>,
}

impl Bindings {
    pub(crate) fn new(
        config: Config,
        constants: Vec<Constant>,
        globals: Vec<Static>,
        items: Vec<ItemContainer>,
        functions: Vec<Function>,
    ) -> Bindings {
        Bindings {
            config: config,
            globals: globals,
            constants: constants,
            items: items,
            functions: functions,
        }
    }

    pub fn write_to_file<P: AsRef<path::Path>>(&self, path: P) -> bool {
        // Don't compare files if we've never written this file before
        if !path.as_ref().is_file() {
            if let Some(parent) = path::Path::new(path.as_ref()).parent() {
                fs::create_dir_all(parent).unwrap();
            }
            self.write(File::create(path).unwrap());
            return true;
        }

        let mut new_file_contents = Vec::new();
        self.write(&mut new_file_contents);

        let mut old_file_contents = Vec::new();
        {
            let mut old_file = File::open(&path).unwrap();
            old_file.read_to_end(&mut old_file_contents).unwrap();
        }

        if old_file_contents != new_file_contents {
            let mut new_file = File::create(&path).unwrap();
            new_file.write_all(&new_file_contents).unwrap();
            true
        } else {
            false
        }
    }

    pub fn write_headers<F: Write>(&self, out: &mut SourceWriter<F>) {
        if let Some(ref f) = self.config.header {
            out.new_line_if_not_start();
            write!(out, "{}", f);
            out.new_line();
        }
        if let Some(ref f) = self.config.include_guard {
            out.new_line_if_not_start();
            write!(out, "#ifndef {}", f);
            out.new_line();
            write!(out, "#define {}", f);
            out.new_line();
        }
        if self.config.include_version {
            out.new_line_if_not_start();
            write!(
                out,
                "/* Generated with cbindgen:{} */",
                ::bindgen::config::VERSION
            );
            out.new_line();
        }
        if let Some(ref f) = self.config.autogen_warning {
            out.new_line_if_not_start();
            write!(out, "{}", f);
            out.new_line();
        }

        out.new_line_if_not_start();
        if !self.config.no_includes {
            if self.config.language == Language::C {
                out.write("#include <stdarg.h>");
                out.new_line();
                out.write("#include <stdbool.h>");
                out.new_line();
                out.write("#include <stdint.h>");
                out.new_line();
                out.write("#include <stdlib.h>");
                out.new_line();
            } else {
                out.write("#include <cstdarg>");
                out.new_line();
                out.write("#include <cstdint>");
                out.new_line();
                out.write("#include <cstdlib>");
                out.new_line();
            }

        }

        for include in &self.config.sys_includes {
            write!(out, "#include <{}>", include);
            out.new_line();
        }

        for include in &self.config.includes {
            write!(out, "#include \"{}\"", include);
            out.new_line();
        }
    }

    pub fn write<F: Write>(&self, file: F) {
        let mut out = SourceWriter::new(file, &self.config);

        if !self.config.no_includes || !self.config.includes.is_empty() || !self.config.sys_includes.is_empty() {
            self.write_headers(&mut out);
        }

        if self.config.language == Language::Cxx {
            self.open_namespaces(&mut out);
        }

        for constant in &self.constants {
            if constant.ty.is_primitive_or_ptr_primitive() {
                out.new_line_if_not_start();
                constant.write(&self.config, &mut out);
                out.new_line();
            }
        }

        for item in &self.items {
            if item
                .deref()
                .annotations()
                .bool("no-export")
                .unwrap_or(false)
            {
                continue;
            }

            out.new_line_if_not_start();
            match item {
                &ItemContainer::Constant(..) => unreachable!(),
                &ItemContainer::Static(..) => unreachable!(),
                &ItemContainer::Enum(ref x) => x.write(&self.config, &mut out),
                &ItemContainer::Struct(ref x) => x.write(&self.config, &mut out),
                &ItemContainer::Union(ref x) => x.write(&self.config, &mut out),
                &ItemContainer::OpaqueItem(ref x) => x.write(&self.config, &mut out),
                &ItemContainer::Typedef(ref x) => x.write(&self.config, &mut out),
            }
            out.new_line();
        }

        for constant in &self.constants {
            if !constant.ty.is_primitive_or_ptr_primitive() {
                out.new_line_if_not_start();
                constant.write(&self.config, &mut out);
                out.new_line();
            }
        }

        if !self.functions.is_empty() || !self.globals.is_empty() {
            if self.config.language == Language::Cxx {
                out.new_line_if_not_start();
                out.write("extern \"C\" {");
                out.new_line();
            }

            for global in &self.globals {
                out.new_line_if_not_start();
                global.write(&self.config, &mut out);
                out.new_line();
            }

            for function in &self.functions {
                out.new_line_if_not_start();
                function.write(&self.config, &mut out);
                out.new_line();
            }

            if self.config.language == Language::Cxx {
                out.new_line_if_not_start();
                out.write("} // extern \"C\"");
                out.new_line();
            }
        }

        if self.config.language == Language::Cxx {
            self.close_namespaces(&mut out);
        }

        if let Some(ref f) = self.config.include_guard {
            out.new_line_if_not_start();
            if self.config.language == Language::C {
                write!(out, "#endif /* {} */", f);
            } else {
                write!(out, "#endif // {}", f);
            }
            out.new_line();
        }
        if let Some(ref f) = self.config.trailer {
            out.new_line_if_not_start();
            write!(out, "{}", f);
            out.new_line();
        }
    }

    pub(crate) fn open_namespaces<F: Write>(&self, out: &mut SourceWriter<F>) {
        let mut wrote_namespace: bool = false;
        if let Some(ref namespace) = self.config.namespace {
            wrote_namespace = true;

            out.new_line();
            write!(out, "namespace {} {{", namespace);
        }
        if let Some(ref namespaces) = self.config.namespaces {
            wrote_namespace = true;
            for namespace in namespaces {
                out.new_line();
                write!(out, "namespace {} {{", namespace);
            }
        }
        if wrote_namespace {
            out.new_line();
        }
    }

    pub(crate) fn close_namespaces<F: Write>(&self, out: &mut SourceWriter<F>) {
        let mut wrote_namespace: bool = false;
        if let Some(ref namespaces) = self.config.namespaces {
            wrote_namespace = true;

            for namespace in namespaces.iter().rev() {
                out.new_line_if_not_start();
                write!(out, "}} // namespace {}", namespace);
            }
        }
        if let Some(ref namespace) = self.config.namespace {
            wrote_namespace = true;

            out.new_line_if_not_start();
            write!(out, "}} // namespace {}", namespace);
        }
        if wrote_namespace {
            out.new_line();
        }
    }
}
<commit_msg>Cargo fmt<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::fs;
use std::fs::File;
use std::io::{Read, Write};
use std::path;

use bindgen::config::{Config, Language};
use bindgen::ir::{Constant, Function, ItemContainer, Static};
use bindgen::writer::{Source, SourceWriter};

/// A bindings header that can be written.
pub struct Bindings {
    config: Config,
    globals: Vec<Static>,
    constants: Vec<Constant>,
    items: Vec<ItemContainer>,
    functions: Vec<Function>,
}

impl Bindings {
    pub(crate) fn new(
        config: Config,
        constants: Vec<Constant>,
        globals: Vec<Static>,
        items: Vec<ItemContainer>,
        functions: Vec<Function>,
    ) -> Bindings {
        Bindings {
            config: config,
            globals: globals,
            constants: constants,
            items: items,
            functions: functions,
        }
    }

    pub fn write_to_file<P: AsRef<path::Path>>(&self, path: P) -> bool {
        // Don't compare files if we've never written this file before
        if !path.as_ref().is_file() {
            if let Some(parent) = path::Path::new(path.as_ref()).parent() {
                fs::create_dir_all(parent).unwrap();
            }
            self.write(File::create(path).unwrap());
            return true;
        }

        let mut new_file_contents = Vec::new();
        self.write(&mut new_file_contents);

        let mut old_file_contents = Vec::new();
        {
            let mut old_file = File::open(&path).unwrap();
            old_file.read_to_end(&mut old_file_contents).unwrap();
        }

        if old_file_contents != new_file_contents {
            let mut new_file = File::create(&path).unwrap();
            new_file.write_all(&new_file_contents).unwrap();
            true
        } else {
            false
        }
    }

    pub fn write_headers<F: Write>(&self, out: &mut SourceWriter<F>) {
        if let Some(ref f) = self.config.header {
            out.new_line_if_not_start();
            write!(out, "{}", f);
            out.new_line();
        }
        if let Some(ref f) = self.config.include_guard {
            out.new_line_if_not_start();
            write!(out, "#ifndef {}", f);
            out.new_line();
            write!(out, "#define {}", f);
            out.new_line();
        }
        if self.config.include_version {
            out.new_line_if_not_start();
            write!(
                out,
                "/* Generated with cbindgen:{} */",
                ::bindgen::config::VERSION
            );
            out.new_line();
        }
        if let Some(ref f) = self.config.autogen_warning {
            out.new_line_if_not_start();
            write!(out, "{}", f);
            out.new_line();
        }

        out.new_line_if_not_start();
        if !self.config.no_includes {
            if self.config.language == Language::C {
                out.write("#include <stdarg.h>");
                out.new_line();
                out.write("#include <stdbool.h>");
                out.new_line();
                out.write("#include <stdint.h>");
                out.new_line();
                out.write("#include <stdlib.h>");
                out.new_line();
            } else {
                out.write("#include <cstdarg>");
                out.new_line();
                out.write("#include <cstdint>");
                out.new_line();
                out.write("#include <cstdlib>");
                out.new_line();
            }
        }

        for include in &self.config.sys_includes {
            write!(out, "#include <{}>", include);
            out.new_line();
        }

        for include in &self.config.includes {
            write!(out, "#include \"{}\"", include);
            out.new_line();
        }
    }

    pub fn write<F: Write>(&self, file: F) {
        let mut out = SourceWriter::new(file, &self.config);

        if !self.config.no_includes
            || !self.config.includes.is_empty()
            || !self.config.sys_includes.is_empty()
        {
            self.write_headers(&mut out);
        }

        if self.config.language == Language::Cxx {
            self.open_namespaces(&mut out);
        }

        for constant in &self.constants {
            if constant.ty.is_primitive_or_ptr_primitive() {
                out.new_line_if_not_start();
                constant.write(&self.config, &mut out);
                out.new_line();
            }
        }

        for item in &self.items {
            if item
                .deref()
                .annotations()
                .bool("no-export")
                .unwrap_or(false)
            {
                continue;
            }

            out.new_line_if_not_start();
            match item {
                &ItemContainer::Constant(..) => unreachable!(),
                &ItemContainer::Static(..) => unreachable!(),
                &ItemContainer::Enum(ref x) => x.write(&self.config, &mut out),
                &ItemContainer::Struct(ref x) => x.write(&self.config, &mut out),
                &ItemContainer::Union(ref x) => x.write(&self.config, &mut out),
                &ItemContainer::OpaqueItem(ref x) => x.write(&self.config, &mut out),
                &ItemContainer::Typedef(ref x) => x.write(&self.config, &mut out),
            }
            out.new_line();
        }

        for constant in &self.constants {
            if !constant.ty.is_primitive_or_ptr_primitive() {
                out.new_line_if_not_start();
                constant.write(&self.config, &mut out);
                out.new_line();
            }
        }

        if !self.functions.is_empty() || !self.globals.is_empty() {
            if self.config.language == Language::Cxx {
                out.new_line_if_not_start();
                out.write("extern \"C\" {");
                out.new_line();
            }

            for global in &self.globals {
                out.new_line_if_not_start();
                global.write(&self.config, &mut out);
                out.new_line();
            }

            for function in &self.functions {
                out.new_line_if_not_start();
                function.write(&self.config, &mut out);
                out.new_line();
            }

            if self.config.language == Language::Cxx {
                out.new_line_if_not_start();
                out.write("} // extern \"C\"");
                out.new_line();
            }
        }

        if self.config.language == Language::Cxx {
            self.close_namespaces(&mut out);
        }

        if let Some(ref f) = self.config.include_guard {
            out.new_line_if_not_start();
            if self.config.language == Language::C {
                write!(out, "#endif /* {} */", f);
            } else {
                write!(out, "#endif // {}", f);
            }
            out.new_line();
        }
        if let Some(ref f) = self.config.trailer {
            out.new_line_if_not_start();
            write!(out, "{}", f);
            out.new_line();
        }
    }

    pub(crate) fn open_namespaces<F: Write>(&self, out: &mut SourceWriter<F>) {
        let mut wrote_namespace: bool = false;
        if let Some(ref namespace) = self.config.namespace {
            wrote_namespace = true;

            out.new_line();
            write!(out, "namespace {} {{", namespace);
        }
        if let Some(ref namespaces) = self.config.namespaces {
            wrote_namespace = true;
            for namespace in namespaces {
                out.new_line();
                write!(out, "namespace {} {{", namespace);
            }
        }
        if wrote_namespace {
            out.new_line();
        }
    }

    pub(crate) fn close_namespaces<F: Write>(&self, out: &mut SourceWriter<F>) {
        let mut wrote_namespace: bool = false;
        if let Some(ref namespaces) = self.config.namespaces {
            wrote_namespace = true;

            for namespace in namespaces.iter().rev() {
                out.new_line_if_not_start();
                write!(out, "}} // namespace {}", namespace);
            }
        }
        if let Some(ref namespace) = self.config.namespace {
            wrote_namespace = true;

            out.new_line_if_not_start();
            write!(out, "}} // namespace {}", namespace);
        }
        if wrote_namespace {
            out.new_line();
        }
    }
}
<|endoftext|>


<commit_before>// TODO: Rename engines module and basically refactor the entire structure of the crate
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(1).map(|x| x[..x.len() - 1].to_owned())).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())
  }
}

// TODO: Fix this so it isn't basically just copy/paste from ProduceRawInfo
impl<T> ProduceInfo for T
  where T: GenerateIndex + ConvertUrlsToRawUrls + Downloader
{
  fn produce_info(&self, _: &Bins, url: &Url) -> Result<Vec<RemotePasteFile>> {
    let raw_url = try!(self.convert_url_to_raw_url(url));
    let mut res = try!(self.download(&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.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
{
  fn produce_raw_info(&self, _: &Bins, url: &Url) -> Result<Vec<RemotePasteFile>> {
    let raw_url = try!(self.convert_url_to_raw_url(url));
    let mut res = try!(self.download(&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.into_iter() {
          urls.push(RemotePasteFile {
            name: name.clone(),
            url: try!(self.convert_url_to_raw_url(url)),
            contents: None
          });
        }
      }
      Err(ref e) => {
        if let ErrorKind::InvalidIndexError = *e.kind() {} else {
          return Err(e.to_string().into());
        }
        let url = raw_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)
  }
}

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

  fn convert_urls_to_raw_urls(&self, urls: Vec<&Url>) -> Result<Vec<Url>> {
    urls.iter().map(|u| self.convert_url_to_raw_url(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<Vec<PasteFile>> {
    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
    };
    // TODO: Change this whole system so that we don't have to do stupid stuff like this.
    if bins.arguments.raw_urls || bins.arguments.urls {
      return Ok(vec![PasteFile {
                       name: "urls".to_owned(),
                       data: 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(&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: content.clone()
        }
      })
      .collect())
  }
}

/// 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;
}

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

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

lazy_static! {
  pub static ref BINS: Vec<Box<Bin>> = {
      vec![
        Box::new(bins::Gist::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>refactor(engines): reduce code re-use for ProduceInfo and ProduceRawInfo<commit_after>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(1).map(|x| x[..x.len() - 1].to_owned())).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, url: &Url) -> Result<Vec<RemotePasteFile>> {
    let raw_url = try!(self.convert_url_to_raw_url(url));
    let mut res = try!(self.download(&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.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(&r.url));
      Ok(RemotePasteFile { url: raw_url, ..r })
    }).collect()
  }
}

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

  fn convert_urls_to_raw_urls(&self, urls: Vec<&Url>) -> Result<Vec<Url>> {
    urls.iter().map(|u| self.convert_url_to_raw_url(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<Vec<PasteFile>> {
    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
    };
    // TODO: Change this whole system so that we don't have to do stupid stuff like this.
    if bins.arguments.raw_urls || bins.arguments.urls {
      return Ok(vec![PasteFile {
                       name: "urls".to_owned(),
                       data: 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(&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: content.clone()
        }
      })
      .collect())
  }
}

/// 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;
}

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

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

lazy_static! {
  pub static ref BINS: Vec<Box<Bin>> = {
      vec![
        Box::new(bins::Gist::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 std::mem;
use std::ptr;
use std::fmt;
use std::iter::{Iterator, ExactSizeIterator};
use super::Deque;

struct Rawlink<T> {
    p: *mut T
}

impl<T> Rawlink<T> {
    fn none() -> Rawlink<T> {
        Rawlink { p: ptr::null_mut() }
    }

    fn some(n: &mut T) -> Rawlink<T> {
        Rawlink { p: n }
    }

    fn take(&mut self) -> Rawlink<T> {
        mem::replace(self, Rawlink::none())
    }
}

struct Node<T> {
    item: T,
    next: Option<Box<Node<T>>>,
    prev: Rawlink<Node<T>>

}

impl<T> Node<T> {
    /// work around for moved value
    fn into_item_and_pointers(self) -> (T, Option<Box<Node<T>>>, Rawlink<Node<T>>) {
        (self.item, self.next, self.prev)
    }

    fn size(&self) -> usize {
        let mut p = self.next.as_ref();
        let mut sz = 1;
        while p.is_some() {
            p = p.unwrap().next.as_ref();
            sz += 1;
        }
        sz
    }
}

/// linked double queue
pub struct LinkedDeque<T> {
    first: Option<Box<Node<T>>>,
    last: Rawlink<Node<T>>
}

impl<T> Deque<T> for LinkedDeque<T> {
    fn new() -> LinkedDeque<T> {
        LinkedDeque {
            first: None,
            last: Rawlink::none()
        }
    }
    fn is_empty(&self) -> bool {
        self.first.is_none()
    }
    fn size(&self) -> usize {
        match self.first {
            None    => 0,
            Some(ref l) => l.size()
        }
    }
    fn add_first(&mut self, item: T) {
        let mut old_first = self.first.take();
        let mut first = Box::new(Node {
            item: item,
            next: None,
            prev: Rawlink::none()
        });

        if old_first.is_some() {
            old_first.as_mut().unwrap().prev = Rawlink::some(&mut first);
            // move in
            first.next = old_first;
        } else {
            self.last = Rawlink::some(&mut first);
        }

        self.first = Some(first)
    }

    fn add_last(&mut self, item: T) {
        if self.first.is_some() {
            let old_last = self.last.take();
            let mut last = Box::new(Node {
                item: item,
                next: None,
                prev: Rawlink::none(),
            });
            self.last = Rawlink::some(&mut last);
            unsafe {
                (*old_last.p).next = Some(last);
            }

        } else {
            self.add_first(item)
        }
    }

    fn remove_first(&mut self) -> Option<T> {
        let old_first = self.first.take();
        if old_first.is_some() {
            let (item, mut first, _) = old_first.unwrap().into_item_and_pointers();
            // update new first's prev field
            first.as_mut().map(|v| v.prev = Rawlink::none());
            self.first = first;
            Some(item)
        } else {
            None
        }
    }

    fn remove_last(&mut self) -> Option<T> {
        let old_last = self.last.take();
        if old_last.p.is_null() {
            return None;
        }
        let last_ref_mut = unsafe { mem::transmute::<_, &mut Node<T>>(old_last.p) };

        let last: Node<T> = mem::replace(last_ref_mut, unsafe { mem::zeroed() });

        if last.prev.p.is_null() {
            self.first = None;
        } else {
            unsafe {
                (*last.prev.p).next = None;
            }
        }
        self.last = last.prev;

        Some(last.item)
    }

}

impl<T> LinkedDeque<T> {
    fn iter<'a>(&'a self) -> Iter<'a, T> {
        Iter {
            current: self.first.as_ref(),
            nelem: self.size()
        }
    }
}

impl<T: fmt::Display> fmt::Display for LinkedDeque<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.first {
            None    => {
                try!(write!(f, "<empty deque>"));
            },
            Some(ref l) => {
                try!(write!(f, "("));
                let mut p = Some(l);
                while p.is_some() {
                    try!(write!(f, "{},", p.unwrap().item));
                    p = p.unwrap().next.as_ref();
                }
                try!(write!(f, ")"));

            }
        }
        Ok(())
    }
}

// TODO impl DoubleEndedIterator, ExactSizeIterator
pub struct Iter<'a, T: 'a> {
    current: Option<&'a Box<Node<T>>>,
    nelem: usize,
}

impl<'a, T> Iterator for Iter<'a, T> {
    type Item = &'a T;

    fn next(&mut self) -> Option<&'a T> {
        if self.nelem == 0 {
            return None;
        }
        let old_current = self.current.take();

        self.current = (**old_current.unwrap()).next.as_ref();
        self.nelem -= 1;
        Some(&old_current.as_ref().unwrap().item)
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.nelem, Some(self.nelem))
    }
}

impl <'a, T> ExactSizeIterator for Iter<'a, T> {
    fn len(&self) -> usize {
        self.nelem
    }
}

#[test]
fn test_linked_deque_add_remove() {
    let mut deque: LinkedDeque<i32> = Deque::new();

    assert!(deque.is_empty());
    assert_eq!(deque.remove_first(), None);
    assert_eq!(deque.remove_last(), None);

    let result = vec![4, 0, 5, 2, 3];
    let mut rit = result.iter();
    // -1 remove last
    // -2 remove first
    // extra 2 more -1 -2 will result None
    for s in vec![4, 2, 3, 0, -1, -2, 5, -2, -1, -1, -2] {
        if s == -2 {
            assert_eq!(deque.remove_first(), rit.next().map(|&v| v));
        } else if s == -1 {
            assert_eq!(deque.remove_last(), rit.next().map(|&v| v));
        } else {
            deque.add_first(s);
        }
    }

    assert!(deque.is_empty());
}

#[test]
fn test_linked_deque_size() {
    let mut deque: LinkedDeque<i32> = Deque::new();

    assert!(deque.is_empty());

    let result = vec![0, 1, 2, 3, 4, 3, 2, 3, 2, 1, 0, 0];
    let mut rit = result.iter();
    // -1 remove last
    // -2 remove first
    for s in vec![4, 2, 3, 0, -1, -2, 5, -1, -1, -2] {
        if s == -2 {
            assert_eq!(deque.size(), *rit.next().unwrap());
            deque.remove_first();
        } else if s == -1 {
            assert_eq!(deque.size(), *rit.next().unwrap());
            deque.remove_last();
        } else {
            assert_eq!(deque.size(), *rit.next().unwrap());
            deque.add_first(s);
        }
    }

    assert!(deque.is_empty());
}

#[test]
fn test_linked_deque_iter() {
    let mut deque: LinkedDeque<i32> = Deque::new();

    assert!(deque.is_empty());
    for i in 0..10 {
        if i % 2 == 0 {
            deque.add_first(i);
        } else {
            deque.add_last(i);
        }
    }

    let mut n = 0i32;
    let it = deque.iter();
    assert_eq!(it.len(), 10);

    for _ in it {
        n += 1;
    }
    assert_eq!(n, 10);
}
<commit_msg>stacks_and_queues/linked_deque: make .iter() public<commit_after>use std::mem;
use std::ptr;
use std::fmt;
use super::Deque;

struct Rawlink<T> {
    p: *mut T
}

impl<T> Rawlink<T> {
    fn none() -> Rawlink<T> {
        Rawlink { p: ptr::null_mut() }
    }

    fn some(n: &mut T) -> Rawlink<T> {
        Rawlink { p: n }
    }

    fn take(&mut self) -> Rawlink<T> {
        mem::replace(self, Rawlink::none())
    }
}

struct Node<T> {
    item: T,
    next: Option<Box<Node<T>>>,
    prev: Rawlink<Node<T>>

}

impl<T> Node<T> {
    /// work around for moved value
    fn into_item_and_pointers(self) -> (T, Option<Box<Node<T>>>, Rawlink<Node<T>>) {
        (self.item, self.next, self.prev)
    }

    fn size(&self) -> usize {
        let mut p = self.next.as_ref();
        let mut sz = 1;
        while p.is_some() {
            p = p.unwrap().next.as_ref();
            sz += 1;
        }
        sz
    }
}

/// linked double queue
pub struct LinkedDeque<T> {
    first: Option<Box<Node<T>>>,
    last: Rawlink<Node<T>>
}

impl<T> Deque<T> for LinkedDeque<T> {
    fn new() -> LinkedDeque<T> {
        LinkedDeque {
            first: None,
            last: Rawlink::none()
        }
    }
    fn is_empty(&self) -> bool {
        self.first.is_none()
    }
    fn size(&self) -> usize {
        match self.first {
            None    => 0,
            Some(ref l) => l.size()
        }
    }
    fn add_first(&mut self, item: T) {
        let mut old_first = self.first.take();
        let mut first = Box::new(Node {
            item: item,
            next: None,
            prev: Rawlink::none()
        });

        if old_first.is_some() {
            old_first.as_mut().unwrap().prev = Rawlink::some(&mut first);
            // move in
            first.next = old_first;
        } else {
            self.last = Rawlink::some(&mut first);
        }

        self.first = Some(first)
    }

    fn add_last(&mut self, item: T) {
        if self.first.is_some() {
            let old_last = self.last.take();
            let mut last = Box::new(Node {
                item: item,
                next: None,
                prev: Rawlink::none(),
            });
            self.last = Rawlink::some(&mut last);
            unsafe {
                (*old_last.p).next = Some(last);
            }

        } else {
            self.add_first(item)
        }
    }

    fn remove_first(&mut self) -> Option<T> {
        let old_first = self.first.take();
        if old_first.is_some() {
            let (item, mut first, _) = old_first.unwrap().into_item_and_pointers();
            // update new first's prev field
            first.as_mut().map(|v| v.prev = Rawlink::none());
            self.first = first;
            Some(item)
        } else {
            None
        }
    }

    fn remove_last(&mut self) -> Option<T> {
        let old_last = self.last.take();
        if old_last.p.is_null() {
            return None;
        }
        let last_ref_mut = unsafe { mem::transmute::<_, &mut Node<T>>(old_last.p) };

        let last: Node<T> = mem::replace(last_ref_mut, unsafe { mem::zeroed() });

        if last.prev.p.is_null() {
            self.first = None;
        } else {
            unsafe {
                (*last.prev.p).next = None;
            }
        }
        self.last = last.prev;

        Some(last.item)
    }

}

impl<T> LinkedDeque<T> {
    pub fn iter<'a>(&'a self) -> Iter<'a, T> {
        Iter {
            current: self.first.as_ref(),
            nelem: self.size()
        }
    }
}

impl<T: fmt::Display> fmt::Display for LinkedDeque<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.first {
            None    => {
                try!(write!(f, "<empty deque>"));
            },
            Some(ref l) => {
                try!(write!(f, "("));
                let mut p = Some(l);
                while p.is_some() {
                    try!(write!(f, "{},", p.unwrap().item));
                    p = p.unwrap().next.as_ref();
                }
                try!(write!(f, ")"));

            }
        }
        Ok(())
    }
}

// TODO impl DoubleEndedIterator
pub struct Iter<'a, T: 'a> {
    current: Option<&'a Box<Node<T>>>,
    nelem: usize,
}

impl<'a, T> Iterator for Iter<'a, T> {
    type Item = &'a T;

    fn next(&mut self) -> Option<&'a T> {
        if self.nelem == 0 {
            return None;
        }
        let old_current = self.current.take();

        self.current = (**old_current.unwrap()).next.as_ref();
        self.nelem -= 1;
        Some(&old_current.as_ref().unwrap().item)
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.nelem, Some(self.nelem))
    }
}

impl <'a, T> ExactSizeIterator for Iter<'a, T> {
    fn len(&self) -> usize {
        self.nelem
    }
}

#[test]
fn test_linked_deque_add_remove() {
    let mut deque: LinkedDeque<i32> = Deque::new();

    assert!(deque.is_empty());
    assert_eq!(deque.remove_first(), None);
    assert_eq!(deque.remove_last(), None);

    let result = vec![4, 0, 5, 2, 3];
    let mut rit = result.iter();
    // -1 remove last
    // -2 remove first
    // extra 2 more -1 -2 will result None
    for s in vec![4, 2, 3, 0, -1, -2, 5, -2, -1, -1, -2] {
        if s == -2 {
            assert_eq!(deque.remove_first(), rit.next().map(|&v| v));
        } else if s == -1 {
            assert_eq!(deque.remove_last(), rit.next().map(|&v| v));
        } else {
            deque.add_first(s);
        }
    }

    assert!(deque.is_empty());
}

#[test]
fn test_linked_deque_size() {
    let mut deque: LinkedDeque<i32> = Deque::new();

    assert!(deque.is_empty());

    let result = vec![0, 1, 2, 3, 4, 3, 2, 3, 2, 1, 0, 0];
    let mut rit = result.iter();
    // -1 remove last
    // -2 remove first
    for s in vec![4, 2, 3, 0, -1, -2, 5, -1, -1, -2] {
        if s == -2 {
            assert_eq!(deque.size(), *rit.next().unwrap());
            deque.remove_first();
        } else if s == -1 {
            assert_eq!(deque.size(), *rit.next().unwrap());
            deque.remove_last();
        } else {
            assert_eq!(deque.size(), *rit.next().unwrap());
            deque.add_first(s);
        }
    }

    assert!(deque.is_empty());
}

#[test]
fn test_linked_deque_iter() {
    let mut deque: LinkedDeque<i32> = Deque::new();

    assert!(deque.is_empty());
    for i in 0..10 {
        if i % 2 == 0 {
            deque.add_first(i);
        } else {
            deque.add_last(i);
        }
    }

    let mut n = 0i32;
    let it = deque.iter();
    assert_eq!(it.len(), 10);

    for _ in it {
        n += 1;
    }
    assert_eq!(n, 10);
}
<|endoftext|>


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

//! Stored objects.

use std::io::Read;
use std::rc::Rc;

use fallible_iterator::{FallibleIterator, IntoFallibleIterator};

use super::super::common::{
    ContainerRef, IntoVerified, ObjectRef, Refresh, ResourceIterator, ResourceQuery,
};
use super::super::session::Session;
use super::super::utils::Query;
use super::super::{Error, Result};
use super::{api, protocol};

/// A query to objects.
#[derive(Clone, Debug)]
pub struct ObjectQuery {
    session: Rc<Session>,
    c_name: String,
    query: Query,
    can_paginate: bool,
}

/// Structure representing an object.
#[derive(Clone, Debug)]
pub struct Object {
    session: Rc<Session>,
    inner: protocol::Object,
    c_name: String,
}

impl Object {
    /// Create a new Object object.
    pub(crate) fn new(session: Rc<Session>, inner: protocol::Object, c_name: String) -> Object {
        Object {
            session,
            inner,
            c_name,
        }
    }

    /// Create an object.
    pub(crate) fn create<C, Id, R>(
        session: Rc<Session>,
        container: C,
        name: Id,
        body: R,
    ) -> Result<Object>
    where
        C: Into<ContainerRef>,
        Id: AsRef<str>,
        R: Read + Send + 'static,
    {
        let c_ref = container.into();
        let c_name = c_ref.to_string();
        let inner = api::create_object(&session, c_ref, name, body)?;
        Ok(Object::new(session, inner, c_name))
    }

    /// Load an Object.
    pub(crate) fn load<C, Id>(session: Rc<Session>, container: C, name: Id) -> Result<Object>
    where
        C: Into<ContainerRef>,
        Id: AsRef<str>,
    {
        let c_ref = container.into();
        let c_name = c_ref.to_string();
        let inner = api::get_object(&session, c_ref, name)?;
        Ok(Object::new(session, inner, c_name))
    }

    /// Delete the object.
    #[inline]
    pub fn delete(self) -> Result<()> {
        api::delete_object(&self.session, &self.c_name, self.inner.name)
    }

    /// Download the object.
    ///
    /// The object can be read from the resulting reader.
    #[inline]
    pub fn download(&self) -> Result<impl Read + '_> {
        api::download_object(&self.session, &self.c_name, &self.inner.name)
    }

    transparent_property! {
        #[doc = "Total size of the object."]
        bytes: u64
    }

    /// Container name.
    #[inline]
    pub fn container_name(&self) -> &String {
        &self.c_name
    }

    transparent_property! {
        #[doc = "Object content type (if set)."]
        content_type: ref Option<String>
    }

    transparent_property! {
        #[doc = "Object name."]
        name: ref String
    }
}

impl Refresh for Object {
    /// Refresh the object.
    fn refresh(&mut self) -> Result<()> {
        self.inner = api::get_object(&self.session, &self.c_name, &self.inner.name)?;
        Ok(())
    }
}

impl ObjectQuery {
    pub(crate) fn new<C: Into<ContainerRef>>(session: Rc<Session>, container: C) -> ObjectQuery {
        ObjectQuery {
            session,
            c_name: container.into().into(),
            query: Query::new(),
            can_paginate: true,
        }
    }

    /// Add marker to the request.
    ///
    /// Using this disables automatic pagination.
    pub fn with_marker<T: Into<String>>(mut self, marker: T) -> Self {
        self.can_paginate = false;
        self.query.push_str("marker", marker);
        self
    }

    /// Add limit to the request.
    ///
    /// Using this disables automatic pagination.
    pub fn with_limit(mut self, limit: usize) -> Self {
        self.can_paginate = false;
        self.query.push("limit", limit);
        self
    }

    /// Convert this query into an iterator executing the request.
    ///
    /// Returns a `FallibleIterator`, which is an iterator with each `next`
    /// call returning a `Result`.
    ///
    /// Note that no requests are done until you start iterating.
    pub fn into_iter(self) -> ResourceIterator<ObjectQuery> {
        debug!(
            "Fetching objects in container {} with {:?}",
            self.c_name, self.query
        );
        ResourceIterator::new(self)
    }

    /// Execute this request and return all results.
    ///
    /// A convenience shortcut for `self.into_iter().collect()`.
    pub fn all(self) -> Result<Vec<Object>> {
        self.into_iter().collect()
    }

    /// Return one and exactly one result.
    ///
    /// Fails with `ResourceNotFound` if the query produces no results and
    /// with `TooManyItems` if the query produces more than one result.
    pub fn one(mut self) -> Result<Object> {
        debug!(
            "Fetching one object in container {} with {:?}",
            self.c_name, self.query
        );
        if self.can_paginate {
            // We need only one result. We fetch maximum two to be able
            // to check if the query yieled more than one result.
            self.query.push("limit", 2);
        }

        self.into_iter().one()
    }
}

impl ResourceQuery for ObjectQuery {
    type Item = Object;

    const DEFAULT_LIMIT: usize = 100;

    fn can_paginate(&self) -> Result<bool> {
        Ok(self.can_paginate)
    }

    fn extract_marker(&self, resource: &Self::Item) -> String {
        resource.name().clone()
    }

    fn fetch_chunk(&self, limit: Option<usize>, marker: Option<String>) -> Result<Vec<Self::Item>> {
        let query = self.query.with_marker_and_limit(limit, marker);
        Ok(api::list_objects(&self.session, &self.c_name, query)?
            .into_iter()
            .map(|item| Object {
                session: self.session.clone(),
                inner: item,
                c_name: self.c_name.clone(),
            })
            .collect())
    }
}

impl IntoFallibleIterator for ObjectQuery {
    type Item = Object;

    type Error = Error;

    type IntoFallibleIter = ResourceIterator<ObjectQuery>;

    fn into_fallible_iter(self) -> Self::IntoFallibleIter {
        self.into_iter()
    }
}

impl From<Object> for ObjectRef {
    fn from(value: Object) -> ObjectRef {
        ObjectRef::new_verified(value.inner.name)
    }
}

#[cfg(feature = "object-storage")]
impl IntoVerified for ObjectRef {}
<commit_msg>feat(object-storage): add url method to Object (#110)<commit_after>// Copyright 2019 Dmitry Tantsur <divius.inside@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Stored objects.

use std::io::Read;
use std::rc::Rc;

use fallible_iterator::{FallibleIterator, IntoFallibleIterator};
use osauth::services::OBJECT_STORAGE;
use reqwest::Url;

use super::super::common::{
    ContainerRef, IntoVerified, ObjectRef, Refresh, ResourceIterator, ResourceQuery,
};
use super::super::session::Session;
use super::super::utils::Query;
use super::super::{Error, Result};
use super::{api, protocol};

/// A query to objects.
#[derive(Clone, Debug)]
pub struct ObjectQuery {
    session: Rc<Session>,
    c_name: String,
    query: Query,
    can_paginate: bool,
}

/// Structure representing an object.
#[derive(Clone, Debug)]
pub struct Object {
    session: Rc<Session>,
    inner: protocol::Object,
    c_name: String,
}

impl Object {
    /// Create a new Object object.
    pub(crate) fn new(session: Rc<Session>, inner: protocol::Object, c_name: String) -> Object {
        Object {
            session,
            inner,
            c_name,
        }
    }

    /// Create an object.
    pub(crate) fn create<C, Id, R>(
        session: Rc<Session>,
        container: C,
        name: Id,
        body: R,
    ) -> Result<Object>
    where
        C: Into<ContainerRef>,
        Id: AsRef<str>,
        R: Read + Send + 'static,
    {
        let c_ref = container.into();
        let c_name = c_ref.to_string();
        let inner = api::create_object(&session, c_ref, name, body)?;
        Ok(Object::new(session, inner, c_name))
    }

    /// Load an Object.
    pub(crate) fn load<C, Id>(session: Rc<Session>, container: C, name: Id) -> Result<Object>
    where
        C: Into<ContainerRef>,
        Id: AsRef<str>,
    {
        let c_ref = container.into();
        let c_name = c_ref.to_string();
        let inner = api::get_object(&session, c_ref, name)?;
        Ok(Object::new(session, inner, c_name))
    }

    /// Delete the object.
    #[inline]
    pub fn delete(self) -> Result<()> {
        api::delete_object(&self.session, &self.c_name, self.inner.name)
    }

    /// Download the object.
    ///
    /// The object can be read from the resulting reader.
    #[inline]
    pub fn download(&self) -> Result<impl Read + '_> {
        api::download_object(&self.session, &self.c_name, &self.inner.name)
    }

    transparent_property! {
        #[doc = "Total size of the object."]
        bytes: u64
    }

    /// Container name.
    #[inline]
    pub fn container_name(&self) -> &String {
        &self.c_name
    }

    transparent_property! {
        #[doc = "Object content type (if set)."]
        content_type: ref Option<String>
    }

    transparent_property! {
        #[doc = "Object name."]
        name: ref String
    }

    /// Object url.
    #[inline]
    pub fn url(&self) -> Result<Url> {
        self.session
            .get_endpoint(OBJECT_STORAGE, &[self.container_name(), self.name()])
    }
}

impl Refresh for Object {
    /// Refresh the object.
    fn refresh(&mut self) -> Result<()> {
        self.inner = api::get_object(&self.session, &self.c_name, &self.inner.name)?;
        Ok(())
    }
}

impl ObjectQuery {
    pub(crate) fn new<C: Into<ContainerRef>>(session: Rc<Session>, container: C) -> ObjectQuery {
        ObjectQuery {
            session,
            c_name: container.into().into(),
            query: Query::new(),
            can_paginate: true,
        }
    }

    /// Add marker to the request.
    ///
    /// Using this disables automatic pagination.
    pub fn with_marker<T: Into<String>>(mut self, marker: T) -> Self {
        self.can_paginate = false;
        self.query.push_str("marker", marker);
        self
    }

    /// Add limit to the request.
    ///
    /// Using this disables automatic pagination.
    pub fn with_limit(mut self, limit: usize) -> Self {
        self.can_paginate = false;
        self.query.push("limit", limit);
        self
    }

    /// Convert this query into an iterator executing the request.
    ///
    /// Returns a `FallibleIterator`, which is an iterator with each `next`
    /// call returning a `Result`.
    ///
    /// Note that no requests are done until you start iterating.
    pub fn into_iter(self) -> ResourceIterator<ObjectQuery> {
        debug!(
            "Fetching objects in container {} with {:?}",
            self.c_name, self.query
        );
        ResourceIterator::new(self)
    }

    /// Execute this request and return all results.
    ///
    /// A convenience shortcut for `self.into_iter().collect()`.
    pub fn all(self) -> Result<Vec<Object>> {
        self.into_iter().collect()
    }

    /// Return one and exactly one result.
    ///
    /// Fails with `ResourceNotFound` if the query produces no results and
    /// with `TooManyItems` if the query produces more than one result.
    pub fn one(mut self) -> Result<Object> {
        debug!(
            "Fetching one object in container {} with {:?}",
            self.c_name, self.query
        );
        if self.can_paginate {
            // We need only one result. We fetch maximum two to be able
            // to check if the query yieled more than one result.
            self.query.push("limit", 2);
        }

        self.into_iter().one()
    }
}

impl ResourceQuery for ObjectQuery {
    type Item = Object;

    const DEFAULT_LIMIT: usize = 100;

    fn can_paginate(&self) -> Result<bool> {
        Ok(self.can_paginate)
    }

    fn extract_marker(&self, resource: &Self::Item) -> String {
        resource.name().clone()
    }

    fn fetch_chunk(&self, limit: Option<usize>, marker: Option<String>) -> Result<Vec<Self::Item>> {
        let query = self.query.with_marker_and_limit(limit, marker);
        Ok(api::list_objects(&self.session, &self.c_name, query)?
            .into_iter()
            .map(|item| Object {
                session: self.session.clone(),
                inner: item,
                c_name: self.c_name.clone(),
            })
            .collect())
    }
}

impl IntoFallibleIterator for ObjectQuery {
    type Item = Object;

    type Error = Error;

    type IntoFallibleIter = ResourceIterator<ObjectQuery>;

    fn into_fallible_iter(self) -> Self::IntoFallibleIter {
        self.into_iter()
    }
}

impl From<Object> for ObjectRef {
    fn from(value: Object) -> ObjectRef {
        ObjectRef::new_verified(value.inner.name)
    }
}

#[cfg(feature = "object-storage")]
impl IntoVerified for ObjectRef {}
<|endoftext|>


<commit_before>/* diosix RV32G/RV64G common hardware-specific code
 *
 * (c) Chris Williams, 2019.
 *
 * See LICENSE for usage and copying.
 */

/*!
# RISC-V platform

This platform crate is the diosix hypervisor's RISC-V-specific code: it starts up a system, and subsequently controls the hardware on behalf
of the portable portion of the hypervisor, which lives in the [hypervisor](https://github.com/diodesign/diosix/blob/master/src/hypervisor/)
parent crate. This portable code should be free of hardware-specific routines and structures, and call down to a platform crate, such as this one,
to carry out hardware-specific actions, such as accessing timers and the serial port.

This platform crate is documented primarily to aid the porting of diosix to other processor architectures, for example: OpenPOWER.
If a platform crate implements the following public data structures and methods, it should integrate seamlessly with the portable hypervisor, and
allow the hypervisor to run on hardware supported by the crate. A new platform crate effectively creates a hardware port of diosix.

The portable hypervisor links with a platform crate when it is built, and that crate is chosen by the [`build.rs`](https://github.com/diodesign/diosix/blob/master/build.rs)
script from the `--target` parameter on the `cargo` command line. For example, selecting the target `riscv32imac-unknown-none-elf`
will link the portable hypervisor code with this `riscv` platform crate, producing an executable that runs on supported RISC-V-powered
hardware. This crate supports 32 and 64-bit RISC-V targets.

See the [build instructions](https://github.com/diodesign/diosix/blob/master/docs/building.md) for more details.

Feel free to drop the project lead a message if you have any questions. The next obvious port would be OpenPOWER.

# Implementing your own platform crate

The information below describes the interface that needs to be implemented between the portable hypervisor and a platform crate,
and suggests steps to create a new platform crate. The documentation should help you effectively port diosix to more hardware.
It is assumed you are comfortable writing low-level Rust and assembly code, and are familiar with stacks, interrupt handlers, linker scripts,
bootloaders, Device Trees, and similar concepts.

## Boot

A suitable GNU binutils-compatible linker script file - [link.ld](https://github.com/diodesign/diosix/blob/master/src/platform/riscv/link.ld),
for example - should be used to to create a single executable, typically an ELF executable, that contains the portable hypervisor,
the platform-specific crate, a [boot capsule](https://github.com/diodesign/diosix/blob/master/docs/buildroot.md), and any required dependencies.
This executable should be loaded into memory and executed by a bootloader.

The linker script should define an entry point within the platform crate, and define locations in memory to load its various sections. These memory
addresses are largely specific to the hardware being supported, and should be chosen carefully by the crate's author. The platform crate's code is
the first diosix code to run when a system is booted, specifically at the aforementioned entry point.

The crate's startup code, typically written in assembly language, should initialize one or more of the system's CPU cores, and assign them each a
stack of memory, set up suitable interrupt and exception handlers, and enable interrupts and exceptions. These handlers should be located within the
platform crate. Finally, the running core(s) should each call the portable hypervisor's entry point `hventry()` with the following parameters:

```no_run
pub extern "C" fn hventry(cpu_nr: CPUId, device_tree_buf: &u8)
```

where `cpu_nr` is an ID number representing the CPU core calling the `hventry()` method, and `device_tree_buf` is a pointer to a standard Device Tree Blob, a binary
data structure, that describes the hardware present in the system. This blob may be generated by the bootloader or system firmware, and thus can be passed
straight on to the hypervisor. The CPU ID number should be an unsigned integer unique to each CPU core, generated at boot and valid until the next system
boot. These ID numbers should start from zero and increase contiguously.

One way to generate these ID numbers is to have each core atomically fetch, increment, and write back a shared unsigned integer value in memory, starting
with a value of zero. This ID number can also be used to calculate stack space for each CPU core: each core can use, for example, the `nth` 64KB block starting
from the base of a known available area of RAM, where `n` is the core's atomically assigned ID number, and the stack pointer of each core descends from
the top of each block. These stacks can be temporary, and long-term stacks allocated after the system has detected all present memory and CPU cores.

One CPU core, known as the boot core, must have an ID that matches `cpu::BOOT_CPUID`, which is defined in the hypervisor's
[`cpu.rs`](https://github.com/diodesign/diosix/blob/master/src/hypervisor/cpu.rs) as zero, as it will be used by the portable hypervisor to initialize the rest
of the system. Once the hypervisor's entry point `hventry()` is called by the boot core, and it has set up the hypervisor's memory management and other features,
all cores capable of running workloads will be made available to the scheduler, once they have each called `hventry()`. If a non-boot core calls `hventry()`
before this initialization is complete, it will be paused until the boot core completes its initialization work.

In summary, one or more CPU cores can call `hventry()` at any time to wait for workloads to be assigned to them by the scheduler. However, this assignment will not
begin until the boot core has called `hventry()` and successfully initialized the hypervisor. If `hventry()` returns to the startup code, a serious failure has
occurred, and the CPU core should be halted: an error message will be output by the hypervisor's debug subsystem. Note that the portable hypervisor's entry point,
`hventry()`, must only be called once per CPU core. In future, it may be possible to reenter the hypervisor if a failure occurs.

In this document, a workload is any software chosen to run on a CPU core: typically, that will be supervisor-mode or user-mode code running within a capsule,
which is an isolated virtualized environment, or the hypervisor itself, running in a mode below supervisor mode. The platform-specific code
leaves the definition and scheduling of workloads to the portable hypervisor: the platform crate simply runs what it is told to run.

## Exceptions and interrupts

Once a CPU core has called `hventry()`, the portable hypervisor has control of the core, and will direct the core to call down to the platform crate when
hardware-specific actions are required. However, when an exception or interrupt occurs, the relevant exception or interrupt handler within the platform crate,
specified by the startup code, will be called, passing control to the platform-specific crate. Note that the diosix project refers to software-generated exceptions
and hardware-generated interrupts generically as IRQs, though this naming convention may change if it proves too confusing.

The IRQ handlers specified by the startup code must disable IRQs, if not already done so automatically by the CPU, and preserve the state of the interrupted
code's CPU registers onto a suitable stack. Then it must call up into the portable hypervisor's IRQ entry point:

```no_run
pub extern "C" fn hypervisor_irq_handler(context: IRQContext)
```

where `context` is a pointer to a platform-specific `IRQContext` structure describing the state of the CPU core when this particular IRQ was raised: this
should contain the state of the interrupted code, the cause of the IRQ, a return address, and other information from the platform that may be
needed to service the IRQ. The format of this structure is defined by the platform crate's implementor: it is opaque to the portable hypervisor.

The context should contain a snapshot of the interrupted software's CPU core and hardware state to a sufficient degree that this state can be
saved to memory and later restored from memory to the CPU core's registers, and the software resumed with any adverse effects: it should just continue as is.
A workload may be interrupted by a periodic timer interrupt, or it may make a system call, for instance. In either case, its context may be copied to memory,
and another workload's context loaded into the CPU registers from memory, continuing the latter software's execution.

The portable hypervisor, once `hypervisor_irq_handler()` is called, will decide what to do next, and call down into the platform crate to perform the next steps. This may involve
copying the interrupted code's register context from the IRQ handler's stack to a structure in memory, and replacing the context on the stack with another
workload's register context from memory.

Thus, when `hypervisor_irq_handler()` returns, whatever context is stored in the IRQ stack - be it the interrupted code's register state, or another
workload's register state - must be restored into the CPU core's registers, IRQs enabled again, and execution allowed to continue. This mechanism allows
the hypervisor to preemptively switch out and switch in different workloads for each CPU core, perform system calls, and so on, all transparently to the workloads.

The platform-specific code can optionally enable IRQs during the handling of an IRQ, provided it can cope with nested interrupts and exceptions. It is recommended
that exceptions, at least, are enabled in order to catch any errors.

## Steps to create a platform crate

With the above in mind, the following steps are suggested as one approach to implementing a new platform crate:

### 1. Start afresh

Make a copy of this RISC-V crate, deleting the private structures and methods and the contents of the public methods until you are left with a
skeleton of empty public methods and populated public structures. Name the crate after the hardware platform, such as `openpower` or `arm`.

A platform crate should support a single processor architecture, such as Arm, RISC-V, MIPS, PowerPC, or x86. The crate should not be tied to a
particular system, such as a particular motherboard, and should instead be able to provide, or pass on from a bootloader or
system firmware, a Device Tree Blob describing the underlying memory map and peripherals that the hypervisor can process to discover and use hardware
as needed. This ensures a wide range of systems can be automatically supported, rather than hard-wire support for a select few into the codebase.

### 2. Craft a linker script and boot code

Write a GNU binutils-compatible linker script that defines an entry point, such as `_start`, and be aware this may need to be at a particular address
in memory, depending on the bootloader and firmware. Create an `asm/entry.s` assembly language source file, and in it, implement `_start`.
This routine should assign any running cores with unique ID numbers, as described above, and some individual initial stack space. Next, it should
perform any required initialization, as described by the processor architecture's manuals, and then configure the CPU cores to use interrupt and
exception handlers you will define in `asm/irq.s`.

Finally, enable interrupts, and call `hventry()` with the ID number and a pointer to a Device Tree Blob describing the system. This blob will need
to be generated either by the boot code, the bootloader, or the machine's firmware.

### 3. Implement IRQ handlers

In `asm/irq.s`, construct the software exception and hardware interrupt handlers specified in `entry.s`. Each of these handlers should preserve
the interrupted code's CPU registers and any other relevant hardware context onto a stack. Once this state is saved, `hypervisor_irq_handler()`
should be called with a pointer to that stacked information, which is presented to the hypervisor as an opaque platform-specific `IRQContext`
structure. When `hypervisor_irq_handler()` returns, the handler should unwind its previous actions: it should copy the stacked context back into
the CPU's registers, enable IRQs, and return to whatever code was interrupted.

The context on the stack may have been switched for the context of another workload, in which case the CPU is actually returning to different code.
Some of the context may also have been altered if the workload made a system call, and data is being returned via the CPU registers.

The portable portion of the hypervisor cannot understand the machine-specific `IRQContext` so after it is invoked via `hypervisor_irq_handler()`,
it will call the following platform-provided public method, implemented in, say, `src/irq.rs` to understand the IRQ:

```no_run
pub fn dispatch(context: IRQContext) -> IRQ
```

where `context` is the context passed to `hypervisor_irq_handler()` and `IRQ` is a portable representation of an interrupt or exception. This
method's job is to translate the machine-specific `context` to an `IRQ` structure the hypervisor can understand regardless of the underlying
hardware. See the definition of `IRQ` in [`src/irq.rs`](https://github.com/diodesign/diosix/blob/master/src/platform/riscv/src/irq.rs) for a guide,
or the documentation below.

Finally, in `src/irq.rs`, implement the following public method:

```no_run
pub fn acknowledge(irq: IRQ)
```

which should instruct the source of the given hardware IRQ, `irq`, that its latest interrupt has been serviced, and that it should not generate
another IRQ unless a fresh interrupt condition arises. This method is called from `hypervisor_irq_handler()` as it concludes handling a hardware interrupt.

### 4. Implement CPU management code

Various methods for inspecting and controlling CPU cores, in [`src/cpu.rs`](https://github.com/diodesign/diosix/blob/master/src/platform/riscv/src/cpu.rs),
need to be implemented. These are called from the portable hypervisor when it needs to perform platform-specific tasks, and most of them should be
straightforward to port.

Of interest is `save_supervisor_state()`, which copies the currently running workload's register and hardware context, stored on the CPU core's IRQ stack,
to a structure in memory, and `load_supervisor_state()` overwrites the currently running workload's context, stored on the CPU core's IRQ stack, with
another context from memory.

These are required to context switch between workloads. Each workload can have one or more virtual CPUs assigned to it; these are essentially
traditional threads if you consider a workload a process. The scheduler switches workload virtual CPU contexts in and out of the physical CPUs. This is
all managed from the portable side of the hypervisor: the platform crate has to implement the actual preservation and restoration of CPU contexts.

Meanwhile, `CPUDescriptionIter` should iterate over the present physical CPU cores, and describe each of them as a text string. Finally,
`features_priv_check()` checks whether the CPU core can run code in the given processor mode, such as user mode. This is needed to support
architectures that provide CPU cores, such as high-privilege system management cores, that are only capable of running code in a particular level.

### 5. Implement hardware support

Finally, the platform crate will need to provide support for [timers](https://github.com/diodesign/diosix/blob/master/src/platform/riscv/src/timer.rs),
a [serial port](https://github.com/diodesign/diosix/blob/master/src/platform/riscv/src/serial.rs) for debug output,
[ending unit tests](https://github.com/diodesign/diosix/blob/master/src/platform/riscv/src/test.rs) in Qemu, and accessing and controlling
[physical memory](https://github.com/diodesign/diosix/blob/master/src/platform/riscv/src/physmem.rs). These are hardware specific and beyond the scope
of this porting overview.

**The specifics of the methods and structures that need to be implemented will be described in the reference documentation below.**

*/

#![no_std]
#![feature(asm)]

/* expose architecture common code to platform-specific code */
#[macro_use]
pub mod csr;
pub mod physmem;
pub mod devicetree;
pub mod irq;
pub mod cpu;
pub mod timer;
pub mod serial;
pub mod test;<commit_msg>Capitalize D in diosix. Keep consistent style across documentation<commit_after>/* diosix RV32G/RV64G common hardware-specific code
 *
 * (c) Chris Williams, 2019.
 *
 * See LICENSE for usage and copying.
 */

/*!
# RISC-V platform

This platform crate is the Diosix hypervisor's RISC-V-specific code: it starts up a system, and subsequently controls the hardware on behalf
of the portable portion of the hypervisor, which lives in the [hypervisor](https://github.com/diodesign/diosix/blob/master/src/hypervisor/)
parent crate. This portable code should be free of hardware-specific routines and structures, and call down to a platform crate, such as this one,
to carry out hardware-specific actions, such as accessing timers and the serial port.

This platform crate is documented primarily to aid the porting of Diosix to other processor architectures, for example: OpenPOWER.
If a platform crate implements the following public data structures and methods, it should integrate seamlessly with the portable hypervisor, and
allow the hypervisor to run on hardware supported by the crate. A new platform crate effectively creates a hardware port of Diosix.

The portable hypervisor links with a platform crate when it is built, and that crate is chosen by the [`build.rs`](https://github.com/diodesign/diosix/blob/master/build.rs)
script from the `--target` parameter on the `cargo` command line. For example, selecting the target `riscv32imac-unknown-none-elf`
will link the portable hypervisor code with this `riscv` platform crate, producing an executable that runs on supported RISC-V-powered
hardware. This crate supports 32 and 64-bit RISC-V targets.

See the [build instructions](https://github.com/diodesign/diosix/blob/master/docs/building.md) for more details.

Feel free to drop the project lead a message if you have any questions. The next obvious port would be OpenPOWER.

# Implementing your own platform crate

The information below describes the interface that needs to be implemented between the portable hypervisor and a platform crate,
and suggests steps to create a new platform crate. The documentation should help you effectively port Diosix to more hardware.
It is assumed you are comfortable writing low-level Rust and assembly code, and are familiar with stacks, interrupt handlers, linker scripts,
bootloaders, Device Trees, and similar concepts.

## Boot

A suitable GNU binutils-compatible linker script file - [link.ld](https://github.com/diodesign/diosix/blob/master/src/platform/riscv/link.ld),
for example - should be used to to create a single executable, typically an ELF executable, that contains the portable hypervisor,
the platform-specific crate, a [boot capsule](https://github.com/diodesign/diosix/blob/master/docs/buildroot.md), and any required dependencies.
This executable should be loaded into memory and executed by a bootloader.

The linker script should define an entry point within the platform crate, and define locations in memory to load its various sections. These memory
addresses are largely specific to the hardware being supported, and should be chosen carefully by the crate's author. The platform crate's code is
the first Diosix code to run when a system is booted, specifically at the aforementioned entry point.

The crate's startup code, typically written in assembly language, should initialize one or more of the system's CPU cores, and assign them each a
stack of memory, set up suitable interrupt and exception handlers, and enable interrupts and exceptions. These handlers should be located within the
platform crate. Finally, the running core(s) should each call the portable hypervisor's entry point `hventry()` with the following parameters:

```no_run
pub extern "C" fn hventry(cpu_nr: CPUId, device_tree_buf: &u8)
```

where `cpu_nr` is an ID number representing the CPU core calling the `hventry()` method, and `device_tree_buf` is a pointer to a standard Device Tree Blob, a binary
data structure, that describes the hardware present in the system. This blob may be generated by the bootloader or system firmware, and thus can be passed
straight on to the hypervisor. The CPU ID number should be an unsigned integer unique to each CPU core, generated at boot and valid until the next system
boot. These ID numbers should start from zero and increase contiguously.

One way to generate these ID numbers is to have each core atomically fetch, increment, and write back a shared unsigned integer value in memory, starting
with a value of zero. This ID number can also be used to calculate stack space for each CPU core: each core can use, for example, the `nth` 64KB block starting
from the base of a known available area of RAM, where `n` is the core's atomically assigned ID number, and the stack pointer of each core descends from
the top of each block. These stacks can be temporary, and long-term stacks allocated after the system has detected all present memory and CPU cores.

One CPU core, known as the boot core, must have an ID that matches `cpu::BOOT_CPUID`, which is defined in the hypervisor's
[`cpu.rs`](https://github.com/diodesign/diosix/blob/master/src/hypervisor/cpu.rs) as zero, as it will be used by the portable hypervisor to initialize the rest
of the system. Once the hypervisor's entry point `hventry()` is called by the boot core, and it has set up the hypervisor's memory management and other features,
all cores capable of running workloads will be made available to the scheduler, once they have each called `hventry()`. If a non-boot core calls `hventry()`
before this initialization is complete, it will be paused until the boot core completes its initialization work.

In summary, one or more CPU cores can call `hventry()` at any time to wait for workloads to be assigned to them by the scheduler. However, this assignment will not
begin until the boot core has called `hventry()` and successfully initialized the hypervisor. If `hventry()` returns to the startup code, a serious failure has
occurred, and the CPU core should be halted: an error message will be output by the hypervisor's debug subsystem. Note that the portable hypervisor's entry point,
`hventry()`, must only be called once per CPU core. In future, it may be possible to reenter the hypervisor if a failure occurs.

In this document, a workload is any software chosen to run on a CPU core: typically, that will be supervisor-mode or user-mode code running within a capsule,
which is an isolated virtualized environment, or the hypervisor itself, running in a mode below supervisor mode. The platform-specific code
leaves the definition and scheduling of workloads to the portable hypervisor: the platform crate simply runs what it is told to run.

## Exceptions and interrupts

Once a CPU core has called `hventry()`, the portable hypervisor has control of the core, and will direct the core to call down to the platform crate when
hardware-specific actions are required. However, when an exception or interrupt occurs, the relevant exception or interrupt handler within the platform crate,
specified by the startup code, will be called, passing control to the platform-specific crate. Note that the Diosix project refers to software-generated exceptions
and hardware-generated interrupts generically as IRQs, though this naming convention may change if it proves too confusing.

The IRQ handlers specified by the startup code must disable IRQs, if not already done so automatically by the CPU, and preserve the state of the interrupted
code's CPU registers onto a suitable stack. Then it must call up into the portable hypervisor's IRQ entry point:

```no_run
pub extern "C" fn hypervisor_irq_handler(context: IRQContext)
```

where `context` is a pointer to a platform-specific `IRQContext` structure describing the state of the CPU core when this particular IRQ was raised: this
should contain the state of the interrupted code, the cause of the IRQ, a return address, and other information from the platform that may be
needed to service the IRQ. The format of this structure is defined by the platform crate's implementor: it is opaque to the portable hypervisor.

The context should contain a snapshot of the interrupted software's CPU core and hardware state to a sufficient degree that this state can be
saved to memory and later restored from memory to the CPU core's registers, and the software resumed with any adverse effects: it should just continue as is.
A workload may be interrupted by a periodic timer interrupt, or it may make a system call, for instance. In either case, its context may be copied to memory,
and another workload's context loaded into the CPU registers from memory, continuing the latter software's execution.

The portable hypervisor, once `hypervisor_irq_handler()` is called, will decide what to do next, and call down into the platform crate to perform the next steps. This may involve
copying the interrupted code's register context from the IRQ handler's stack to a structure in memory, and replacing the context on the stack with another
workload's register context from memory.

Thus, when `hypervisor_irq_handler()` returns, whatever context is stored in the IRQ stack - be it the interrupted code's register state, or another
workload's register state - must be restored into the CPU core's registers, IRQs enabled again, and execution allowed to continue. This mechanism allows
the hypervisor to preemptively switch out and switch in different workloads for each CPU core, perform system calls, and so on, all transparently to the workloads.

The platform-specific code can optionally enable IRQs during the handling of an IRQ, provided it can cope with nested interrupts and exceptions. It is recommended
that exceptions, at least, are enabled in order to catch any errors.

## Steps to create a platform crate

With the above in mind, the following steps are suggested as one approach to implementing a new platform crate:

### 1. Start afresh

Make a copy of this RISC-V crate, deleting the private structures and methods and the contents of the public methods until you are left with a
skeleton of empty public methods and populated public structures. Name the crate after the hardware platform, such as `openpower` or `arm`.

A platform crate should support a single processor architecture, such as Arm, RISC-V, MIPS, PowerPC, or x86. The crate should not be tied to a
particular system, such as a particular motherboard, and should instead be able to provide, or pass on from a bootloader or
system firmware, a Device Tree Blob describing the underlying memory map and peripherals that the hypervisor can process to discover and use hardware
as needed. This ensures a wide range of systems can be automatically supported, rather than hard-wire support for a select few into the codebase.

### 2. Craft a linker script and boot code

Write a GNU binutils-compatible linker script that defines an entry point, such as `_start`, and be aware this may need to be at a particular address
in memory, depending on the bootloader and firmware. Create an `asm/entry.s` assembly language source file, and in it, implement `_start`.
This routine should assign any running cores with unique ID numbers, as described above, and some individual initial stack space. Next, it should
perform any required initialization, as described by the processor architecture's manuals, and then configure the CPU cores to use interrupt and
exception handlers you will define in `asm/irq.s`.

Finally, enable interrupts, and call `hventry()` with the ID number and a pointer to a Device Tree Blob describing the system. This blob will need
to be generated either by the boot code, the bootloader, or the machine's firmware.

### 3. Implement IRQ handlers

In `asm/irq.s`, construct the software exception and hardware interrupt handlers specified in `entry.s`. Each of these handlers should preserve
the interrupted code's CPU registers and any other relevant hardware context onto a stack. Once this state is saved, `hypervisor_irq_handler()`
should be called with a pointer to that stacked information, which is presented to the hypervisor as an opaque platform-specific `IRQContext`
structure. When `hypervisor_irq_handler()` returns, the handler should unwind its previous actions: it should copy the stacked context back into
the CPU's registers, enable IRQs, and return to whatever code was interrupted.

The context on the stack may have been switched for the context of another workload, in which case the CPU is actually returning to different code.
Some of the context may also have been altered if the workload made a system call, and data is being returned via the CPU registers.

The portable portion of the hypervisor cannot understand the machine-specific `IRQContext` so after it is invoked via `hypervisor_irq_handler()`,
it will call the following platform-provided public method, implemented in, say, `src/irq.rs` to understand the IRQ:

```no_run
pub fn dispatch(context: IRQContext) -> IRQ
```

where `context` is the context passed to `hypervisor_irq_handler()` and `IRQ` is a portable representation of an interrupt or exception. This
method's job is to translate the machine-specific `context` to an `IRQ` structure the hypervisor can understand regardless of the underlying
hardware. See the definition of `IRQ` in [`src/irq.rs`](https://github.com/diodesign/diosix/blob/master/src/platform/riscv/src/irq.rs) for a guide,
or the documentation below.

Finally, in `src/irq.rs`, implement the following public method:

```no_run
pub fn acknowledge(irq: IRQ)
```

which should instruct the source of the given hardware IRQ, `irq`, that its latest interrupt has been serviced, and that it should not generate
another IRQ unless a fresh interrupt condition arises. This method is called from `hypervisor_irq_handler()` as it concludes handling a hardware interrupt.

### 4. Implement CPU management code

Various methods for inspecting and controlling CPU cores, in [`src/cpu.rs`](https://github.com/diodesign/diosix/blob/master/src/platform/riscv/src/cpu.rs),
need to be implemented. These are called from the portable hypervisor when it needs to perform platform-specific tasks, and most of them should be
straightforward to port.

Of interest is `save_supervisor_state()`, which copies the currently running workload's register and hardware context, stored on the CPU core's IRQ stack,
to a structure in memory, and `load_supervisor_state()` overwrites the currently running workload's context, stored on the CPU core's IRQ stack, with
another context from memory.

These are required to context switch between workloads. Each workload can have one or more virtual CPUs assigned to it; these are essentially
traditional threads if you consider a workload a process. The scheduler switches workload virtual CPU contexts in and out of the physical CPUs. This is
all managed from the portable side of the hypervisor: the platform crate has to implement the actual preservation and restoration of CPU contexts.

Meanwhile, `CPUDescriptionIter` should iterate over the present physical CPU cores, and describe each of them as a text string. Finally,
`features_priv_check()` checks whether the CPU core can run code in the given processor mode, such as user mode. This is needed to support
architectures that provide CPU cores, such as high-privilege system management cores, that are only capable of running code in a particular level.

### 5. Implement hardware support

Finally, the platform crate will need to provide support for [timers](https://github.com/diodesign/diosix/blob/master/src/platform/riscv/src/timer.rs),
a [serial port](https://github.com/diodesign/diosix/blob/master/src/platform/riscv/src/serial.rs) for debug output,
[ending unit tests](https://github.com/diodesign/diosix/blob/master/src/platform/riscv/src/test.rs) in Qemu, and accessing and controlling
[physical memory](https://github.com/diodesign/diosix/blob/master/src/platform/riscv/src/physmem.rs). These are hardware specific and beyond the scope
of this porting overview.

**The specifics of the methods and structures that need to be implemented will be described in the reference documentation below.**

*/

#![no_std]
#![feature(asm)]

/* expose architecture common code to platform-specific code */
#[macro_use]
pub mod csr;
pub mod physmem;
pub mod devicetree;
pub mod irq;
pub mod cpu;
pub mod timer;
pub mod serial;
pub mod test;<|endoftext|>


<commit_before><commit_msg>加入出错代码,以供参考.<commit_after><|endoftext|>