<commit_before>use directories::ProjectDirs;
use gettextrs::gettext;
use ron;

use std::fs::{create_dir_all, File};
use std::io::Write;
use std::ops::{Deref, DerefMut};
use std::path::PathBuf;
use std::sync::RwLock;

use super::{TLD, SLD};

const CONFIG_FILENAME: &str = "config.ron";

lazy_static! {
    pub static ref CONFIG: RwLock<Config> = RwLock::new(Config::new());
}

#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
pub struct UI {
    pub width: i32,
    pub height: i32,
    pub is_chapters_list_hidden: bool,
}

#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
pub struct Media {
    pub is_gl_disable: bool,
}

#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
pub struct GlobalConfig {
    pub ui: UI,
    pub media: Media,
}

pub struct Config {
    path: PathBuf,
    last: GlobalConfig,
    current: GlobalConfig,
}

impl Config {
    fn new() -> Config {
        let project_dirs = ProjectDirs::from(TLD, SLD, env!("CARGO_PKG_NAME"));
        let config_dir = project_dirs.config_dir();
        create_dir_all(&config_dir).unwrap();
        let path = config_dir.join(CONFIG_FILENAME).to_owned();

        let last = match File::open(&path) {
            Ok(config_file) => {
                let config: Result<GlobalConfig, ron::de::Error> = ron::de::from_reader(config_file);
                match config {
                    Ok(config) => {
                        debug!("read config: {:?}", config);
                        config
                    }
                    Err(err) => {
                        error!("{}",
                            &gettext("couldn't load configuration: {}")
                                .replacen("{}", &format!("{:?}", err), 1),
                        );
                        GlobalConfig::default()
                    }
                }
            }
            Err(_) => GlobalConfig::default(),
        };

        Config {
            path,
            current: last.clone(),
            last,
        }
    }

    pub fn save(&mut self) {
        if self.last == self.current {
            // unchanged => don't save
            return;
        }

        match File::create(&self.path) {
            Ok(mut config_file) => {
                match ron::ser::to_string_pretty(
                    &self.current,
                    ron::ser::PrettyConfig::default(),
                ) {
                    Ok(config_str) => match config_file.write_all(config_str.as_bytes()) {
                        Ok(()) => {
                            self.last = self.current.clone();
                            debug!("saved config: {:?}", self.current);
                        }
                        Err(err) => {
                            error!("{}",
                                &gettext("couldn't write configuration: {}")
                                    .replacen("{}", &format!("{:?}", err), 1),
                            );
                        }
                    }
                    Err(err) => {
                        error!("{}",
                            &gettext("couldn't serialize configuration: {}")
                                .replacen("{}", &format!("{:?}", err), 1),
                        );
                    }
                }
            }
            Err(err) => {
                error!("{}",
                    &gettext("couldn't create configuration file: {}")
                        .replacen("{}", &format!("{:?}", err), 1),
                );
            }
        }
    }
}

impl Deref for Config {
    type Target = GlobalConfig;

    fn deref(&self) -> &Self::Target {
        &self.current
    }
}

impl DerefMut for Config {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.current
    }
}<commit_msg>Swap Config & GlobalConfig (#122)<commit_after>use directories::ProjectDirs;
use gettextrs::gettext;
use ron;

use std::fs::{create_dir_all, File};
use std::io::Write;
use std::ops::{Deref, DerefMut};
use std::path::PathBuf;
use std::sync::RwLock;

use super::{TLD, SLD};

const CONFIG_FILENAME: &str = "config.ron";

lazy_static! {
    pub static ref CONFIG: RwLock<GlobalConfig> = RwLock::new(GlobalConfig::new());
}

#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
pub struct UI {
    pub width: i32,
    pub height: i32,
    pub is_chapters_list_hidden: bool,
}

#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
pub struct Media {
    pub is_gl_disable: bool,
}

#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
pub struct Config {
    pub ui: UI,
    pub media: Media,
}

pub struct GlobalConfig {
    path: PathBuf,
    last: Config,
    current: Config,
}

impl GlobalConfig {
    fn new() -> GlobalConfig {
        let project_dirs = ProjectDirs::from(TLD, SLD, env!("CARGO_PKG_NAME"));
        let config_dir = project_dirs.config_dir();
        create_dir_all(&config_dir).unwrap();
        let path = config_dir.join(CONFIG_FILENAME).to_owned();

        let last = match File::open(&path) {
            Ok(config_file) => {
                let config: Result<Config, ron::de::Error> = ron::de::from_reader(config_file);
                match config {
                    Ok(config) => {
                        debug!("read config: {:?}", config);
                        config
                    }
                    Err(err) => {
                        error!("{}",
                            &gettext("couldn't load configuration: {}")
                                .replacen("{}", &format!("{:?}", err), 1),
                        );
                        Config::default()
                    }
                }
            }
            Err(_) => Config::default(),
        };

        GlobalConfig {
            path,
            current: last.clone(),
            last,
        }
    }

    pub fn save(&mut self) {
        if self.last == self.current {
            // unchanged => don't save
            return;
        }

        match File::create(&self.path) {
            Ok(mut config_file) => {
                match ron::ser::to_string_pretty(
                    &self.current,
                    ron::ser::PrettyConfig::default(),
                ) {
                    Ok(config_str) => match config_file.write_all(config_str.as_bytes()) {
                        Ok(()) => {
                            self.last = self.current.clone();
                            debug!("saved config: {:?}", self.current);
                        }
                        Err(err) => {
                            error!("{}",
                                &gettext("couldn't write configuration: {}")
                                    .replacen("{}", &format!("{:?}", err), 1),
                            );
                        }
                    }
                    Err(err) => {
                        error!("{}",
                            &gettext("couldn't serialize configuration: {}")
                                .replacen("{}", &format!("{:?}", err), 1),
                        );
                    }
                }
            }
            Err(err) => {
                error!("{}",
                    &gettext("couldn't create configuration file: {}")
                        .replacen("{}", &format!("{:?}", err), 1),
                );
            }
        }
    }
}

impl Deref for GlobalConfig {
    type Target = Config;

    fn deref(&self) -> &Self::Target {
        &self.current
    }
}

impl DerefMut for GlobalConfig {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.current
    }
}<|endoftext|>


<commit_before>//! Shell completion machinery

pub mod utils;

use std::ffi::OsString;
use std::fs::File;
use std::io::Error;
use std::io::Write;
use std::path::PathBuf;

use clap::App;

/// Generator trait which can be used to write generators
pub trait Generator {
    /// Returns the file name that is created when this generator is called during compile time.
    ///
    /// # Examples
    ///
    /// ```
    /// # use std::io::Write;
    /// # use clap::App;
    /// use clap_complete::Generator;
    ///
    /// pub struct Fish;
    ///
    /// impl Generator for Fish {
    /// #   fn generate(&self, app: &App, buf: &mut dyn Write) {}
    ///     fn file_name(&self, name: &str) -> String {
    ///         format!("{}.fish", name)
    ///     }
    /// }
    /// ```
    fn file_name(&self, name: &str) -> String;

    /// Generates output out of [`clap::App`](App).
    ///
    /// # Examples
    ///
    /// The following example generator displays the [`clap::App`](App)
    /// as if it is printed using [`std::println`].
    ///
    /// ```
    /// use std::{io::Write, fmt::write};
    /// use clap::App;
    /// use clap_complete::Generator;
    ///
    /// pub struct ClapDebug;
    ///
    /// impl Generator for ClapDebug {
    ///     fn generate(&self, app: &App, buf: &mut dyn Write) {
    ///         write!(buf, "{}", app).unwrap();
    ///     }
    /// #   fn file_name(&self, name: &str) -> String {
    /// #    name.into()
    /// #   }
    /// }
    /// ```
    fn generate(&self, app: &App, buf: &mut dyn Write);
}

/// Generate a completions file for a specified shell at compile-time.
///
/// **NOTE:** to generate the file at compile time you must use a `build.rs` "Build Script" or a
/// [`cargo-xtask`](https://github.com/matklad/cargo-xtask)
///
/// # Examples
///
/// The following example generates a bash completion script via a `build.rs` script. In this
/// simple example, we'll demo a very small application with only a single subcommand and two
/// args. Real applications could be many multiple levels deep in subcommands, and have tens or
/// potentially hundreds of arguments.
///
/// First, it helps if we separate out our `App` definition into a separate file. Whether you
/// do this as a function, or bare App definition is a matter of personal preference.
///
/// ```
/// // src/cli.rs
///
/// use clap::{App, Arg};
///
/// pub fn build_cli() -> App<'static> {
///     App::new("compl")
///         .about("Tests completions")
///         .arg(Arg::new("file")
///             .help("some input file"))
///         .subcommand(App::new("test")
///             .about("tests things")
///             .arg(Arg::new("case")
///                 .long("case")
///                 .takes_value(true)
///                 .help("the case to test")))
/// }
/// ```
///
/// In our regular code, we can simply call this `build_cli()` function, then call
/// `get_matches()`, or any of the other normal methods directly after. For example:
///
/// ```ignore
/// // src/main.rs
///
/// mod cli;
///
/// fn main() {
///     let _m = cli::build_cli().get_matches();
///
///     // normal logic continues...
/// }
/// ```
///
/// Next, we set up our `Cargo.toml` to use a `build.rs` build script.
///
/// ```toml
/// # Cargo.toml
/// build = "build.rs"
///
/// [dependencies]
/// clap = "*"
///
/// [build-dependencies]
/// clap = "*"
/// clap_complete = "*"
/// ```
///
/// Next, we place a `build.rs` in our project root.
///
/// ```ignore
/// use clap_complete::{generate_to, shells::Bash};
/// use std::env;
/// use std::io::Error;
///
/// include!("src/cli.rs");
///
/// fn main() -> Result<(), Error> {
///     let outdir = match env::var_os("OUT_DIR") {
///         None => return Ok(()),
///         Some(outdir) => outdir,
///     };
///
///     let mut app = build_cli();
///     let path = generate_to(
///         Bash,
///         &mut app, // We need to specify what generator to use
///         "myapp",  // We need to specify the bin name manually
///         outdir,   // We need to specify where to write to
///     )?;
///
///     println!("cargo:warning=completion file is generated: {:?}", path);
///
///     Ok(())
/// }
/// ```
///
/// Now, once we compile there will be a `{bin_name}.bash` file in the directory.
/// Assuming we compiled with debug mode, it would be somewhere similar to
/// `<project>/target/debug/build/myapp-<hash>/out/myapp.bash`.
///
/// **NOTE:** Please look at the individual [shells][crate::shells]
/// to see the name of the files generated.
pub fn generate_to<G, S, T>(
    gen: G,
    app: &mut clap::App,
    bin_name: S,
    out_dir: T,
) -> Result<PathBuf, Error>
where
    G: Generator,
    S: Into<String>,
    T: Into<OsString>,
{
    app.set_bin_name(bin_name);

    let out_dir = PathBuf::from(out_dir.into());
    let file_name = gen.file_name(app.get_bin_name().unwrap());

    let path = out_dir.join(file_name);
    let mut file = File::create(&path)?;

    _generate::<G, S>(gen, app, &mut file);
    Ok(path)
}

/// Generate a completions file for a specified shell at runtime.
///
/// Until `cargo install` can install extra files like a completion script, this may be
/// used e.g. in a command that outputs the contents of the completion script, to be
/// redirected into a file by the user.
///
/// # Examples
///
/// Assuming a separate `cli.rs` like the [example above](generate_to()),
/// we can let users generate a completion script using a command:
///
/// ```ignore
/// // src/main.rs
///
/// mod cli;
/// use std::io;
/// use clap_complete::{generate, shells::Bash};
///
/// fn main() {
///     let matches = cli::build_cli().get_matches();
///
///     if matches.is_present("generate-bash-completions") {
///         generate(Bash, &mut cli::build_cli(), "myapp", &mut io::stdout());
///     }
///
///     // normal logic continues...
/// }
///
/// ```
///
/// Usage:
///
/// ```shell
/// $ myapp generate-bash-completions > /usr/share/bash-completion/completions/myapp.bash
/// ```
pub fn generate<G, S>(gen: G, app: &mut clap::App, bin_name: S, buf: &mut dyn Write)
where
    G: Generator,
    S: Into<String>,
{
    app.set_bin_name(bin_name);
    _generate::<G, S>(gen, app, buf)
}

fn _generate<G, S>(gen: G, app: &mut clap::App, buf: &mut dyn Write)
where
    G: Generator,
    S: Into<String>,
{
    app._build_all();

    gen.generate(app, buf)
}
<commit_msg>docs(complete): Clarify some panic conditions<commit_after>//! Shell completion machinery

pub mod utils;

use std::ffi::OsString;
use std::fs::File;
use std::io::Error;
use std::io::Write;
use std::path::PathBuf;

use clap::App;

/// Generator trait which can be used to write generators
pub trait Generator {
    /// Returns the file name that is created when this generator is called during compile time.
    ///
    /// # Panics
    ///
    /// May panic when called outside of the context of [`generate`] or [`generate_to`]
    ///
    /// # Examples
    ///
    /// ```
    /// # use std::io::Write;
    /// # use clap::App;
    /// use clap_complete::Generator;
    ///
    /// pub struct Fish;
    ///
    /// impl Generator for Fish {
    /// #   fn generate(&self, app: &App, buf: &mut dyn Write) {}
    ///     fn file_name(&self, name: &str) -> String {
    ///         format!("{}.fish", name)
    ///     }
    /// }
    /// ```
    fn file_name(&self, name: &str) -> String;

    /// Generates output out of [`clap::App`](App).
    ///
    /// # Panics
    ///
    /// May panic when called outside of the context of [`generate`] or [`generate_to`]
    ///
    /// # Examples
    ///
    /// The following example generator displays the [`clap::App`](App)
    /// as if it is printed using [`std::println`].
    ///
    /// ```
    /// use std::{io::Write, fmt::write};
    /// use clap::App;
    /// use clap_complete::Generator;
    ///
    /// pub struct ClapDebug;
    ///
    /// impl Generator for ClapDebug {
    ///     fn generate(&self, app: &App, buf: &mut dyn Write) {
    ///         write!(buf, "{}", app).unwrap();
    ///     }
    /// #   fn file_name(&self, name: &str) -> String {
    /// #    name.into()
    /// #   }
    /// }
    /// ```
    fn generate(&self, app: &App, buf: &mut dyn Write);
}

/// Generate a completions file for a specified shell at compile-time.
///
/// **NOTE:** to generate the file at compile time you must use a `build.rs` "Build Script" or a
/// [`cargo-xtask`](https://github.com/matklad/cargo-xtask)
///
/// # Examples
///
/// The following example generates a bash completion script via a `build.rs` script. In this
/// simple example, we'll demo a very small application with only a single subcommand and two
/// args. Real applications could be many multiple levels deep in subcommands, and have tens or
/// potentially hundreds of arguments.
///
/// First, it helps if we separate out our `App` definition into a separate file. Whether you
/// do this as a function, or bare App definition is a matter of personal preference.
///
/// ```
/// // src/cli.rs
///
/// use clap::{App, Arg};
///
/// pub fn build_cli() -> App<'static> {
///     App::new("compl")
///         .about("Tests completions")
///         .arg(Arg::new("file")
///             .help("some input file"))
///         .subcommand(App::new("test")
///             .about("tests things")
///             .arg(Arg::new("case")
///                 .long("case")
///                 .takes_value(true)
///                 .help("the case to test")))
/// }
/// ```
///
/// In our regular code, we can simply call this `build_cli()` function, then call
/// `get_matches()`, or any of the other normal methods directly after. For example:
///
/// ```ignore
/// // src/main.rs
///
/// mod cli;
///
/// fn main() {
///     let _m = cli::build_cli().get_matches();
///
///     // normal logic continues...
/// }
/// ```
///
/// Next, we set up our `Cargo.toml` to use a `build.rs` build script.
///
/// ```toml
/// # Cargo.toml
/// build = "build.rs"
///
/// [dependencies]
/// clap = "*"
///
/// [build-dependencies]
/// clap = "*"
/// clap_complete = "*"
/// ```
///
/// Next, we place a `build.rs` in our project root.
///
/// ```ignore
/// use clap_complete::{generate_to, shells::Bash};
/// use std::env;
/// use std::io::Error;
///
/// include!("src/cli.rs");
///
/// fn main() -> Result<(), Error> {
///     let outdir = match env::var_os("OUT_DIR") {
///         None => return Ok(()),
///         Some(outdir) => outdir,
///     };
///
///     let mut app = build_cli();
///     let path = generate_to(
///         Bash,
///         &mut app, // We need to specify what generator to use
///         "myapp",  // We need to specify the bin name manually
///         outdir,   // We need to specify where to write to
///     )?;
///
///     println!("cargo:warning=completion file is generated: {:?}", path);
///
///     Ok(())
/// }
/// ```
///
/// Now, once we compile there will be a `{bin_name}.bash` file in the directory.
/// Assuming we compiled with debug mode, it would be somewhere similar to
/// `<project>/target/debug/build/myapp-<hash>/out/myapp.bash`.
///
/// **NOTE:** Please look at the individual [shells][crate::shells]
/// to see the name of the files generated.
pub fn generate_to<G, S, T>(
    gen: G,
    app: &mut clap::App,
    bin_name: S,
    out_dir: T,
) -> Result<PathBuf, Error>
where
    G: Generator,
    S: Into<String>,
    T: Into<OsString>,
{
    app.set_bin_name(bin_name);

    let out_dir = PathBuf::from(out_dir.into());
    let file_name = gen.file_name(app.get_bin_name().unwrap());

    let path = out_dir.join(file_name);
    let mut file = File::create(&path)?;

    _generate::<G, S>(gen, app, &mut file);
    Ok(path)
}

/// Generate a completions file for a specified shell at runtime.
///
/// Until `cargo install` can install extra files like a completion script, this may be
/// used e.g. in a command that outputs the contents of the completion script, to be
/// redirected into a file by the user.
///
/// # Examples
///
/// Assuming a separate `cli.rs` like the [example above](generate_to()),
/// we can let users generate a completion script using a command:
///
/// ```ignore
/// // src/main.rs
///
/// mod cli;
/// use std::io;
/// use clap_complete::{generate, shells::Bash};
///
/// fn main() {
///     let matches = cli::build_cli().get_matches();
///
///     if matches.is_present("generate-bash-completions") {
///         generate(Bash, &mut cli::build_cli(), "myapp", &mut io::stdout());
///     }
///
///     // normal logic continues...
/// }
///
/// ```
///
/// Usage:
///
/// ```shell
/// $ myapp generate-bash-completions > /usr/share/bash-completion/completions/myapp.bash
/// ```
pub fn generate<G, S>(gen: G, app: &mut clap::App, bin_name: S, buf: &mut dyn Write)
where
    G: Generator,
    S: Into<String>,
{
    app.set_bin_name(bin_name);
    _generate::<G, S>(gen, app, buf)
}

fn _generate<G, S>(gen: G, app: &mut clap::App, buf: &mut dyn Write)
where
    G: Generator,
    S: Into<String>,
{
    app._build_all();

    gen.generate(app, buf)
}
<|endoftext|>


<commit_before>extern crate polydraw;

use std::cmp::Ordering;

use polydraw::{Application, Renderer, Frame};
use polydraw::draw::{RGB, bresenham};
use polydraw::geom::point::Point;
use polydraw::geom::polygon::Polygon;

struct PolygonRenderer {
   polygon: Polygon,
}

impl PolygonRenderer {
   fn new() -> Self {
      let mult = 50;
      let mut polygon = Polygon::new(vec![
         Point::new(2 * mult, 9 * mult),
         Point::new(13 * mult, 12 * mult),
         Point::new(9 * mult, 3 * mult),
         Point::new(7 * mult, 2 * mult),
      ]);

      PolygonRenderer {
         polygon: polygon
      }
   }

   fn printall(&self) {
      let mut prev = self.polygon.points[self.polygon.points.len() - 1];

      for point in &self.polygon.points {
         if cmp_points(&prev, &point) == Ordering::Less {
            println!("{} {} - {} {}", prev.x, prev.y, point.x, point.y);
         } else {
            println!("{} {} - {} {}", point.x, point.y, prev.x, prev.y);
         }
         prev = point.clone();
      }
   }
}

impl Renderer for PolygonRenderer {
   fn render(&mut self, frame: &mut Frame) {
      frame.clear();

      let color = RGB::new(127, 223, 255);

      let mut prev = self.polygon.points[self.polygon.points.len() - 1];

      for point in &self.polygon.points {
         bresenham(frame, prev.x as i32, prev.y as i32, point.x as i32, point.y as i32, &color);
         prev = point.clone();
      }
   }
}

fn cmp_points(pt1: &Point, pt2: &Point) -> Ordering {
   if pt1.y < pt2.y {
      Ordering::Less
   } else if pt1.y > pt2.y {
      Ordering::Greater
   } else if pt1.x < pt2.x {
      Ordering::Less
   } else if pt1.x > pt2.x {
      Ordering::Greater
   } else {
      Ordering::Equal
   }
}

fn main() {
   let mut renderer = PolygonRenderer::new();

   renderer.printall();

   Application::new()
      .renderer(&mut renderer)
      .title("Polygon")
      .run();
}
<commit_msg>Mutability warning fix<commit_after>extern crate polydraw;

use std::cmp::Ordering;

use polydraw::{Application, Renderer, Frame};
use polydraw::draw::{RGB, bresenham};
use polydraw::geom::point::Point;
use polydraw::geom::polygon::Polygon;

struct PolygonRenderer {
   polygon: Polygon,
}

impl PolygonRenderer {
   fn new() -> Self {
      let mult = 50;
      let polygon = Polygon::new(vec![
         Point::new(2 * mult, 9 * mult),
         Point::new(13 * mult, 12 * mult),
         Point::new(9 * mult, 3 * mult),
         Point::new(7 * mult, 2 * mult),
      ]);

      PolygonRenderer {
         polygon: polygon
      }
   }

   fn printall(&self) {
      let mut prev = self.polygon.points[self.polygon.points.len() - 1];

      for point in &self.polygon.points {
         if cmp_points(&prev, &point) == Ordering::Less {
            println!("{} {} - {} {}", prev.x, prev.y, point.x, point.y);
         } else {
            println!("{} {} - {} {}", point.x, point.y, prev.x, prev.y);
         }
         prev = point.clone();
      }
   }
}

impl Renderer for PolygonRenderer {
   fn render(&mut self, frame: &mut Frame) {
      frame.clear();

      let color = RGB::new(127, 223, 255);

      let mut prev = self.polygon.points[self.polygon.points.len() - 1];

      for point in &self.polygon.points {
         bresenham(frame, prev.x as i32, prev.y as i32, point.x as i32, point.y as i32, &color);
         prev = point.clone();
      }
   }
}

fn cmp_points(pt1: &Point, pt2: &Point) -> Ordering {
   if pt1.y < pt2.y {
      Ordering::Less
   } else if pt1.y > pt2.y {
      Ordering::Greater
   } else if pt1.x < pt2.x {
      Ordering::Less
   } else if pt1.x > pt2.x {
      Ordering::Greater
   } else {
      Ordering::Equal
   }
}

fn main() {
   let mut renderer = PolygonRenderer::new();

   renderer.printall();

   Application::new()
      .renderer(&mut renderer)
      .title("Polygon")
      .run();
}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![feature(custom_derive)]
#![plugin(serde_macros)]

extern crate docopt;
extern crate env_logger;
extern crate raft;
extern crate serde;
extern crate rustc_serialize;

use std::net::SocketAddr;
use std::str::FromStr;
use std::io::{Error, Result};
use std::collections::HashMap;

use serde::json::{self, Value};
use docopt::Docopt;

use raft::{
    state_machine,
    persistent_log,
    ServerId,
    Server,
    Client,
};

static USAGE: &'static str = "
A replicated mutable value. Operations on the register have serializable
consistency, but no durability (once all register servers are terminated the
value is lost).

Each register server holds a replica of the register, and coordinates with its
peers to update the register's value according to client commands. The register
is available for reading and writing only if a majority of register servers are
available.

Commands:

  get     Returns the current value of the register.

  put     Sets the current value of the register, and returns the previous
          value.

  cas     (compare and set) Conditionally sets the value of the register if the
          current value matches an expected value, returning true if the
          register was set.

  server  Starts a register server. Servers must be provided a unique ID and
          address (ip:port) at startup, along with the ID and address of all
          peer servers.

Usage:
  register get (<server-address>)...
  register put <new-value> (<server-address>)...
  register cas <expected-value> <new-value> (<server-address>)...
  register server <id> <address> [<peer-id> <peer-address>]...
  register (-h | --help)

Options:
  -h --help   Show a help message.
";

#[derive(Debug, RustcDecodable)]
struct Args {
    cmd_server: bool,
    cmd_get: bool,
    cmd_put: bool,
    cmd_cas: bool,

    arg_id: Option<u64>,
    arg_address: String,
    arg_peer_id: Vec<u64>,
    arg_peer_address: Vec<String>,
    arg_server_address: Vec<String>,

    arg_new_value: String,
    arg_expected_value: String,
}

fn main() {
    let _ = env_logger::init();
    let args: Args = Docopt::new(USAGE)
                            .and_then(|d| d.decode())
                            .unwrap_or_else(|e| e.exit());
    if args.cmd_server {
        server(&args);
    } else if args.cmd_get {
        get(&args);
    } else if args.cmd_put {
        put(&args);
    } else if args.cmd_cas {
        cas(&args);
    }
}

fn parse_addr(addr: &str) -> SocketAddr {
    SocketAddr::from_str(addr)
               .ok()
               .expect(&format!("unable to parse socket address: {}", addr))
}

fn server(args: &Args) {
    let persistent_log = persistent_log::MemLog::new();
    let state_machine = HashmapStateMachine::new();

    let id = ServerId::from(args.arg_id.unwrap());
    let addr = parse_addr(&args.arg_address);
    let peers = args.arg_peer_id
                    .iter()
                    .zip(args.arg_peer_address.iter())
                    .map(|(&id, addr)| (ServerId::from(id), parse_addr(&addr)))
                    .collect();

    Server::run(id, addr, peers, persistent_log, state_machine).unwrap();
}

fn get(args: &Args) {
    let cluster = args.arg_server_address.iter()
        .map(|v| parse_addr(&v))
        .collect();
    let mut client = Client::new(cluster);
    let response = client.query(args.arg_new_value.as_bytes()).unwrap();
    println!("{}", String::from_utf8(response).unwrap())
}

fn put(args: &Args) {
    let cluster = args.arg_server_address.iter()
        .map(|v| parse_addr(&v))
        .collect();
    let mut client = Client::new(cluster);
    let response = client.propose(args.arg_new_value.as_bytes()).unwrap();
    println!("{}", String::from_utf8(response).unwrap())
}

fn cas(_args: &Args) {
    panic!("unimplemented: waiting on changes to the Raft Client and StateMachine APIs");
}

/// A state machine that holds a hashmap.
#[derive(Debug)]
pub struct HashmapStateMachine {
    map: HashMap<String, Value>,
}

/// Generally maps (key, value)
#[derive(Serialize, Deserialize)]
pub struct Message(String, Option<Value>);

impl HashmapStateMachine {
    pub fn new() -> HashmapStateMachine {
        HashmapStateMachine {
            map: HashMap::new(),
        }
    }
}

impl state_machine::StateMachine for HashmapStateMachine {

    type Error = Error;

    fn apply(&mut self, new_value: &[u8]) -> Result<Vec<u8>> {
        let string = String::from_utf8_lossy(new_value);
        let Message(key, value) = json::from_str(&string).unwrap();

        let response = json::to_string(&match value {
            Some(v) => (key.clone(), self.map.insert(key, v)),
            None => (key.clone(), self.map.remove(&key)),
        }).unwrap();

        Ok(response.into_bytes())
    }

    fn query(&self, query: &[u8]) -> Result<Vec<u8>> {
        let string = String::from_utf8_lossy(query);
        let Message(key, _) = json::from_str(&string).unwrap();

        let response = json::to_string(&self.map.get(&key).map(|v| v.clone())).unwrap();

        Ok(response.into_bytes())
    }

    fn snapshot(&self) -> Result<Vec<u8>> {
        Ok(json::to_string(&self.map)
            .unwrap()
            .into_bytes())
    }

    fn restore_snapshot(&mut self, snapshot_value: Vec<u8>) -> Result<()> {
        self.map = json::from_str(&String::from_utf8_lossy(&snapshot_value)).unwrap();
        Ok(())
    }
}
<commit_msg>Improve hashmap<commit_after>#![feature(plugin)]
#![feature(custom_derive)]
#![plugin(serde_macros)]

extern crate docopt;
extern crate env_logger;
extern crate raft;
extern crate serde;
extern crate rustc_serialize;

use std::net::SocketAddr;
use std::str::FromStr;
use std::io::{Error, Result};
use std::collections::HashMap;
use std::fmt::Debug;
use std::hash::Hash;

use serde::json::{self, Value};
use serde::{Serialize, Deserialize};
use docopt::Docopt;

use raft::{
    state_machine,
    persistent_log,
    ServerId,
    Server,
    Client,
};
use Message::*;

static USAGE: &'static str = "
A replicated mutable value. Operations on the register have serializable
consistency, but no durability (once all register servers are terminated the
value is lost).

Each register server holds a replica of the register, and coordinates with its
peers to update the register's value according to client commands. The register
is available for reading and writing only if a majority of register servers are
available.

Commands:

  get     Returns the current value of the key.

  put     Sets the current value of the key, and returns the previous
          value.

  cas     (compare and set) Conditionally sets the value of the key if the
          current value matches an expected value, returning true if the
          key was set.

  server  Starts a key server. Servers must be provided a unique ID and
          address (ip:port) at startup, along with the ID and address of all
          peer servers.register

Usage:
  hashmap get <key> (<node-address>)...
  hashmap put <key> <new-value> (<node-address>)...
  hashmap cas <key> <expected-value> <new-value> (<node-address>)...
  hashmap server <id> [<node-id> <node-address>]...
  hashmap (-h | --help)

Options:
  -h --help   Show a help message.
";

#[derive(Debug, RustcDecodable)]
struct Args {
    cmd_server: bool,
    cmd_get: bool,
    cmd_put: bool,
    cmd_cas: bool,

    arg_id: Option<u64>,
    arg_node_id: Vec<u64>,
    arg_node_address: Vec<String>,
    arg_server_id: Option<u64>,

    arg_key: String,
    arg_new_value: String,
    arg_expected_value: String,
}

fn main() {
    let _ = env_logger::init();
    let args: Args = Docopt::new(USAGE)
                            .and_then(|d| d.decode())
                            .unwrap_or_else(|e| e.exit());
    if args.cmd_server {
        server(&args);
    } else if args.cmd_get {
        get(&args);
    } else if args.cmd_put {
        put(&args);
    } else if args.cmd_cas {
        cas(&args);
    }
}

fn parse_addr(addr: &str) -> SocketAddr {
    SocketAddr::from_str(addr)
               .ok()
               .expect(&format!("unable to parse socket address: {}", addr))
}

fn server(args: &Args) {
    let persistent_log = persistent_log::MemLog::new();
    let state_machine = HashmapStateMachine::new();

    let id = ServerId::from(args.arg_id.unwrap());
    let mut peers = args.arg_node_id
                    .iter()
                    .zip(args.arg_node_address.iter())
                    .map(|(&id, addr)| (ServerId::from(id), parse_addr(&addr)))
                    .collect::<HashMap<_,_>>();

    let addr = peers.remove(&id).unwrap();
    Server::run(id, addr, peers, persistent_log, state_machine).unwrap();
}

fn get(args: &Args) {
    let cluster = args.arg_node_address.iter()
        .map(|v| parse_addr(&v))
        .collect();
    let mut client = Client::new(cluster);
    let payload = json::to_string(&Message::Get(args.arg_key.clone())).unwrap();
    let response = client.query(payload.as_bytes()).unwrap();
    println!("{}", String::from_utf8(response).unwrap())
}

fn put(args: &Args) {
    let cluster = args.arg_node_address.iter()
        .map(|v| parse_addr(&v))
        .collect();
    let mut client = Client::new(cluster);
    let new_value = json::to_value(&args.arg_new_value);
    let payload = json::to_string(&Message::Put(args.arg_key.clone(), new_value)).unwrap();
    let response = client.propose(payload.as_bytes()).unwrap();
    println!("{}", String::from_utf8(response).unwrap())
}

fn cas(_args: &Args) {
    panic!("unimplemented: waiting on changes to the Raft Client and StateMachine APIs");
}

/// A state machine that holds a hashmap.
#[derive(Debug)]
pub struct HashmapStateMachine {
    map: HashMap<String, Value>,
}

#[derive(Serialize, Deserialize)]
pub enum Message {
    Get(String),
    Put(String, Value),
    Cas(String, Value, Value),
}

impl HashmapStateMachine {
    pub fn new() -> HashmapStateMachine {
        HashmapStateMachine {
            map: HashMap::new(),
        }
    }
}

impl state_machine::StateMachine for HashmapStateMachine {

    type Error = Error;

    fn apply(&mut self, new_value: &[u8]) -> Result<Vec<u8>> {
        let string = String::from_utf8_lossy(new_value);
        let message = json::from_str(&string).unwrap();

        let response = match message {
            Get(key) => {
                let old_value = &self.map.get(&key).map(|v| v.clone());
                json::to_string(old_value)
            },
            Put(key, value) => {
                let old_value = &self.map.insert(key, value);
                json::to_string(old_value)
            },
            Cas(key, old_check, new) => {
                if *self.map.get(&key).unwrap() == old_check {
                    let _ = self.map.insert(key, new);
                    json::to_string(&true)
                } else {
                    json::to_string(&false)
                }
            },
        };

        Ok(response.unwrap().into_bytes())
    }

    fn query(&self, query: &[u8]) -> Result<Vec<u8>> {
        let string = String::from_utf8_lossy(query);
        let message = json::from_str(&string).unwrap();

        let response = match message {
            Get(key) => {
                let old_value = &self.map.get(&key).map(|v| v.clone());
                json::to_string(old_value)
            },
            _ => panic!("Can't do mutating requests in query"),
        };

        Ok(response.unwrap().into_bytes())
    }

    fn snapshot(&self) -> Result<Vec<u8>> {
        Ok(json::to_string(&self.map)
            .unwrap()
            .into_bytes())
    }

    fn restore_snapshot(&mut self, snapshot_value: Vec<u8>) -> Result<()> {
        self.map = json::from_str(&String::from_utf8_lossy(&snapshot_value)).unwrap();
        Ok(())
    }
}
<|endoftext|>


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

//! Tasks scheduled on a GJ event loop are not preemptive. For an event loop to make progress,
//! event callbacks must yield control by returning.
//!
//! GJ event loops are thread-local. To take advantage of multiprocessor hardware or to deal with
//! tasks that cannot easily yield, you can send tasks to separate threads where they will execute
//! on separate event loops. The example program illustrates how that might work, using
//! `std::thread::sleep_ms()` as a stand-in for a blocking computation.

#![cfg(unix)]

extern crate gj;
use gj::Promise;
use gj::io::{AsyncRead, AsyncWrite};
use std::time::Duration;

fn child_loop(delay: Duration,
              stream: gj::io::unix::Stream,
              buf: Vec<u8>) -> gj::Promise<(), gj::io::Error<(gj::io::unix::Stream, Vec<u8>)>> {

    // This blocks the entire thread. This is okay because we are on a child thread
    // where nothing else needs to happen.
    ::std::thread::sleep(delay);

    stream.write(buf).then(move |(stream, buf)| {
        child_loop(delay, stream, buf)
    })
}

fn child(delay: Duration) -> Result<gj::io::unix::Stream, Box<::std::error::Error>> {
    let (_, stream) = try!(gj::io::unix::spawn(move |parent_stream, wait_scope| {
        try!(child_loop(delay, parent_stream, vec![0u8]).lift::<Box<::std::error::Error>>().wait(wait_scope));
        Ok(())
    }));
    return Ok(stream);
}

fn listen_to_child(id: &'static str,
                   stream: gj::io::unix::Stream,
                   buf: Vec<u8>) -> gj::Promise<(), gj::io::Error<(gj::io::unix::Stream, Vec<u8>)>> {
    stream.read(buf, 1).then(move |(stream, buf, _n)| {
        println!("heard back from {}", id);
        listen_to_child(id, stream, buf)
    })
}

fn parent_wait_loop() -> Promise<(), ::std::io::Error> {
    println!("parent wait loop...");

    // If we used ::std::thread::sleep() here, we would block the main event loop.
    gj::io::Timer.after_delay(Duration::from_millis(3000)).then(|()| {
        parent_wait_loop()
    })
}

pub fn main() {
    gj::EventLoop::top_level(|wait_scope| {

        let children = vec![
            parent_wait_loop().lift::<Box<::std::error::Error>>(),
            listen_to_child("CHILD 1", try!(child(Duration::from_millis(700))), vec![0]).lift(),
            listen_to_child("CHILD 2", try!(child(Duration::from_millis(1900))), vec![0]).lift(),
            listen_to_child("CHILD 3", try!(child(Duration::from_millis(2600))), vec![0]).lift()];

        try!(Promise::all(children.into_iter()).wait(wait_scope));

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

//! Tasks scheduled on a GJ event loop are not preemptive. For an event loop to make progress,
//! event callbacks must yield control by returning.
//!
//! GJ event loops are thread-local. To take advantage of multiprocessor hardware or to deal with
//! tasks that cannot easily yield, you can send tasks to separate threads where they will execute
//! on separate event loops. The example program illustrates how that might work, using
//! `std::thread::sleep_ms()` as a stand-in for a blocking computation.

extern crate gj;
use gj::Promise;
use gj::io::{AsyncRead, AsyncWrite, unix};
use std::time::Duration;

fn child_loop(delay: Duration,
              stream: unix::Stream,
              buf: Vec<u8>) -> Promise<(), gj::io::Error<(unix::Stream, Vec<u8>)>> {

    // This blocks the entire thread. This is okay because we are on a child thread
    // where nothing else needs to happen.
    ::std::thread::sleep(delay);

    stream.write(buf).then(move |(stream, buf)| {
        child_loop(delay, stream, buf)
    })
}

fn child(delay: Duration) -> Result<unix::Stream, Box<::std::error::Error>> {
    let (_, stream) = try!(unix::spawn(move |parent_stream, wait_scope| {
        try!(child_loop(delay, parent_stream, vec![0u8]).lift::<Box<::std::error::Error>>().wait(wait_scope));
        Ok(())
    }));
    return Ok(stream);
}

fn listen_to_child(id: &'static str,
                   stream: unix::Stream,
                   buf: Vec<u8>) -> Promise<(), gj::io::Error<(unix::Stream, Vec<u8>)>> {
    stream.read(buf, 1).then(move |(stream, buf, _n)| {
        println!("heard back from {}", id);
        listen_to_child(id, stream, buf)
    })
}

fn parent_wait_loop() -> Promise<(), ::std::io::Error> {
    println!("parent wait loop...");

    // If we used ::std::thread::sleep() here, we would block the main event loop.
    gj::io::Timer.after_delay(Duration::from_millis(3000)).then(|()| {
        parent_wait_loop()
    })
}

pub fn main() {
    gj::EventLoop::top_level(|wait_scope| {

        let children = vec![
            parent_wait_loop().lift::<Box<::std::error::Error>>(),
            listen_to_child("CHILD 1", try!(child(Duration::from_millis(700))), vec![0]).lift(),
            listen_to_child("CHILD 2", try!(child(Duration::from_millis(1900))), vec![0]).lift(),
            listen_to_child("CHILD 3", try!(child(Duration::from_millis(2600))), vec![0]).lift()];

        try!(Promise::all(children.into_iter()).wait(wait_scope));

        Ok(())
    }).unwrap();
}
<|endoftext|>


<commit_before>//! Encode a `BlockContent` nodes to HTML

use super::{
    attr, attr_id, attr_itemprop, attr_itemtype, attr_prop, attr_slot, concat, elem, elem_empty,
    elem_meta, elem_placeholder, json, nothing, EncodeContext, ToHtml,
};
use html_escape::encode_safe;
use stencila_schema::*;

impl ToHtml for BlockContent {
    fn to_html(&self, context: &EncodeContext) -> String {
        match self {
            BlockContent::Claim(node) => node.to_html(context),
            BlockContent::CodeBlock(node) => node.to_html(context),
            BlockContent::CodeChunk(node) => node.to_html(context),
            BlockContent::Collection(node) => node.to_html(context),
            BlockContent::Figure(node) => node.to_html(context),
            BlockContent::Heading(node) => node.to_html(context),
            BlockContent::Include(node) => node.to_html(context),
            BlockContent::List(node) => node.to_html(context),
            BlockContent::MathBlock(node) => node.to_html(context),
            BlockContent::Paragraph(node) => node.to_html(context),
            BlockContent::QuoteBlock(node) => node.to_html(context),
            BlockContent::Table(node) => node.to_html(context),
            BlockContent::ThematicBreak(node) => node.to_html(context),
        }
    }
}

impl ToHtml for ClaimSimple {
    fn to_html(&self, _context: &EncodeContext) -> String {
        elem(
            "pre",
            &[
                attr_itemtype::<Self>(),
                attr_id(&self.id),
                attr("class", "todo"),
            ],
            &json(self),
        )
    }
}

impl ToHtml for CodeBlock {
    /// Encode a [`CodeBlock`] as HTML
    ///
    /// The `programming_language` is encoded as both a `class` attribute and a `<meta>` element.
    /// The `<meta>` element is for Microdata and Stencila WebComponent compatibility.
    /// The `class` follows the recommendation of [HTML5 spec](https://html.spec.whatwg.org/#the-code-element)
    /// to "use the class attribute, e.g. by adding a class prefixed with "language-" to the element."
    fn to_html(&self, _context: &EncodeContext) -> String {
        let (lang_class, lang_meta) = match &self.programming_language {
            Some(programming_language) => (
                attr("class", &["language-", programming_language].concat()),
                elem_meta("programmingLanguage", programming_language),
            ),
            None => (nothing(), nothing()),
        };

        let text = elem(
            "code",
            &[attr_itemprop("text"), lang_class],
            &encode_safe(&self.text),
        );

        elem(
            "pre",
            &[attr_itemtype::<Self>(), attr_id(&self.id)],
            &[lang_meta, text].concat(),
        )
    }
}

impl ToHtml for CodeChunk {
    fn to_html(&self, context: &EncodeContext) -> String {
        let text = elem(
            "pre",
            &[attr_prop("text"), attr_slot("text")],
            &self.text.to_html(context),
        );

        let lang_attr = attr("programming-language", &self.programming_language);
        let lang_meta = elem_meta("programmingLanguage", &self.programming_language);

        let outputs = elem_placeholder(
            "div",
            &[attr_prop("outputs"), attr_slot("outputs")],
            &self.outputs,
            context,
        );

        let errors = elem_placeholder(
            "div",
            &[attr_prop("errors"), attr_slot("errors")],
            &self.errors,
            context,
        );

        let label = elem_placeholder(
            "span",
            &[attr_prop("label"), attr_slot("label")],
            &self.label,
            context,
        );

        let caption = elem_placeholder(
            "figcaption",
            &[attr_prop("caption"), attr_slot("caption")],
            &self.caption,
            context,
        );

        elem(
            "stencila-code-chunk",
            &[attr_itemtype::<Self>(), attr_id(&self.id), lang_attr],
            &[lang_meta, text, outputs, errors, label, caption].concat(),
        )
    }
}

impl ToHtml for CodeChunkCaption {
    fn to_html(&self, context: &EncodeContext) -> String {
        match self {
            CodeChunkCaption::String(string) => string.to_html(context),
            CodeChunkCaption::VecBlockContent(blocks) => blocks.to_html(context),
        }
    }
}

/// Encode a code error to HTML
///
/// In the future the current `CodeError` is likely to be replaced by a `CodeMessage`
/// (and `messages` added as a property of code elements).
impl ToHtml for CodeError {
    fn to_html(&self, context: &EncodeContext) -> String {
        let kind = if self.error_message.to_lowercase().contains("warning")
            && self.stack_trace.is_none()
        {
            "warning"
        } else {
            "error"
        };

        let error_type = elem_placeholder(
            "span",
            &[attr_prop("errorType"), attr_slot("errortype")],
            &self.error_type,
            context,
        );

        let error_message = elem(
            "span",
            &[attr_prop("errorMessage"), attr_slot("errormessage")],
            &self.error_message.to_html(context),
        );

        let stack_trace = elem_placeholder(
            "pre",
            &[attr_prop("stackTrace"), attr_slot("stacktrace")],
            &self.stack_trace,
            context,
        );

        elem(
            "stencila-code-error",
            &[
                attr_itemtype::<Self>(),
                attr_id(&self.id),
                attr("kind", kind),
            ],
            &[error_type, error_message, stack_trace].concat(),
        )
    }
}

impl ToHtml for CollectionSimple {
    fn to_html(&self, context: &EncodeContext) -> String {
        elem(
            "ol",
            &[attr_itemtype::<Self>(), attr_id(&self.id)],
            &concat(&self.parts, |part| elem("li", &[], &part.to_html(context))),
        )
    }
}

/// Encode a figure as HTML
///
/// Similar to as for tables, except that the label and caption are at the bottom
/// (although themes should be able to move them) and are not grouped together in a `<caption>`
/// element as they are in a table.
impl ToHtml for FigureSimple {
    fn to_html(&self, context: &EncodeContext) -> String {
        let content = elem_placeholder("div", &[attr_prop("content")], &self.content, context);

        let label = elem_placeholder("span", &[attr_prop("label")], &self.label, context);

        let caption = elem_placeholder(
            "figcaption",
            &[attr_prop("caption")],
            &self.caption,
            context,
        );

        elem(
            "figure",
            &[attr_itemtype::<Figure>(), attr_id(&self.id)],
            &[content, label, caption].concat(),
        )
    }
}

impl ToHtml for FigureCaption {
    fn to_html(&self, context: &EncodeContext) -> String {
        match self {
            FigureCaption::String(string) => string.to_html(context),
            FigureCaption::VecBlockContent(blocks) => blocks.to_html(context),
        }
    }
}

impl ToHtml for Heading {
    /// Encode a `Heading` node to a `<h2>`, `<h3>` etc element.
    ///
    /// > Generally, it is a best practice to ensure that the beginning of a
    /// > page's main content starts with a h1 element, and also to ensure
    /// > that the page contains only one h1 element.
    /// > From https://dequeuniversity.com/rules/axe/3.5/page-has-heading-one
    ///
    /// This codec follows that recommendation and reserves `<h1>` for the
    /// `title` property of a creative work.
    ///
    /// In rare cases that there is no content in the heading, return an empty
    /// text node to avoid the 'Heading tag found with no content' accessibility error.
    fn to_html(&self, context: &EncodeContext) -> String {
        let depth = match &self.depth {
            Some(depth) => std::cmp::min(*depth + 1, 6),
            None => 2,
        };

        elem(
            &["h", &depth.to_string()].concat(),
            &[attr_itemtype::<Self>(), attr_id(&self.id)],
            &self.content.to_html(context),
        )
    }
}

impl ToHtml for Include {
    fn to_html(&self, context: &EncodeContext) -> String {
        let content = self
            .content
            .as_ref()
            .map_or_else(|| "".to_string(), |content| content.to_html(context));

        elem(
            "div",
            &[attr_itemtype::<Self>(), attr_id(&self.id)],
            &content,
        )
    }
}

impl ToHtml for List {
    fn to_html(&self, context: &EncodeContext) -> String {
        let tag = match &self.order {
            Some(ListOrder::Ascending) => "ol",
            _ => "ul",
        };

        elem(
            tag,
            &[attr_itemtype::<Self>(), attr_id(&self.id)],
            &self
                .items
                .iter()
                .map(|item| item.to_html(context))
                .collect::<Vec<String>>()
                .concat(),
        )
    }
}

impl ToHtml for ListItem {
    fn to_html(&self, context: &EncodeContext) -> String {
        let checkbox = self.is_checked.map(|is_checked| match is_checked {
            true => InlineContent::String("☑ ".to_string()),
            false => InlineContent::String("☐ ".to_string()),
        });

        let content = match &self.content {
            Some(content) => match content {
                ListItemContent::VecInlineContent(inlines) => match checkbox {
                    Some(checkbox) => [vec![checkbox], inlines.clone()].concat().to_html(context),
                    None => inlines.to_html(context),
                },
                ListItemContent::VecBlockContent(blocks) => match checkbox {
                    Some(checkbox) => {
                        // Check box is only added if the first block is a paragraph
                        if let Some(BlockContent::Paragraph(paragraph)) = blocks.first() {
                            let mut paragraph = paragraph.clone();
                            paragraph.content.insert(0, checkbox);
                            [
                                paragraph.to_html(context),
                                concat(&blocks[1..], |block| block.to_html(context)),
                            ]
                            .concat()
                        } else {
                            blocks.to_html(context)
                        }
                    }
                    None => blocks.to_html(context),
                },
            },
            None => "".to_string(),
        };

        elem(
            "li",
            &[attr_itemtype::<Self>(), attr_id(&self.id)],
            &content,
        )
    }
}

impl ToHtml for MathBlock {
    fn to_html(&self, _context: &EncodeContext) -> String {
        elem(
            "pre",
            &[
                attr_itemtype::<Self>(),
                attr_id(&self.id),
                attr("class", "todo"),
            ],
            &encode_safe(&self.text),
        )
    }
}

impl ToHtml for Paragraph {
    fn to_html(&self, context: &EncodeContext) -> String {
        elem(
            "p",
            &[attr_itemtype::<Self>(), attr_id(&self.id)],
            &self.content.to_html(context),
        )
    }
}

impl ToHtml for QuoteBlock {
    fn to_html(&self, context: &EncodeContext) -> String {
        elem(
            "blockquote",
            &[attr_itemtype::<Self>(), attr_id(&self.id)],
            &self.content.to_html(context),
        )
    }
}

/// Encode a table as HTML
///
/// Previously this function split the table cell's into `thead`, `tbody` and `tfoot` sections.
/// However, that interferes with resolving cell addresses in the DOM, so we reverted to a
/// simpler approach of placing all cell into `tbody`
///
/// Note that both the `label` and `caption` properties are nested within a `<caption>` element.
impl ToHtml for TableSimple {
    fn to_html(&self, context: &EncodeContext) -> String {
        let label = elem_placeholder("span", &[attr_prop("label")], &self.label, context);

        let caption = elem_placeholder("div", &[attr_prop("caption")], &self.caption, context);

        let body = elem(
            "tbody",
            &[attr_prop("rows")],
            &concat(&self.rows, |row| row.to_html(context)),
        );

        elem(
            "table",
            &[attr_itemtype::<Table>(), attr_id(&self.id)],
            &[elem("caption", &[], &[label, caption].concat()), body].concat(),
        )
    }
}

impl ToHtml for TableCaption {
    fn to_html(&self, context: &EncodeContext) -> String {
        match self {
            TableCaption::String(string) => string.to_html(context),
            TableCaption::VecBlockContent(blocks) => blocks.to_html(context),
        }
    }
}

/// Encode a table row to HTML
///
/// Previously we passed the row type down to the cells so that they could use `<th>`
/// or `<td>` if a cell type was not specified. However, that does not allow adding
/// a `th` cell using a patch (because when it is part of the patch, the new cell does
/// not know it's row context). Therefore we deprecate the use of row type alone, and
/// encourage use of both for header rows.
impl ToHtml for TableRow {
    fn to_html(&self, context: &EncodeContext) -> String {
        elem(
            "tr",
            &[attr_itemtype::<Self>(), attr_id(&self.id)],
            &self.cells.to_html(context),
        )
    }
}

impl ToHtml for TableCell {
    fn to_html(&self, context: &EncodeContext) -> String {
        let tag = match &self.cell_type {
            Some(cell_type) => match cell_type {
                TableCellCellType::Header => "th",
                TableCellCellType::Data => "td",
            },
            None => "td",
        };

        let colspan = match self.colspan {
            Some(colspan) => attr("colspan", &colspan.to_string()),
            None => nothing(),
        };

        let rowspan = match self.rowspan {
            Some(rowspan) => attr("rowspan", &rowspan.to_string()),
            None => nothing(),
        };

        elem(
            tag,
            &[attr_itemtype::<Self>(), attr_id(&self.id), colspan, rowspan],
            &self.content.to_html(context),
        )
    }
}

impl ToHtml for TableCellCellType {
    fn to_html(&self, _context: &EncodeContext) -> String {
        match self {
            TableCellCellType::Header => "Header".to_string(),
            TableCellCellType::Data => "Data".to_string(),
        }
    }
}

impl ToHtml for TableCellContent {
    fn to_html(&self, context: &EncodeContext) -> String {
        match self {
            TableCellContent::VecInlineContent(nodes) => nodes.to_html(context),
            TableCellContent::VecBlockContent(nodes) => nodes.to_html(context),
        }
    }
}

impl ToHtml for ThematicBreak {
    fn to_html(&self, _context: &EncodeContext) -> String {
        elem_empty("hr", &[attr_itemtype::<Self>(), attr_id(&self.id)])
    }
}
<commit_msg>fix(HTML encoding): Do not add non-existent slot names to error parts<commit_after>//! Encode a `BlockContent` nodes to HTML

use super::{
    attr, attr_id, attr_itemprop, attr_itemtype, attr_prop, attr_slot, concat, elem, elem_empty,
    elem_meta, elem_placeholder, json, nothing, EncodeContext, ToHtml,
};
use html_escape::encode_safe;
use stencila_schema::*;

impl ToHtml for BlockContent {
    fn to_html(&self, context: &EncodeContext) -> String {
        match self {
            BlockContent::Claim(node) => node.to_html(context),
            BlockContent::CodeBlock(node) => node.to_html(context),
            BlockContent::CodeChunk(node) => node.to_html(context),
            BlockContent::Collection(node) => node.to_html(context),
            BlockContent::Figure(node) => node.to_html(context),
            BlockContent::Heading(node) => node.to_html(context),
            BlockContent::Include(node) => node.to_html(context),
            BlockContent::List(node) => node.to_html(context),
            BlockContent::MathBlock(node) => node.to_html(context),
            BlockContent::Paragraph(node) => node.to_html(context),
            BlockContent::QuoteBlock(node) => node.to_html(context),
            BlockContent::Table(node) => node.to_html(context),
            BlockContent::ThematicBreak(node) => node.to_html(context),
        }
    }
}

impl ToHtml for ClaimSimple {
    fn to_html(&self, _context: &EncodeContext) -> String {
        elem(
            "pre",
            &[
                attr_itemtype::<Self>(),
                attr_id(&self.id),
                attr("class", "todo"),
            ],
            &json(self),
        )
    }
}

impl ToHtml for CodeBlock {
    /// Encode a [`CodeBlock`] as HTML
    ///
    /// The `programming_language` is encoded as both a `class` attribute and a `<meta>` element.
    /// The `<meta>` element is for Microdata and Stencila WebComponent compatibility.
    /// The `class` follows the recommendation of [HTML5 spec](https://html.spec.whatwg.org/#the-code-element)
    /// to "use the class attribute, e.g. by adding a class prefixed with "language-" to the element."
    fn to_html(&self, _context: &EncodeContext) -> String {
        let (lang_class, lang_meta) = match &self.programming_language {
            Some(programming_language) => (
                attr("class", &["language-", programming_language].concat()),
                elem_meta("programmingLanguage", programming_language),
            ),
            None => (nothing(), nothing()),
        };

        let text = elem(
            "code",
            &[attr_itemprop("text"), lang_class],
            &encode_safe(&self.text),
        );

        elem(
            "pre",
            &[attr_itemtype::<Self>(), attr_id(&self.id)],
            &[lang_meta, text].concat(),
        )
    }
}

impl ToHtml for CodeChunk {
    fn to_html(&self, context: &EncodeContext) -> String {
        let text = elem(
            "pre",
            &[attr_prop("text"), attr_slot("text")],
            &self.text.to_html(context),
        );

        let lang_attr = attr("programming-language", &self.programming_language);
        let lang_meta = elem_meta("programmingLanguage", &self.programming_language);

        let outputs = elem_placeholder(
            "div",
            &[attr_prop("outputs"), attr_slot("outputs")],
            &self.outputs,
            context,
        );

        let errors = elem_placeholder(
            "div",
            &[attr_prop("errors"), attr_slot("errors")],
            &self.errors,
            context,
        );

        let label = elem_placeholder(
            "span",
            &[attr_prop("label"), attr_slot("label")],
            &self.label,
            context,
        );

        let caption = elem_placeholder(
            "figcaption",
            &[attr_prop("caption"), attr_slot("caption")],
            &self.caption,
            context,
        );

        elem(
            "stencila-code-chunk",
            &[attr_itemtype::<Self>(), attr_id(&self.id), lang_attr],
            &[lang_meta, text, outputs, errors, label, caption].concat(),
        )
    }
}

impl ToHtml for CodeChunkCaption {
    fn to_html(&self, context: &EncodeContext) -> String {
        match self {
            CodeChunkCaption::String(string) => string.to_html(context),
            CodeChunkCaption::VecBlockContent(blocks) => blocks.to_html(context),
        }
    }
}

/// Encode a code error to HTML
///
/// In the future the current `CodeError` is likely to be replaced by a `CodeMessage`
/// (and `messages` added as a property of code elements).
impl ToHtml for CodeError {
    fn to_html(&self, context: &EncodeContext) -> String {
        let kind = if self.error_message.to_lowercase().contains("warning")
            && self.stack_trace.is_none()
        {
            "warning"
        } else {
            "error"
        };

        let error_type = elem_placeholder(
            "span",
            &[attr_prop("errorType")],
            &self.error_type,
            context,
        );

        let error_message = elem(
            "span",
            &[attr_prop("errorMessage")],
            &self.error_message.to_html(context),
        );

        let stack_trace = elem_placeholder(
            "pre",
            &[attr_prop("stackTrace"), attr_slot("stacktrace")],
            &self.stack_trace,
            context,
        );

        elem(
            "stencila-code-error",
            &[
                attr_itemtype::<Self>(),
                attr_id(&self.id),
                attr("kind", kind),
            ],
            &[error_type, error_message, stack_trace].concat(),
        )
    }
}

impl ToHtml for CollectionSimple {
    fn to_html(&self, context: &EncodeContext) -> String {
        elem(
            "ol",
            &[attr_itemtype::<Self>(), attr_id(&self.id)],
            &concat(&self.parts, |part| elem("li", &[], &part.to_html(context))),
        )
    }
}

/// Encode a figure as HTML
///
/// Similar to as for tables, except that the label and caption are at the bottom
/// (although themes should be able to move them) and are not grouped together in a `<caption>`
/// element as they are in a table.
impl ToHtml for FigureSimple {
    fn to_html(&self, context: &EncodeContext) -> String {
        let content = elem_placeholder("div", &[attr_prop("content")], &self.content, context);

        let label = elem_placeholder("span", &[attr_prop("label")], &self.label, context);

        let caption = elem_placeholder(
            "figcaption",
            &[attr_prop("caption")],
            &self.caption,
            context,
        );

        elem(
            "figure",
            &[attr_itemtype::<Figure>(), attr_id(&self.id)],
            &[content, label, caption].concat(),
        )
    }
}

impl ToHtml for FigureCaption {
    fn to_html(&self, context: &EncodeContext) -> String {
        match self {
            FigureCaption::String(string) => string.to_html(context),
            FigureCaption::VecBlockContent(blocks) => blocks.to_html(context),
        }
    }
}

impl ToHtml for Heading {
    /// Encode a `Heading` node to a `<h2>`, `<h3>` etc element.
    ///
    /// > Generally, it is a best practice to ensure that the beginning of a
    /// > page's main content starts with a h1 element, and also to ensure
    /// > that the page contains only one h1 element.
    /// > From https://dequeuniversity.com/rules/axe/3.5/page-has-heading-one
    ///
    /// This codec follows that recommendation and reserves `<h1>` for the
    /// `title` property of a creative work.
    ///
    /// In rare cases that there is no content in the heading, return an empty
    /// text node to avoid the 'Heading tag found with no content' accessibility error.
    fn to_html(&self, context: &EncodeContext) -> String {
        let depth = match &self.depth {
            Some(depth) => std::cmp::min(*depth + 1, 6),
            None => 2,
        };

        elem(
            &["h", &depth.to_string()].concat(),
            &[attr_itemtype::<Self>(), attr_id(&self.id)],
            &self.content.to_html(context),
        )
    }
}

impl ToHtml for Include {
    fn to_html(&self, context: &EncodeContext) -> String {
        let content = self
            .content
            .as_ref()
            .map_or_else(|| "".to_string(), |content| content.to_html(context));

        elem(
            "div",
            &[attr_itemtype::<Self>(), attr_id(&self.id)],
            &content,
        )
    }
}

impl ToHtml for List {
    fn to_html(&self, context: &EncodeContext) -> String {
        let tag = match &self.order {
            Some(ListOrder::Ascending) => "ol",
            _ => "ul",
        };

        elem(
            tag,
            &[attr_itemtype::<Self>(), attr_id(&self.id)],
            &self
                .items
                .iter()
                .map(|item| item.to_html(context))
                .collect::<Vec<String>>()
                .concat(),
        )
    }
}

impl ToHtml for ListItem {
    fn to_html(&self, context: &EncodeContext) -> String {
        let checkbox = self.is_checked.map(|is_checked| match is_checked {
            true => InlineContent::String("☑ ".to_string()),
            false => InlineContent::String("☐ ".to_string()),
        });

        let content = match &self.content {
            Some(content) => match content {
                ListItemContent::VecInlineContent(inlines) => match checkbox {
                    Some(checkbox) => [vec![checkbox], inlines.clone()].concat().to_html(context),
                    None => inlines.to_html(context),
                },
                ListItemContent::VecBlockContent(blocks) => match checkbox {
                    Some(checkbox) => {
                        // Check box is only added if the first block is a paragraph
                        if let Some(BlockContent::Paragraph(paragraph)) = blocks.first() {
                            let mut paragraph = paragraph.clone();
                            paragraph.content.insert(0, checkbox);
                            [
                                paragraph.to_html(context),
                                concat(&blocks[1..], |block| block.to_html(context)),
                            ]
                            .concat()
                        } else {
                            blocks.to_html(context)
                        }
                    }
                    None => blocks.to_html(context),
                },
            },
            None => "".to_string(),
        };

        elem(
            "li",
            &[attr_itemtype::<Self>(), attr_id(&self.id)],
            &content,
        )
    }
}

impl ToHtml for MathBlock {
    fn to_html(&self, _context: &EncodeContext) -> String {
        elem(
            "pre",
            &[
                attr_itemtype::<Self>(),
                attr_id(&self.id),
                attr("class", "todo"),
            ],
            &encode_safe(&self.text),
        )
    }
}

impl ToHtml for Paragraph {
    fn to_html(&self, context: &EncodeContext) -> String {
        elem(
            "p",
            &[attr_itemtype::<Self>(), attr_id(&self.id)],
            &self.content.to_html(context),
        )
    }
}

impl ToHtml for QuoteBlock {
    fn to_html(&self, context: &EncodeContext) -> String {
        elem(
            "blockquote",
            &[attr_itemtype::<Self>(), attr_id(&self.id)],
            &self.content.to_html(context),
        )
    }
}

/// Encode a table as HTML
///
/// Previously this function split the table cell's into `thead`, `tbody` and `tfoot` sections.
/// However, that interferes with resolving cell addresses in the DOM, so we reverted to a
/// simpler approach of placing all cell into `tbody`
///
/// Note that both the `label` and `caption` properties are nested within a `<caption>` element.
impl ToHtml for TableSimple {
    fn to_html(&self, context: &EncodeContext) -> String {
        let label = elem_placeholder("span", &[attr_prop("label")], &self.label, context);

        let caption = elem_placeholder("div", &[attr_prop("caption")], &self.caption, context);

        let body = elem(
            "tbody",
            &[attr_prop("rows")],
            &concat(&self.rows, |row| row.to_html(context)),
        );

        elem(
            "table",
            &[attr_itemtype::<Table>(), attr_id(&self.id)],
            &[elem("caption", &[], &[label, caption].concat()), body].concat(),
        )
    }
}

impl ToHtml for TableCaption {
    fn to_html(&self, context: &EncodeContext) -> String {
        match self {
            TableCaption::String(string) => string.to_html(context),
            TableCaption::VecBlockContent(blocks) => blocks.to_html(context),
        }
    }
}

/// Encode a table row to HTML
///
/// Previously we passed the row type down to the cells so that they could use `<th>`
/// or `<td>` if a cell type was not specified. However, that does not allow adding
/// a `th` cell using a patch (because when it is part of the patch, the new cell does
/// not know it's row context). Therefore we deprecate the use of row type alone, and
/// encourage use of both for header rows.
impl ToHtml for TableRow {
    fn to_html(&self, context: &EncodeContext) -> String {
        elem(
            "tr",
            &[attr_itemtype::<Self>(), attr_id(&self.id)],
            &self.cells.to_html(context),
        )
    }
}

impl ToHtml for TableCell {
    fn to_html(&self, context: &EncodeContext) -> String {
        let tag = match &self.cell_type {
            Some(cell_type) => match cell_type {
                TableCellCellType::Header => "th",
                TableCellCellType::Data => "td",
            },
            None => "td",
        };

        let colspan = match self.colspan {
            Some(colspan) => attr("colspan", &colspan.to_string()),
            None => nothing(),
        };

        let rowspan = match self.rowspan {
            Some(rowspan) => attr("rowspan", &rowspan.to_string()),
            None => nothing(),
        };

        elem(
            tag,
            &[attr_itemtype::<Self>(), attr_id(&self.id), colspan, rowspan],
            &self.content.to_html(context),
        )
    }
}

impl ToHtml for TableCellCellType {
    fn to_html(&self, _context: &EncodeContext) -> String {
        match self {
            TableCellCellType::Header => "Header".to_string(),
            TableCellCellType::Data => "Data".to_string(),
        }
    }
}

impl ToHtml for TableCellContent {
    fn to_html(&self, context: &EncodeContext) -> String {
        match self {
            TableCellContent::VecInlineContent(nodes) => nodes.to_html(context),
            TableCellContent::VecBlockContent(nodes) => nodes.to_html(context),
        }
    }
}

impl ToHtml for ThematicBreak {
    fn to_html(&self, _context: &EncodeContext) -> String {
        elem_empty("hr", &[attr_itemtype::<Self>(), attr_id(&self.id)])
    }
}
<|endoftext|>


<commit_before>use sass::output_style::SassOutputStyle;
use token::{Lexeme, Token};
use ast::number_value::NumberValue;
use context::Context;
use error::{Result, SassError, ErrorKind};

#[derive(Debug, Clone, PartialEq)]
pub enum Expression {
    List(Vec<Expression>),
    Number(NumberValue),
    Operator(Lexeme),
    String(Lexeme),
}

impl Expression {
    #[allow(unused_variables)]
    pub fn to_string(&self, style: SassOutputStyle) -> String {
        match *self {
            Expression::List(ref elements) => {
                elements.iter().map(|e|
                    e.to_string(style)
                ).collect::<Vec<_>>().join(" ")
            },
            Expression::Number(ref nv) => nv.to_string(),
            Expression::Operator(ref lex) => lex.token.to_string(),
            Expression::String(ref lex) => lex.token.to_string(),
        }
    }

    pub fn parse<T: Iterator<Item = Result<Lexeme>>>(tokenizer: &mut T) -> Result<Expression> {
        let mut list = vec![];
        while let Some(Ok(lexeme)) = tokenizer.next() {
            match lexeme.token {
                Token::Semicolon => {
                    if list.len() == 1 {
                        return Ok(list.pop().unwrap())
                    } else {
                        return Ok(Expression::List(list))
                    }
                },
                Token::Number(_, _) => {
                    list.push(Expression::Number(NumberValue::from_scalar(lexeme)));
                },
                Token::Plus | Token::Minus | Token::Star | Token::Slash | Token::Percent => {
                    list.push(Expression::Operator(lexeme));
                },
                Token::Comment(_) => {},
                _ => {
                    list.push(Expression::String(lexeme));
                }
            }
        }

        let error_offset = match list.pop() {
            Some(Expression::String(lexeme)) => lexeme.offset.unwrap_or(0),
            Some(Expression::Operator(lexeme)) => lexeme.offset.unwrap_or(0),
            Some(Expression::Number(nv)) => nv.offset().unwrap_or(0),
            Some(Expression::List(_)) => unreachable!(), // for now until nested lists
            None => 0,
        };
        Err(SassError {
            offset: error_offset,
            kind: ErrorKind::UnexpectedEof,
            message: String::from(
                "Expected semicolon while parsing a value expression; reached EOF instead."
            ),
        })
    }

    fn evaluate_list(self, context: &Context, force_slash: bool) -> Expression {
        if let Expression::List(exprs) = self {
            let mut last_was_an_operator = true;
            // TODO: I'd love to have more types here, to ensure only values are
            // on the value stack and only operators are on the operator stack...
            let mut value_stack: Vec<Expression> = Vec::new();
            let mut op_stack: Vec<Expression> = Vec::new();

            // Split into value stacks and operator stacks

            let mut exprs = exprs.into_iter();
            while let Some(part) = exprs.next() {
                debug!("Processing list item {:#?}", part);
                match part {
                    Expression::Number(nv) => {
                        if last_was_an_operator {
                            debug!("Push on value stack {:#?}", nv);
                            value_stack.push(Expression::Number(nv));
                        } else {
                            let list = create_list(
                                value_stack.pop(),
                                Expression::Number(nv),
                            );
                            debug!("Push on list on value stack {:#?}", list);
                            value_stack.push(list);
                        }
                        last_was_an_operator = false;
                    },
                    Expression::Operator(ref op) if op.token == Token::Slash && !force_slash => {
                        let list = create_list(
                            value_stack.pop(),
                            Expression::Operator(op.clone()),
                        );
                        debug!("Push on list on value stack {:#?}", list);
                        value_stack.push(list);

                    },
                    Expression::Operator(op) => {
                        debug!("Push on op stack {:#?}", op);
                        op_stack.push(Expression::Operator(op));
                        last_was_an_operator = true;
                    },
                    Expression::String(lex) => {
                        let var_eval = context.get_variable(&lex)
                                        .unwrap_or(Expression::String(lex));

                        if last_was_an_operator {
                            value_stack.push(var_eval);
                        } else {
                            let list = create_list(
                                value_stack.pop(),
                                var_eval,
                            );
                            value_stack.push(list);
                        }
                        last_was_an_operator = false;
                    },
                    Expression::List(list) => {
                        debug!("Push list on value stack {:#?}", list);
                        value_stack.push(Expression::List(list));
                        last_was_an_operator = false;
                    },
                }
            }

            // Process the stacks
            while !op_stack.is_empty() {
                let op = op_stack.pop().unwrap();
                let second = value_stack.pop().expect("Expected a second argument on the value stack");
                let first = value_stack.pop().expect("Expected a first argument on the value stack");

                let math_result = first.apply_math(op, second, context, force_slash);
                debug!("Math result: {:#?}", math_result);

                value_stack.push(math_result);
            }

            value_stack.pop().unwrap()
        } else {
            self
        }
    }

    pub fn evaluate(self, context: &Context) -> Expression {
        match self {
            Expression::Number(nv) => Expression::Number(nv),
            Expression::Operator(op) => Expression::Operator(op),
            Expression::String(lex) => {
                context.get_variable(&lex).unwrap_or(Expression::String(lex))
            },
            Expression::List(exprs) => {
                Expression::List(exprs).evaluate_list(context, false)
            },
        }
    }

    fn apply_math(self, operator: Expression, second: Expression, context: &Context, force_slash: bool) -> Expression {
        debug!("Applying math to:\nfirst: {:#?}\nop: {:#?}\nsecond: {:#?}", self, operator, second);
        match (self.clone(), second.clone()) {
            (Expression::Number(f), Expression::Number(s)) => {
                match operator {
                    Expression::Operator(ref o) if o.token == Token::Slash => {
                        if force_slash || (f.computed || s.computed) {
                            let result = f.apply_math(&o, &s);
                            Expression::Number(result)
                        } else {
                            Expression::List(vec![self, operator.clone(), second.clone()])
                        }
                    },
                    Expression::Operator(o) => {
                        let result = f.apply_math(&o, &s);
                        Expression::Number(result)
                    },
                    _ => unreachable!(),
                }
            },
            (Expression::List(f), Expression::List(s)) => {
                let eval_first = Expression::List(f).evaluate_list(context, true);
                let eval_second = Expression::List(s).evaluate_list(context, true);
                eval_first.apply_math(operator, eval_second, context, true)
            },
            (Expression::List(f), Expression::Number(s)) => {
                let eval_first = Expression::List(f).evaluate_list(context, true);
                eval_first.apply_math(operator, Expression::Number(s), context, true)
            },
            (Expression::Number(f), Expression::List(s)) => {
                let eval_second = Expression::List(s).evaluate_list(context, true);
                Expression::Number(f).apply_math(operator, eval_second, context, true)
            },
            _ => unimplemented!(),
        }
    }
}

fn create_list(head: Option<Expression>, tail: Expression) -> Expression {
    let mut list = match head {
        Some(Expression::List(v)) => v,
        Some(e) => vec![e],
        None => vec![],
    };
    list.push(tail);
    Expression::List(list)
}

#[cfg(test)]
mod tests {
    use super::*;
    use token::{Lexeme, Token};
    use ast::number_value::NumberValue;
    use context::Context;

    fn semicolon() -> Lexeme {
        Lexeme { token: Token::Semicolon, offset: None }
    }

    fn blue() -> Lexeme {
        Lexeme { token: Token::String("blue".into()), offset: None }
    }

    fn plus() -> Lexeme {
        Lexeme { token: Token::Plus, offset: None }
    }

    fn slash() -> Lexeme {
        Lexeme { token: Token::Slash, offset: None }
    }

    fn one() -> Lexeme {
        Lexeme { token: Token::Number(1.0, None), offset: None }
    }

    fn two() -> Lexeme {
        Lexeme { token: Token::Number(2.0, None), offset: None }
    }

    fn one_px() -> Lexeme {
        Lexeme { token: Token::Number(1.0, Some("px".into())), offset: None }
    }

    #[test]
    fn it_parses_a_single_string() {
        let mut fake_tokenizer = vec![Ok(blue()), Ok(semicolon())].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::String(blue()))
        );
    }

    #[test]
    fn it_parses_a_list() {
        let mut fake_tokenizer = vec![
            Ok(one()),
            Ok(plus()),
            Ok(one()),
            Ok(semicolon())
        ].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::List(vec![
                Expression::Number(NumberValue::from_scalar(one())),
                Expression::Operator(plus()),
                Expression::Number(NumberValue::from_scalar(one())),
            ]))
        );
    }

    #[test]
    fn it_parses_a_number_without_units() {
        let mut fake_tokenizer = vec![Ok(one()), Ok(semicolon())].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::Number(NumberValue::from_scalar(one())))
        );
    }

    #[test]
    fn it_parses_a_number_with_units() {
        let mut fake_tokenizer = vec![Ok(one_px()), Ok(semicolon())].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::Number(NumberValue::from_scalar(one_px())))
        );
    }

    #[test]
    fn it_evaluates_a_list() {
        let ex = Expression::List(vec![
            Expression::Number(NumberValue::from_scalar(one())),
            Expression::Operator(slash()),
            Expression::Number(NumberValue::from_scalar(two())),
            Expression::Operator(plus()),
            Expression::Number(NumberValue::from_scalar(one())),
            Expression::Operator(slash()),
            Expression::Number(NumberValue::from_scalar(two())),
        ]);
        let fake_context = Context::new();
        assert_eq!(
            ex.evaluate(&fake_context),
            Expression::Number(NumberValue { scalar: one(), computed: true })
        );
    }
}
<commit_msg>Parse sub-lists when encountering parens<commit_after>use sass::output_style::SassOutputStyle;
use token::{Lexeme, Token};
use ast::number_value::NumberValue;
use context::Context;
use error::{Result, SassError, ErrorKind};

#[derive(Debug, Clone, PartialEq)]
pub enum Expression {
    List(Vec<Expression>),
    Number(NumberValue),
    Operator(Lexeme),
    String(Lexeme),
}

impl Expression {
    #[allow(unused_variables)]
    pub fn to_string(&self, style: SassOutputStyle) -> String {
        match *self {
            Expression::List(ref elements) => {
                elements.iter().map(|e|
                    e.to_string(style)
                ).collect::<Vec<_>>().join(" ")
            },
            Expression::Number(ref nv) => nv.to_string(),
            Expression::Operator(ref lex) => lex.token.to_string(),
            Expression::String(ref lex) => lex.token.to_string(),
        }
    }

    fn parse_parenthetical<T: Iterator<Item = Result<Lexeme>>>(tokenizer: &mut T) -> Result<Expression> {
        let mut list = vec![];
        while let Some(Ok(lexeme)) = tokenizer.next() {
            match lexeme.token {
                Token::RightParen => {
                    return Ok(Expression::List(list))
                },
                Token::LeftParen => {
                    list.push(
                        try!(Expression::parse_parenthetical(tokenizer))
                    );
                },
                Token::Number(_, _) => {
                    list.push(
                        Expression::Number(NumberValue::from_scalar(lexeme))
                    );
                },
                Token::Plus | Token::Minus | Token::Star |
                              Token::Slash | Token::Percent => {
                    list.push(Expression::Operator(lexeme));
                },
                Token::Comment(_) => {},
                _ => {
                    list.push(Expression::String(lexeme));
                }
            }
        }

        let error_offset = match list.pop() {
            Some(Expression::String(lexeme)) => lexeme.offset.unwrap_or(0),
            Some(Expression::Operator(lexeme)) => lexeme.offset.unwrap_or(0),
            Some(Expression::Number(nv)) => nv.offset().unwrap_or(0),
            Some(Expression::List(_)) => unreachable!(), // for now until nested lists
            None => 0,
        };
        Err(SassError {
            offset: error_offset,
            kind: ErrorKind::UnexpectedEof,
            message: String::from(
                "Expected right paren while parsing a value expression; reached EOF instead."
            ),
        })
    }

    pub fn parse<T: Iterator<Item = Result<Lexeme>>>(tokenizer: &mut T) -> Result<Expression> {
        let mut list = vec![];
        while let Some(Ok(lexeme)) = tokenizer.next() {
            match lexeme.token {
                Token::Semicolon => {
                    if list.len() == 1 {
                        return Ok(list.pop().unwrap())
                    } else {
                        return Ok(Expression::List(list))
                    }
                },
                Token::LeftParen => {
                    list.push(
                        try!(Expression::parse_parenthetical(tokenizer))
                    );
                },
                Token::Number(_, _) => {
                    list.push(
                        Expression::Number(NumberValue::from_scalar(lexeme))
                    );
                },
                Token::Plus | Token::Minus | Token::Star |
                              Token::Slash | Token::Percent => {
                    list.push(Expression::Operator(lexeme));
                },
                Token::Comment(_) => {},
                _ => {
                    list.push(Expression::String(lexeme));
                }
            }
        }

        let error_offset = match list.pop() {
            Some(Expression::String(lexeme)) => lexeme.offset.unwrap_or(0),
            Some(Expression::Operator(lexeme)) => lexeme.offset.unwrap_or(0),
            Some(Expression::Number(nv)) => nv.offset().unwrap_or(0),
            Some(Expression::List(_)) => unreachable!(), // for now until nested lists
            None => 0,
        };
        Err(SassError {
            offset: error_offset,
            kind: ErrorKind::UnexpectedEof,
            message: String::from(
                "Expected semicolon while parsing a value expression; reached EOF instead."
            ),
        })
    }

    fn evaluate_list(self, context: &Context, force_slash: bool) -> Expression {
        if let Expression::List(exprs) = self {
            let mut last_was_an_operator = true;
            // TODO: I'd love to have more types here, to ensure only values are
            // on the value stack and only operators are on the operator stack...
            let mut value_stack: Vec<Expression> = Vec::new();
            let mut op_stack: Vec<Expression> = Vec::new();

            // Split into value stacks and operator stacks

            let mut exprs = exprs.into_iter();
            while let Some(part) = exprs.next() {
                debug!("Processing list item {:#?}", part);
                match part {
                    Expression::Number(nv) => {
                        if last_was_an_operator {
                            debug!("Push on value stack {:#?}", nv);
                            value_stack.push(Expression::Number(nv));
                        } else {
                            let list = create_list(
                                value_stack.pop(),
                                Expression::Number(nv),
                            );
                            debug!("Push on list on value stack {:#?}", list);
                            value_stack.push(list);
                        }
                        last_was_an_operator = false;
                    },
                    Expression::Operator(ref op) if op.token == Token::Slash && !force_slash => {
                        let list = create_list(
                            value_stack.pop(),
                            Expression::Operator(op.clone()),
                        );
                        debug!("Push on list on value stack {:#?}", list);
                        value_stack.push(list);

                    },
                    Expression::Operator(op) => {
                        debug!("Push on op stack {:#?}", op);
                        op_stack.push(Expression::Operator(op));
                        last_was_an_operator = true;
                    },
                    Expression::String(lex) => {
                        let var_eval = context.get_variable(&lex)
                                        .unwrap_or(Expression::String(lex));

                        if last_was_an_operator {
                            value_stack.push(var_eval);
                        } else {
                            let list = create_list(
                                value_stack.pop(),
                                var_eval,
                            );
                            value_stack.push(list);
                        }
                        last_was_an_operator = false;
                    },
                    Expression::List(list) => {
                        debug!("Push list on value stack {:#?}", list);
                        value_stack.push(Expression::List(list));
                        last_was_an_operator = false;
                    },
                }
            }

            // Process the stacks
            while !op_stack.is_empty() {
                let op = op_stack.pop().unwrap();
                let second = value_stack.pop().expect("Expected a second argument on the value stack");
                let first = value_stack.pop().expect("Expected a first argument on the value stack");

                let math_result = first.apply_math(op, second, context, force_slash);
                debug!("Math result: {:#?}", math_result);

                value_stack.push(math_result);
            }

            value_stack.pop().unwrap()
        } else {
            self
        }
    }

    pub fn evaluate(self, context: &Context) -> Expression {
        match self {
            Expression::Number(nv) => Expression::Number(nv),
            Expression::Operator(op) => Expression::Operator(op),
            Expression::String(lex) => {
                context.get_variable(&lex).unwrap_or(Expression::String(lex))
            },
            Expression::List(exprs) => {
                Expression::List(exprs).evaluate_list(context, false)
            },
        }
    }

    fn apply_math(self, operator: Expression, second: Expression, context: &Context, force_slash: bool) -> Expression {
        debug!("Applying math to:\nfirst: {:#?}\nop: {:#?}\nsecond: {:#?}", self, operator, second);
        match (self.clone(), second.clone()) {
            (Expression::Number(f), Expression::Number(s)) => {
                match operator {
                    Expression::Operator(ref o) if o.token == Token::Slash => {
                        if force_slash || (f.computed || s.computed) {
                            let result = f.apply_math(&o, &s);
                            Expression::Number(result)
                        } else {
                            Expression::List(vec![self, operator.clone(), second.clone()])
                        }
                    },
                    Expression::Operator(o) => {
                        let result = f.apply_math(&o, &s);
                        Expression::Number(result)
                    },
                    _ => unreachable!(),
                }
            },
            (Expression::List(f), Expression::List(s)) => {
                let eval_first = Expression::List(f).evaluate_list(context, true);
                let eval_second = Expression::List(s).evaluate_list(context, true);
                eval_first.apply_math(operator, eval_second, context, true)
            },
            (Expression::List(f), Expression::Number(s)) => {
                let eval_first = Expression::List(f).evaluate_list(context, true);
                eval_first.apply_math(operator, Expression::Number(s), context, true)
            },
            (Expression::Number(f), Expression::List(s)) => {
                let eval_second = Expression::List(s).evaluate_list(context, true);
                Expression::Number(f).apply_math(operator, eval_second, context, true)
            },
            _ => unimplemented!(),
        }
    }
}

fn create_list(head: Option<Expression>, tail: Expression) -> Expression {
    let mut list = match head {
        Some(Expression::List(v)) => v,
        Some(e) => vec![e],
        None => vec![],
    };
    list.push(tail);
    Expression::List(list)
}

#[cfg(test)]
mod tests {
    use super::*;
    use token::{Lexeme, Token};
    use ast::number_value::NumberValue;
    use context::Context;

    fn semicolon() -> Lexeme {
        Lexeme { token: Token::Semicolon, offset: None }
    }

    fn blue() -> Lexeme {
        Lexeme { token: Token::String("blue".into()), offset: None }
    }

    fn plus() -> Lexeme {
        Lexeme { token: Token::Plus, offset: None }
    }

    fn slash() -> Lexeme {
        Lexeme { token: Token::Slash, offset: None }
    }

    fn one() -> Lexeme {
        Lexeme { token: Token::Number(1.0, None), offset: None }
    }

    fn two() -> Lexeme {
        Lexeme { token: Token::Number(2.0, None), offset: None }
    }

    fn one_px() -> Lexeme {
        Lexeme { token: Token::Number(1.0, Some("px".into())), offset: None }
    }

    #[test]
    fn it_parses_a_single_string() {
        let mut fake_tokenizer = vec![Ok(blue()), Ok(semicolon())].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::String(blue()))
        );
    }

    #[test]
    fn it_parses_a_list() {
        let mut fake_tokenizer = vec![
            Ok(one()),
            Ok(plus()),
            Ok(one()),
            Ok(semicolon())
        ].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::List(vec![
                Expression::Number(NumberValue::from_scalar(one())),
                Expression::Operator(plus()),
                Expression::Number(NumberValue::from_scalar(one())),
            ]))
        );
    }

    #[test]
    fn it_parses_a_number_without_units() {
        let mut fake_tokenizer = vec![Ok(one()), Ok(semicolon())].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::Number(NumberValue::from_scalar(one())))
        );
    }

    #[test]
    fn it_parses_a_number_with_units() {
        let mut fake_tokenizer = vec![Ok(one_px()), Ok(semicolon())].into_iter();
        assert_eq!(
            Expression::parse(&mut fake_tokenizer),
            Ok(Expression::Number(NumberValue::from_scalar(one_px())))
        );
    }

    #[test]
    fn it_evaluates_a_list() {
        let ex = Expression::List(vec![
            Expression::Number(NumberValue::from_scalar(one())),
            Expression::Operator(slash()),
            Expression::Number(NumberValue::from_scalar(two())),
            Expression::Operator(plus()),
            Expression::Number(NumberValue::from_scalar(one())),
            Expression::Operator(slash()),
            Expression::Number(NumberValue::from_scalar(two())),
        ]);
        let fake_context = Context::new();
        assert_eq!(
            ex.evaluate(&fake_context),
            Expression::Number(NumberValue { scalar: one(), computed: true })
        );
    }
}
<|endoftext|>