<commit_before>/// The sure stream.
///
/// The sure stream represents a linearization of a SureTree.  By keeping
/// representations as iterators across SureNodes instead of keeping an
/// entire tree in memory, we can process larger filesystem trees, using
/// temporary space on the hard disk instead of using memory.
use crate::{suretree::AttMap, Error, Result};
use flate2::{read::GzDecoder, write::GzEncoder, Compression};
use std::{
    fs::File,
    io::{self, BufRead, BufReader, BufWriter, Read, Write},
    path::{Path, PathBuf},
};
use weave::NamingConvention;

mod compare;
pub mod fs;
mod fullpath;
mod hashes;

pub use compare::compare_trees;
pub use fullpath::into_tracker;
pub use hashes::{HashCombiner, HashUpdater, Source};

#[derive(Clone, Debug)]
pub enum SureNode {
    Enter { name: String, atts: AttMap },
    Leave,
    File { name: String, atts: AttMap },
    Sep,
}

impl SureNode {
    pub fn is_enter(&self) -> bool {
        match self {
            SureNode::Enter { .. } => true,
            _ => false,
        }
    }

    pub fn is_reg_file(&self) -> bool {
        match self {
            SureNode::File { atts, .. } => atts["kind"] == "file",
            _ => false,
        }
    }

    pub fn is_file(&self) -> bool {
        match self {
            SureNode::File { .. } => true,
            _ => false,
        }
    }

    pub fn is_leave(&self) -> bool {
        match self {
            SureNode::Leave => true,
            _ => false,
        }
    }

    pub fn is_sep(&self) -> bool {
        match self {
            SureNode::Sep => true,
            _ => false,
        }
    }

    pub fn needs_hash(&self) -> bool {
        match self {
            SureNode::File { atts, .. } => atts["kind"] == "file" && !atts.contains_key("sha1"),
            _ => false,
        }
    }

    pub fn size(&self) -> u64 {
        match self {
            SureNode::File { atts, .. } => {
                atts.get("size").map(|x| x.parse().unwrap()).unwrap_or(0)
            }
            _ => 0,
        }
    }

    /// Get the name of this node.  Panics if the node type does not have
    /// an associated name.
    pub fn name(&self) -> &str {
        match self {
            SureNode::File { ref name, .. } => name,
            SureNode::Enter { ref name, .. } => name,
            _ => panic!("Node does not have a name"),
        }
    }

    /// Safely get the name of this node.
    pub fn get_name(&self) -> Option<&str> {
        match self {
            SureNode::File { ref name, .. } => Some(name),
            SureNode::Enter { ref name, .. } => Some(name),
            _ => None,
        }
    }

    /// Get a nice representation of the kind of this node.  Returns "???"
    /// if the kind isn't meaningful.
    pub fn kind(&self) -> &str {
        self.atts()
            .map(|a| a.get("kind").map(|k| &k[..]).unwrap_or("???"))
            .unwrap_or("???")
    }

    /// Access the nodes attributes.
    pub fn atts(&self) -> Option<&AttMap> {
        match self {
            SureNode::File { ref atts, .. } => Some(atts),
            SureNode::Enter { ref atts, .. } => Some(atts),
            _ => None,
        }
    }

    /// Access the nodes attributes mutably.
    pub fn atts_mut(&mut self) -> Option<&mut AttMap> {
        match self {
            SureNode::File { ref mut atts, .. } => Some(atts),
            SureNode::Enter { ref mut atts, .. } => Some(atts),
            _ => None,
        }
    }
}

// TODO: These might be possible to make more generic, but it gets messy,
// as it might just be best to assume failure.

/// Write a sure iterator to a standard gzipped file of the given name.
pub fn save<P, I>(name: P, nodes: I) -> Result<()>
where
    P: AsRef<Path>,
    I: Iterator<Item = Result<SureNode>>,
{
    let wr = File::create(name)?;
    let wr = GzEncoder::new(wr, Compression::default());
    save_to(wr, nodes)
}

/// Write a sure iterator to a new temp file with a given naming
/// convention.  Returns the name of the file, if it could be created.  The
/// data will not be written compressed.
pub fn save_naming<I, N>(naming: &N, nodes: I) -> Result<PathBuf>
where
    N: NamingConvention,
    I: Iterator<Item = Result<SureNode>>,
{
    let (tmp_name, mut tmp_file) = naming.temp_file()?;
    save_to(&mut tmp_file, nodes)?;
    Ok(tmp_name)
}

/// Save a sure tree to the given writer.
pub fn save_to<W, I>(wr: W, nodes: I) -> Result<()>
where
    W: Write,
    I: Iterator<Item = Result<SureNode>>,
{
    let mut wr = BufWriter::new(wr);

    writeln!(&mut wr, "asure-2.0")?;
    writeln!(&mut wr, "-----")?;

    for node in nodes {
        match node? {
            SureNode::Enter { name, atts } => header(&mut wr, 'd', &name, &atts)?,
            SureNode::File { name, atts } => header(&mut wr, 'f', &name, &atts)?,
            SureNode::Sep => writeln!(&mut wr, "-")?,
            SureNode::Leave => writeln!(&mut wr, "u")?,
        }
    }
    Ok(())
}

/// For pushed based writing, we can also write using a NodeWriter.
pub struct NodeWriter<W: Write> {
    writer: BufWriter<W>,
}

impl<W: Write> NodeWriter<W> {
    pub fn new(writer: W) -> Result<NodeWriter<W>> {
        let mut wr = BufWriter::new(writer);
        writeln!(&mut wr, "asure-2.0")?;
        writeln!(&mut wr, "-----")?;

        Ok(NodeWriter { writer: wr })
    }

    pub fn write_node(&mut self, node: &SureNode) -> Result<()> {
        match node {
            SureNode::Enter { name, atts } => header(&mut self.writer, 'd', &name, &atts)?,
            SureNode::File { name, atts } => header(&mut self.writer, 'f', &name, &atts)?,
            SureNode::Sep => writeln!(&mut self.writer, "-")?,
            SureNode::Leave => writeln!(&mut self.writer, "u")?,
        }
        Ok(())
    }
}

fn header<W: Write>(out: &mut W, kind: char, name: &str, atts: &AttMap) -> Result<()> {
    write!(out, "{}{} [", kind, name)?;

    for (k, v) in atts {
        write!(out, "{} {} ", k, v)?;
    }
    writeln!(out, "]")?;
    Ok(())
}

/// Load and iterate a sure tree from a standard gzip compressed surefile.
pub fn load<P: AsRef<Path>>(name: P) -> Result<ReadIterator<GzDecoder<File>>> {
    let rd = File::open(name)?;
    let rd = GzDecoder::new(rd);
    load_from(rd)
}

/// Load a surenode sequence from the given reader.
pub fn load_from<R: Read>(rd: R) -> Result<ReadIterator<R>> {
    let rd = BufReader::new(rd);
    let mut lines = rd.split('\n' as u8);

    fixed(&mut lines, b"asure-2.0")?;
    fixed(&mut lines, b"-----")?;

    Ok(ReadIterator {
        lines: lines,
        depth: 0,
        done: false,
    })
}

fn fixed<I>(inp: &mut I, exp: &[u8]) -> Result<()>
where
    I: Iterator<Item = io::Result<Vec<u8>>>,
{
    match inp.next() {
        Some(Ok(ref text)) if &text[..] == exp => Ok(()),
        Some(Ok(ref text)) => Err(Error::UnexpectedLine(
            String::from_utf8_lossy(text).into_owned(),
            String::from_utf8_lossy(exp).into_owned(),
        )),
        Some(Err(e)) => Err(Error::SureFileError(e)),
        None => Err(Error::SureFileEof),
    }
}

pub struct ReadIterator<R> {
    lines: io::Split<BufReader<R>>,
    depth: usize,
    done: bool,
}

impl<R: Read> Iterator for ReadIterator<R> {
    type Item = Result<SureNode>;

    fn next(&mut self) -> Option<Result<SureNode>> {
        if self.done {
            return None;
        }

        let line = match self.get_line() {
            Ok(line) => line,
            Err(e) => return Some(Err(e)),
        };

        match line[0] {
            b'd' => {
                let (dname, datts) = decode_entity(&line[1..]);
                self.depth += 1;
                Some(Ok(SureNode::Enter {
                    name: dname,
                    atts: datts,
                }))
            }
            b'f' => {
                let (fname, fatts) = decode_entity(&line[1..]);
                Some(Ok(SureNode::File {
                    name: fname,
                    atts: fatts,
                }))
            }
            b'-' => Some(Ok(SureNode::Sep)),
            b'u' => {
                self.depth -= 1;
                if self.depth == 0 {
                    self.done = true;
                }
                Some(Ok(SureNode::Leave))
            }
            ch => Some(Err(Error::InvalidSurefileChar(ch as char))),
        }
    }
}

impl<R: Read> ReadIterator<R> {
    fn get_line(&mut self) -> Result<Vec<u8>> {
        match self.lines.next() {
            None => return Err(Error::TruncatedSurefile),
            Some(l) => Ok(l?),
        }
    }
}

// TODO: This should return Result to handle errors.
pub(crate) fn decode_entity(text: &[u8]) -> (String, AttMap) {
    let (name, mut text) = get_delim(text, b' ');
    assert!(text[0] == b'[');
    text = &text[1..];

    let mut atts = AttMap::new();
    while text[0] != b']' {
        let (key, t2) = get_delim(text, b' ');
        let (value, t2) = get_delim(t2, b' ');
        text = t2;

        atts.insert(key, value);
    }

    (name, atts)
}

fn get_delim(text: &[u8], delim: u8) -> (String, &[u8]) {
    let mut it = text.iter();
    let space = it.position(|&s| s == delim).unwrap();
    (
        String::from_utf8(text[..space].to_owned()).unwrap(),
        &text[space + 1..],
    )
}
<commit_msg>clippy: Use matches!() macro when possible<commit_after>/// The sure stream.
///
/// The sure stream represents a linearization of a SureTree.  By keeping
/// representations as iterators across SureNodes instead of keeping an
/// entire tree in memory, we can process larger filesystem trees, using
/// temporary space on the hard disk instead of using memory.
use crate::{suretree::AttMap, Error, Result};
use flate2::{read::GzDecoder, write::GzEncoder, Compression};
use std::{
    fs::File,
    io::{self, BufRead, BufReader, BufWriter, Read, Write},
    path::{Path, PathBuf},
};
use weave::NamingConvention;

mod compare;
pub mod fs;
mod fullpath;
mod hashes;

pub use compare::compare_trees;
pub use fullpath::into_tracker;
pub use hashes::{HashCombiner, HashUpdater, Source};

#[derive(Clone, Debug)]
pub enum SureNode {
    Enter { name: String, atts: AttMap },
    Leave,
    File { name: String, atts: AttMap },
    Sep,
}

impl SureNode {
    pub fn is_enter(&self) -> bool {
        matches!(self, SureNode::Enter { .. })
    }

    pub fn is_reg_file(&self) -> bool {
        match self {
            SureNode::File { atts, .. } => atts["kind"] == "file",
            _ => false,
        }
    }

    pub fn is_file(&self) -> bool {
        matches!(self, SureNode::File { .. })
    }

    pub fn is_leave(&self) -> bool {
        matches!(self, SureNode::Leave)
    }

    pub fn is_sep(&self) -> bool {
        matches!(self, SureNode::Sep)
    }

    pub fn needs_hash(&self) -> bool {
        match self {
            SureNode::File { atts, .. } => atts["kind"] == "file" && !atts.contains_key("sha1"),
            _ => false,
        }
    }

    pub fn size(&self) -> u64 {
        match self {
            SureNode::File { atts, .. } => {
                atts.get("size").map(|x| x.parse().unwrap()).unwrap_or(0)
            }
            _ => 0,
        }
    }

    /// Get the name of this node.  Panics if the node type does not have
    /// an associated name.
    pub fn name(&self) -> &str {
        match self {
            SureNode::File { ref name, .. } => name,
            SureNode::Enter { ref name, .. } => name,
            _ => panic!("Node does not have a name"),
        }
    }

    /// Safely get the name of this node.
    pub fn get_name(&self) -> Option<&str> {
        match self {
            SureNode::File { ref name, .. } => Some(name),
            SureNode::Enter { ref name, .. } => Some(name),
            _ => None,
        }
    }

    /// Get a nice representation of the kind of this node.  Returns "???"
    /// if the kind isn't meaningful.
    pub fn kind(&self) -> &str {
        self.atts()
            .map(|a| a.get("kind").map(|k| &k[..]).unwrap_or("???"))
            .unwrap_or("???")
    }

    /// Access the nodes attributes.
    pub fn atts(&self) -> Option<&AttMap> {
        match self {
            SureNode::File { ref atts, .. } => Some(atts),
            SureNode::Enter { ref atts, .. } => Some(atts),
            _ => None,
        }
    }

    /// Access the nodes attributes mutably.
    pub fn atts_mut(&mut self) -> Option<&mut AttMap> {
        match self {
            SureNode::File { ref mut atts, .. } => Some(atts),
            SureNode::Enter { ref mut atts, .. } => Some(atts),
            _ => None,
        }
    }
}

// TODO: These might be possible to make more generic, but it gets messy,
// as it might just be best to assume failure.

/// Write a sure iterator to a standard gzipped file of the given name.
pub fn save<P, I>(name: P, nodes: I) -> Result<()>
where
    P: AsRef<Path>,
    I: Iterator<Item = Result<SureNode>>,
{
    let wr = File::create(name)?;
    let wr = GzEncoder::new(wr, Compression::default());
    save_to(wr, nodes)
}

/// Write a sure iterator to a new temp file with a given naming
/// convention.  Returns the name of the file, if it could be created.  The
/// data will not be written compressed.
pub fn save_naming<I, N>(naming: &N, nodes: I) -> Result<PathBuf>
where
    N: NamingConvention,
    I: Iterator<Item = Result<SureNode>>,
{
    let (tmp_name, mut tmp_file) = naming.temp_file()?;
    save_to(&mut tmp_file, nodes)?;
    Ok(tmp_name)
}

/// Save a sure tree to the given writer.
pub fn save_to<W, I>(wr: W, nodes: I) -> Result<()>
where
    W: Write,
    I: Iterator<Item = Result<SureNode>>,
{
    let mut wr = BufWriter::new(wr);

    writeln!(&mut wr, "asure-2.0")?;
    writeln!(&mut wr, "-----")?;

    for node in nodes {
        match node? {
            SureNode::Enter { name, atts } => header(&mut wr, 'd', &name, &atts)?,
            SureNode::File { name, atts } => header(&mut wr, 'f', &name, &atts)?,
            SureNode::Sep => writeln!(&mut wr, "-")?,
            SureNode::Leave => writeln!(&mut wr, "u")?,
        }
    }
    Ok(())
}

/// For pushed based writing, we can also write using a NodeWriter.
pub struct NodeWriter<W: Write> {
    writer: BufWriter<W>,
}

impl<W: Write> NodeWriter<W> {
    pub fn new(writer: W) -> Result<NodeWriter<W>> {
        let mut wr = BufWriter::new(writer);
        writeln!(&mut wr, "asure-2.0")?;
        writeln!(&mut wr, "-----")?;

        Ok(NodeWriter { writer: wr })
    }

    pub fn write_node(&mut self, node: &SureNode) -> Result<()> {
        match node {
            SureNode::Enter { name, atts } => header(&mut self.writer, 'd', &name, &atts)?,
            SureNode::File { name, atts } => header(&mut self.writer, 'f', &name, &atts)?,
            SureNode::Sep => writeln!(&mut self.writer, "-")?,
            SureNode::Leave => writeln!(&mut self.writer, "u")?,
        }
        Ok(())
    }
}

fn header<W: Write>(out: &mut W, kind: char, name: &str, atts: &AttMap) -> Result<()> {
    write!(out, "{}{} [", kind, name)?;

    for (k, v) in atts {
        write!(out, "{} {} ", k, v)?;
    }
    writeln!(out, "]")?;
    Ok(())
}

/// Load and iterate a sure tree from a standard gzip compressed surefile.
pub fn load<P: AsRef<Path>>(name: P) -> Result<ReadIterator<GzDecoder<File>>> {
    let rd = File::open(name)?;
    let rd = GzDecoder::new(rd);
    load_from(rd)
}

/// Load a surenode sequence from the given reader.
pub fn load_from<R: Read>(rd: R) -> Result<ReadIterator<R>> {
    let rd = BufReader::new(rd);
    let mut lines = rd.split('\n' as u8);

    fixed(&mut lines, b"asure-2.0")?;
    fixed(&mut lines, b"-----")?;

    Ok(ReadIterator {
        lines: lines,
        depth: 0,
        done: false,
    })
}

fn fixed<I>(inp: &mut I, exp: &[u8]) -> Result<()>
where
    I: Iterator<Item = io::Result<Vec<u8>>>,
{
    match inp.next() {
        Some(Ok(ref text)) if &text[..] == exp => Ok(()),
        Some(Ok(ref text)) => Err(Error::UnexpectedLine(
            String::from_utf8_lossy(text).into_owned(),
            String::from_utf8_lossy(exp).into_owned(),
        )),
        Some(Err(e)) => Err(Error::SureFileError(e)),
        None => Err(Error::SureFileEof),
    }
}

pub struct ReadIterator<R> {
    lines: io::Split<BufReader<R>>,
    depth: usize,
    done: bool,
}

impl<R: Read> Iterator for ReadIterator<R> {
    type Item = Result<SureNode>;

    fn next(&mut self) -> Option<Result<SureNode>> {
        if self.done {
            return None;
        }

        let line = match self.get_line() {
            Ok(line) => line,
            Err(e) => return Some(Err(e)),
        };

        match line[0] {
            b'd' => {
                let (dname, datts) = decode_entity(&line[1..]);
                self.depth += 1;
                Some(Ok(SureNode::Enter {
                    name: dname,
                    atts: datts,
                }))
            }
            b'f' => {
                let (fname, fatts) = decode_entity(&line[1..]);
                Some(Ok(SureNode::File {
                    name: fname,
                    atts: fatts,
                }))
            }
            b'-' => Some(Ok(SureNode::Sep)),
            b'u' => {
                self.depth -= 1;
                if self.depth == 0 {
                    self.done = true;
                }
                Some(Ok(SureNode::Leave))
            }
            ch => Some(Err(Error::InvalidSurefileChar(ch as char))),
        }
    }
}

impl<R: Read> ReadIterator<R> {
    fn get_line(&mut self) -> Result<Vec<u8>> {
        match self.lines.next() {
            None => return Err(Error::TruncatedSurefile),
            Some(l) => Ok(l?),
        }
    }
}

// TODO: This should return Result to handle errors.
pub(crate) fn decode_entity(text: &[u8]) -> (String, AttMap) {
    let (name, mut text) = get_delim(text, b' ');
    assert!(text[0] == b'[');
    text = &text[1..];

    let mut atts = AttMap::new();
    while text[0] != b']' {
        let (key, t2) = get_delim(text, b' ');
        let (value, t2) = get_delim(t2, b' ');
        text = t2;

        atts.insert(key, value);
    }

    (name, atts)
}

fn get_delim(text: &[u8], delim: u8) -> (String, &[u8]) {
    let mut it = text.iter();
    let space = it.position(|&s| s == delim).unwrap();
    (
        String::from_utf8(text[..space].to_owned()).unwrap(),
        &text[space + 1..],
    )
}
<|endoftext|>


<commit_before>use std::io::Write;

use Result;
use layout::Element;

/// The SVG format.
pub struct SVG;

impl super::Format for SVG {
    fn write(&self, elements: &[Element], writer: &mut Write) -> Result<()> {
        use std::f64::{INFINITY, NEG_INFINITY};

        let (mut min_x, mut min_y) = (INFINITY, INFINITY);
        let (mut max_x, mut max_y) = (NEG_INFINITY, NEG_INFINITY);
        for &Element { position: (x, y), dimension: (width, height), .. } in elements {
            min_x = min_x.min(x);
            min_y = min_y.min(y);
            max_x = max_x.max(x + width);
            max_y = max_y.max(y + height);
        }

        const MARGIN: f64 = 5.0;
        const WIDTH: f64 = 800.0;

        let scale = WIDTH / (max_x - min_x);
        let height = (max_y - min_y) * scale;

        let mut body = String::new();
        for &Element { ref name, position: (x, y), dimension: (width, height) } in elements {
            body.push_str(&format!(
                "    <rect x=\"{:.0}\" \
                           y=\"{:.0}\" \
                           width=\"{:.0}\" \
                           height=\"{:.0}\" \
                           id=\"{}\" />\n",
                MARGIN + scale * (x - min_x), MARGIN + scale * (y - min_y),
                scale * width, scale * height, name,
            ));
        }

        macro_rules! template(
            () => (
r#"<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="{width:.0}" height="{height:.0}">
  <style type="text/css">
    rect {{
        fill: none;
        stroke: black;
        stroke-width: 2;
    }}
    rect[id^="Core"] {{
        fill: #0071bc;
    }}
    rect[id^="L3"] {{
        fill: #ecb01f;
    }}
  </style>
  <g transform="translate(0, {height:.0}) scale(1, -1)">
    {body}
  </g>
</svg>
"#
            );
        );

        ok!(writer.write_all(format!(template!(), width = WIDTH + 2.0 * MARGIN,
                                     height = height + 2.0 * MARGIN,
                                     body = body.trim()).as_bytes()));

        Ok(())
    }
}
<commit_msg>Refactor the SVG formatter<commit_after>use std::io::Write;

use Result;
use layout::Element;

const MARGIN: f64 = 5.0;
const WIDTH: f64 = 800.0;

macro_rules! element_template(
    () => (
r#"    <rect x="{x:.0}" y="{y:.0}" width="{width:.0}" height="{height:.0}" id="{id}" />
"#
    );
);

macro_rules! document_template(
    () => (
r#"<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="{width:.0}" height="{height:.0}">
  <style type="text/css">
    rect {{
        fill: none;
        stroke: black;
        stroke-width: 2;
    }}
    rect[id^="Core"] {{
        fill: #0071bc;
    }}
    rect[id^="L3"] {{
        fill: #ecb01f;
    }}
  </style>
  <g transform="translate(0, {height:.0}) scale(1, -1)">
    {body}
  </g>
</svg>
"#
    );
);

/// The SVG format.
pub struct SVG;

impl super::Format for SVG {
    fn write(&self, elements: &[Element], writer: &mut Write) -> Result<()> {
        use std::f64::{INFINITY, NEG_INFINITY};

        let (mut min_x, mut min_y) = (INFINITY, INFINITY);
        let (mut max_x, mut max_y) = (NEG_INFINITY, NEG_INFINITY);
        for &Element { position: (x, y), dimension: (width, height), .. } in elements {
            min_x = min_x.min(x);
            min_y = min_y.min(y);
            max_x = max_x.max(x + width);
            max_y = max_y.max(y + height);
        }

        let scale = WIDTH / (max_x - min_x);
        let height = (max_y - min_y) * scale;

        let mut body = String::new();
        for &Element { ref name, position: (x, y), dimension: (width, height) } in elements {
            body.push_str(&format!(element_template!(),
                                   id = name,
                                   x = MARGIN + scale * (x - min_x),
                                   y = MARGIN + scale * (y - min_y),
                                   width = scale * width,
                                   height = scale * height));
        }

        ok!(writer.write_fmt(format_args!(document_template!(),
                                          width = WIDTH + 2.0 * MARGIN,
                                          height = height + 2.0 * MARGIN,
                                          body = body.trim())));

        Ok(())
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Adrien Champion. See the COPYRIGHT file at the top-level
// directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*! Solver configuration, contains backend solver specific info. */

use std::fmt ;

use common::UnexSmtRes ;

use self::SolverStyle::* ;

/** A configuration item is either a keyword or unsupported. */
pub type ConfItem = Result<& 'static str, UnexSmtRes> ;

/** Value of an unsupported configuration item. */
#[inline(always)]
fn unsupported() -> ConfItem { Err(UnexSmtRes::Unsupported) }
/** Keyword for a configuration item. */
#[inline(always)]
fn supported(keyword: & 'static str) -> ConfItem { Ok(keyword) }

/** Solver styles. */
#[derive(Debug)]
pub enum SolverStyle {
  /** Z3-style smt solver. */
  Z3,
  /** CVC4-style smt solver. */
  CVC4,
}

impl SolverStyle {
  /** Default configuration for a solver style. */
  pub fn default(self) -> SolverConf {
    match self {
      Z3 => SolverConf {
        style: self,
        options: vec![
          "-in", "-smt2"
        ],
        parse_success: false,
        unsat_cores: false,
        check_sat_assuming: supported("check-sat"),
      },
      CVC4 => SolverConf {
        style: self,
        options: vec![
          "--smtlib-strict", "-qqqqq", "--interactive"
        ],
        parse_success: false,
        unsat_cores: false,
        check_sat_assuming: unsupported(),
      },
    }
  }
}

impl fmt::Display for SolverStyle {
  fn fmt(& self, fmt: & mut fmt::Formatter) -> fmt::Result {
    match * self {
      Z3 => write!(fmt, "z3"),
      CVC4 => write!(fmt, "cvc4"),
    }
  }
}

/** Configuration and solver specific info. */
pub struct SolverConf {
  /** Solver style. */
  style: SolverStyle,
  /** Options to call the solver with. */
  options: Vec<& 'static str>,
  /** Parse success. */
  parse_success: bool,
  /** Triggers unsat-core production. */
  unsat_cores: bool,
  /** Keyword for check-sat with assumptions. */
  check_sat_assuming: ConfItem,
}

impl SolverConf {
  /** Creates a new z3-like solver configuration. */
  #[inline(always)]
  pub fn z3() -> Self { Z3.default() }

  /** Creates a new CVC4-like solver configuration. */
  #[inline(always)]
  pub fn cvc4() -> Self { CVC4.default() }

  /** Solver style. */
  #[inline(always)]
  pub fn style(& self) -> & SolverStyle { & self.style }

  /** Options of the configuration. */
  #[inline(always)]
  pub fn get_options(& self) -> & [& 'static str] { & self.options }

  /** Indicates if parse success is active. */
  #[inline(always)]
  pub fn get_parse_success(& self) -> bool { self.parse_success }

  /** Keyword for check-sat with assumptions. */
  #[inline(always)]
  pub fn get_check_sat_assuming(& self) -> & ConfItem {
    & self.check_sat_assuming
  }

  /** Adds an option to the configuration. */
  #[inline(always)]
  pub fn option(mut self, o: & 'static str) -> Self {
    self.options.push(o) ;
    self
  }

  /** Activates parse sucess. */
  #[inline(always)]
  #[cfg(not(no_parse_success))]
  pub fn print_success(mut self) -> Self {
    self.parse_success = true ;
    self
  }

  /** Activates unsat-core production. */
  #[inline(always)]
  pub fn unsat_cores(mut self) -> Self {
    self.unsat_cores = true ;
    self
  }

  // /** Creates a solver instance. */
  // #[inline(always)]
  // pub fn mk<Parser>(
  //   self, cmd: Command, parser: Parser
  // ) -> io::Result<Solver<Parser>> {
  //   Solver::mk(cmd, self, parser)
  // }
}<commit_msg>Clone for SolverStyle<commit_after>// Copyright 2015 Adrien Champion. See the COPYRIGHT file at the top-level
// directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*! Solver configuration, contains backend solver specific info. */

use std::fmt ;

use common::UnexSmtRes ;

use self::SolverStyle::* ;

/** A configuration item is either a keyword or unsupported. */
pub type ConfItem = Result<& 'static str, UnexSmtRes> ;

/** Value of an unsupported configuration item. */
#[inline(always)]
fn unsupported() -> ConfItem { Err(UnexSmtRes::Unsupported) }
/** Keyword for a configuration item. */
#[inline(always)]
fn supported(keyword: & 'static str) -> ConfItem { Ok(keyword) }

/** Solver styles. */
#[derive(Debug, Clone)]
pub enum SolverStyle {
  /** Z3-style smt solver. */
  Z3,
  /** CVC4-style smt solver. */
  CVC4,
}

impl SolverStyle {
  /** Default configuration for a solver style. */
  pub fn default(self) -> SolverConf {
    match self {
      Z3 => SolverConf {
        style: self,
        options: vec![
          "-in", "-smt2"
        ],
        parse_success: false,
        unsat_cores: false,
        check_sat_assuming: supported("check-sat"),
      },
      CVC4 => SolverConf {
        style: self,
        options: vec![
          "--smtlib-strict", "-qqqqq", "--interactive"
        ],
        parse_success: false,
        unsat_cores: false,
        check_sat_assuming: unsupported(),
      },
    }
  }
}

impl fmt::Display for SolverStyle {
  fn fmt(& self, fmt: & mut fmt::Formatter) -> fmt::Result {
    match * self {
      Z3 => write!(fmt, "z3"),
      CVC4 => write!(fmt, "cvc4"),
    }
  }
}

/** Configuration and solver specific info. */
pub struct SolverConf {
  /** Solver style. */
  style: SolverStyle,
  /** Options to call the solver with. */
  options: Vec<& 'static str>,
  /** Parse success. */
  parse_success: bool,
  /** Triggers unsat-core production. */
  unsat_cores: bool,
  /** Keyword for check-sat with assumptions. */
  check_sat_assuming: ConfItem,
}

impl SolverConf {
  /** Creates a new z3-like solver configuration. */
  #[inline(always)]
  pub fn z3() -> Self { Z3.default() }

  /** Creates a new CVC4-like solver configuration. */
  #[inline(always)]
  pub fn cvc4() -> Self { CVC4.default() }

  /** Solver style. */
  #[inline(always)]
  pub fn style(& self) -> & SolverStyle { & self.style }

  /** Options of the configuration. */
  #[inline(always)]
  pub fn get_options(& self) -> & [& 'static str] { & self.options }

  /** Indicates if parse success is active. */
  #[inline(always)]
  pub fn get_parse_success(& self) -> bool { self.parse_success }

  /** Keyword for check-sat with assumptions. */
  #[inline(always)]
  pub fn get_check_sat_assuming(& self) -> & ConfItem {
    & self.check_sat_assuming
  }

  /** Adds an option to the configuration. */
  #[inline(always)]
  pub fn option(mut self, o: & 'static str) -> Self {
    self.options.push(o) ;
    self
  }

  /** Activates parse sucess. */
  #[inline(always)]
  #[cfg(not(no_parse_success))]
  pub fn print_success(mut self) -> Self {
    self.parse_success = true ;
    self
  }

  /** Activates unsat-core production. */
  #[inline(always)]
  pub fn unsat_cores(mut self) -> Self {
    self.unsat_cores = true ;
    self
  }

  // /** Creates a solver instance. */
  // #[inline(always)]
  // pub fn mk<Parser>(
  //   self, cmd: Command, parser: Parser
  // ) -> io::Result<Solver<Parser>> {
  //   Solver::mk(cmd, self, parser)
  // }
}<|endoftext|>


<commit_before>//! The `conf` module contains functions for loading and saving game
//! configurations.
//!
//! A `Conf` struct is used to specify hardware setup stuff used to create
//! the window and other context information.
//!
//! By default a ggez game will search its resource paths for a `/conf.toml`
//! file and load values from it when the `Context` is created.  This file
//! must be complete (ie you cannot just fill in some fields and have the
//! rest be default) and provides a nice way to specify settings that
//! can be tweaked such as window resolution, multisampling options, etc.

use std::io;
use toml;

use GameResult;

/// Possible fullscreen modes.
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum FullscreenType {
    /// Windowed mode
    Off,
    /// Real fullscreen
    True,
    /// Windowed fullscreen, generally preferred over real fullscreen
    /// these days 'cause it plays nicer with multiple monitors.
    Desktop,
}

use sdl2::video::FullscreenType as SdlFullscreenType;
impl From<SdlFullscreenType> for FullscreenType {
    fn from(other: SdlFullscreenType) -> Self {
        match other {
            SdlFullscreenType::Off => FullscreenType::Off,
            SdlFullscreenType::True => FullscreenType::True,
            SdlFullscreenType::Desktop => FullscreenType::Desktop,
        }
    }
}

impl From<FullscreenType> for SdlFullscreenType {
    fn from(other: FullscreenType) -> Self {
        match other {
            FullscreenType::Off => SdlFullscreenType::Off,
            FullscreenType::True => SdlFullscreenType::True,
            FullscreenType::Desktop => SdlFullscreenType::Desktop,
        }
    }
}

/// A builder structure containing window settings
/// that can be set at runtime and changed with `graphics::set_mode()`
///
/// Defaults:
///
/// ```rust,ignore
/// WindowMode {
///     width: 800,
///     height: 600,
///     borderless: false,
///     fullscreen_type: FullscreenType::Off,
///     vsync: true,
///     min width: 0,
///     max_width: 0,
///     min_height: 0,
///     max_height: 0,
/// }
/// ```
#[derive(Debug, Copy, Clone, SmartDefault, Serialize, Deserialize, PartialEq, Eq)]
pub struct WindowMode {
    /// Window width
    #[default = r#"800"#]
    pub width: u32,
    /// Window height
    #[default = r#"600"#]
    pub height: u32,
    /// Whether or not to show window decorations
    #[default = r#"false"#]
    pub borderless: bool,
    /// Fullscreen type
    #[default = r#"FullscreenType::Off"#]
    pub fullscreen_type: FullscreenType,
    /// Whether or not to enable vsync
    #[default = r#"true"#]
    pub vsync: bool,
    /// Minimum width for resizable windows; 0 means no limit
    #[default = r#"0"#]
    pub min_width: u32,
    /// Minimum height for resizable windows; 0 means no limit
    #[default = r#"0"#]
    pub min_height: u32,
    /// Maximum width for resizable windows; 0 means no limit
    #[default = r#"0"#]
    pub max_width: u32,
    /// Maximum height for resizable windows; 0 means no limit
    #[default = r#"0"#]
    pub max_height: u32,
}

impl WindowMode {
    /// Set borderless
    pub fn borderless(mut self, borderless: bool) -> Self {
        self.borderless = borderless;
        self
    }

    /// Set the fullscreen type
    pub fn fullscreen_type(mut self, fullscreen_type: FullscreenType) -> Self {
        self.fullscreen_type = fullscreen_type;
        self
    }

    /// Set vsync
    pub fn vsync(mut self, vsync: bool) -> Self {
        self.vsync = vsync;
        self
    }

    /// Set default window size, or screen resolution in fullscreen mode
    pub fn dimensions(mut self, width: u32, height: u32) -> Self {
        self.width = width;
        self.height = height;
        self
    }

    /// Set minimum window dimensions for windowed mode
    pub fn min_dimensions(mut self, width: u32, height: u32) -> Self {
        self.min_width = width;
        self.min_height = height;
        self
    }

    /// Set maximum window dimensions for windowed mode
    pub fn max_dimensions(mut self, width: u32, height: u32) -> Self {
        self.max_width = width;
        self.max_height = height;
        self
    }
}

/// A builder structure containing window settings
/// that must be set at init time and cannot be changed afterwards.
///
/// Defaults:
///
/// ```rust,ignore
/// WindowSetup {
///     title: "An easy, good game".to_owned(),
///     icon: "".to_owned(),
///     resizable: false,
///     allow_highdpi: true,
///     samples: NumSamples::One,
/// }
/// ```
#[derive(Debug, Clone, SmartDefault, Serialize, Deserialize, PartialEq, Eq)]
pub struct WindowSetup {
    /// The window title.
    #[default = r#""An easy, good game".to_owned()"#]
    pub title: String,
    /// A file path to the window's icon.
    /// It is rooted in the `resources` directory (see the `filesystem` module for details),
    /// and an empty string results in a blank/default icon.
    #[default = r#""".to_owned()"#]
    pub icon: String,
    /// Whether or not the window is resizable
    #[default = r#"false"#]
    pub resizable: bool,
    /// Whether or not to allow high DPI mode when creating the window
    #[default = r#"true"#]
    pub allow_highdpi: bool,
    /// Number of samples for multisample anti-aliasing
    #[default = r#"NumSamples::One"#]
    pub samples: NumSamples,
}

impl WindowSetup {
    /// Set window title
    pub fn title(mut self, title: &str) -> Self {
        self.title = title.to_owned();
        self
    }

    /// Set the window's icon.
    pub fn icon(mut self, icon: &str) -> Self {
        self.icon = icon.to_owned();
        self
    }

    /// Set resizable
    pub fn resizable(mut self, resizable: bool) -> Self {
        self.resizable = resizable;
        self
    }

    /// Set allow_highdpi
    pub fn allow_highdpi(mut self, allow: bool) -> Self {
        self.allow_highdpi = allow;
        self
    }

    /// Set number of samples
    ///
    /// Returns None if given an invalid value
    /// (valid values are powers of 2 from 1 to 16)
    pub fn samples(mut self, samples: u32) -> Option<Self> {
        match NumSamples::from_u32(samples) {
            Some(s) => {
                self.samples = s;
                Some(self)
            }
            None => None,
        }
    }
}

/// Possible backends.
/// Currently, only OpenGL Core spec is supported,
/// but this lets you specify the version numbers.
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, SmartDefault)]
#[serde(tag = "type")]
pub enum Backend {
    /// Defaults to OpenGL 3.2, which is supported by basically
    /// every machine since 2009 or so (apart from the ones that don't)
    #[default]
    OpenGL {
        /// OpenGL major version
        #[default = r#"3"#]
        major: u8,
        /// OpenGL minor version
        #[default = r#"2"#]
        minor: u8,
    },
}

/// The possible number of samples for multisample anti-aliasing
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum NumSamples {
    /// One sample
    One = 1,
    /// Two samples
    Two = 2,
    /// Four samples
    Four = 4,
    /// Eight samples
    Eight = 8,
    /// Sixteen samples
    Sixteen = 16,
}

impl NumSamples {
    /// Create a NumSamples from a number.
    /// Returns None if i is invalid.
    pub fn from_u32(i: u32) -> Option<NumSamples> {
        match i {
            1 => Some(NumSamples::One),
            2 => Some(NumSamples::Two),
            4 => Some(NumSamples::Four),
            8 => Some(NumSamples::Eight),
            16 => Some(NumSamples::Sixteen),
            _ => None,
        }
    }
}

/// A structure containing configuration data
/// for the game engine.
///
/// Defaults:
///
/// ```rust,ignore
/// Conf {
///     window_mode: WindowMode::default(),
///     window_setup: WindowSetup::default(),
///     backend: Backend::OpenGL(3, 2),
/// }
/// ```
#[derive(Serialize, Deserialize, Debug, PartialEq, SmartDefault)]
pub struct Conf {
    /// Window setting information that can be set at runtime
    pub window_mode: WindowMode,
    /// Window setting information that must be set at init-time
    pub window_setup: WindowSetup,
    /// Backend configuration
    pub backend: Backend,
}

impl Conf {
    /// Same as Conf::default()
    pub fn new() -> Self {
        Self::default()
    }

    /// Load a TOML file from the given `Read` and attempts to parse
    /// a `Conf` from it.
    pub fn from_toml_file<R: io::Read>(file: &mut R) -> GameResult<Conf> {
        let mut s = String::new();
        file.read_to_string(&mut s)?;
        let decoded = toml::from_str(&s)?;
        Ok(decoded)
    }

    /// Saves the `Conf` to the given `Write` object,
    /// formatted as TOML.
    pub fn to_toml_file<W: io::Write>(&self, file: &mut W) -> GameResult<()> {
        let s = toml::to_vec(self)?;
        file.write_all(&s)?;
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use conf;

    /// Tries to encode and decode a `Conf` object
    /// and makes sure it gets the same result it had.
    #[test]
    fn encode_round_trip() {
        let c1 = conf::Conf::new();
        let mut writer = Vec::new();
        let _c = c1.to_toml_file(&mut writer).unwrap();
        let mut reader = writer.as_slice();
        let c2 = conf::Conf::from_toml_file(&mut reader).unwrap();
        assert_eq!(c1, c2);
    }
}
<commit_msg>conf.rs: Fix typo in WindowMode documentation<commit_after>//! The `conf` module contains functions for loading and saving game
//! configurations.
//!
//! A `Conf` struct is used to specify hardware setup stuff used to create
//! the window and other context information.
//!
//! By default a ggez game will search its resource paths for a `/conf.toml`
//! file and load values from it when the `Context` is created.  This file
//! must be complete (ie you cannot just fill in some fields and have the
//! rest be default) and provides a nice way to specify settings that
//! can be tweaked such as window resolution, multisampling options, etc.

use std::io;
use toml;

use GameResult;

/// Possible fullscreen modes.
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum FullscreenType {
    /// Windowed mode
    Off,
    /// Real fullscreen
    True,
    /// Windowed fullscreen, generally preferred over real fullscreen
    /// these days 'cause it plays nicer with multiple monitors.
    Desktop,
}

use sdl2::video::FullscreenType as SdlFullscreenType;
impl From<SdlFullscreenType> for FullscreenType {
    fn from(other: SdlFullscreenType) -> Self {
        match other {
            SdlFullscreenType::Off => FullscreenType::Off,
            SdlFullscreenType::True => FullscreenType::True,
            SdlFullscreenType::Desktop => FullscreenType::Desktop,
        }
    }
}

impl From<FullscreenType> for SdlFullscreenType {
    fn from(other: FullscreenType) -> Self {
        match other {
            FullscreenType::Off => SdlFullscreenType::Off,
            FullscreenType::True => SdlFullscreenType::True,
            FullscreenType::Desktop => SdlFullscreenType::Desktop,
        }
    }
}

/// A builder structure containing window settings
/// that can be set at runtime and changed with `graphics::set_mode()`
///
/// Defaults:
///
/// ```rust,ignore
/// WindowMode {
///     width: 800,
///     height: 600,
///     borderless: false,
///     fullscreen_type: FullscreenType::Off,
///     vsync: true,
///     min_width: 0,
///     max_width: 0,
///     min_height: 0,
///     max_height: 0,
/// }
/// ```
#[derive(Debug, Copy, Clone, SmartDefault, Serialize, Deserialize, PartialEq, Eq)]
pub struct WindowMode {
    /// Window width
    #[default = r#"800"#]
    pub width: u32,
    /// Window height
    #[default = r#"600"#]
    pub height: u32,
    /// Whether or not to show window decorations
    #[default = r#"false"#]
    pub borderless: bool,
    /// Fullscreen type
    #[default = r#"FullscreenType::Off"#]
    pub fullscreen_type: FullscreenType,
    /// Whether or not to enable vsync
    #[default = r#"true"#]
    pub vsync: bool,
    /// Minimum width for resizable windows; 0 means no limit
    #[default = r#"0"#]
    pub min_width: u32,
    /// Minimum height for resizable windows; 0 means no limit
    #[default = r#"0"#]
    pub min_height: u32,
    /// Maximum width for resizable windows; 0 means no limit
    #[default = r#"0"#]
    pub max_width: u32,
    /// Maximum height for resizable windows; 0 means no limit
    #[default = r#"0"#]
    pub max_height: u32,
}

impl WindowMode {
    /// Set borderless
    pub fn borderless(mut self, borderless: bool) -> Self {
        self.borderless = borderless;
        self
    }

    /// Set the fullscreen type
    pub fn fullscreen_type(mut self, fullscreen_type: FullscreenType) -> Self {
        self.fullscreen_type = fullscreen_type;
        self
    }

    /// Set vsync
    pub fn vsync(mut self, vsync: bool) -> Self {
        self.vsync = vsync;
        self
    }

    /// Set default window size, or screen resolution in fullscreen mode
    pub fn dimensions(mut self, width: u32, height: u32) -> Self {
        self.width = width;
        self.height = height;
        self
    }

    /// Set minimum window dimensions for windowed mode
    pub fn min_dimensions(mut self, width: u32, height: u32) -> Self {
        self.min_width = width;
        self.min_height = height;
        self
    }

    /// Set maximum window dimensions for windowed mode
    pub fn max_dimensions(mut self, width: u32, height: u32) -> Self {
        self.max_width = width;
        self.max_height = height;
        self
    }
}

/// A builder structure containing window settings
/// that must be set at init time and cannot be changed afterwards.
///
/// Defaults:
///
/// ```rust,ignore
/// WindowSetup {
///     title: "An easy, good game".to_owned(),
///     icon: "".to_owned(),
///     resizable: false,
///     allow_highdpi: true,
///     samples: NumSamples::One,
/// }
/// ```
#[derive(Debug, Clone, SmartDefault, Serialize, Deserialize, PartialEq, Eq)]
pub struct WindowSetup {
    /// The window title.
    #[default = r#""An easy, good game".to_owned()"#]
    pub title: String,
    /// A file path to the window's icon.
    /// It is rooted in the `resources` directory (see the `filesystem` module for details),
    /// and an empty string results in a blank/default icon.
    #[default = r#""".to_owned()"#]
    pub icon: String,
    /// Whether or not the window is resizable
    #[default = r#"false"#]
    pub resizable: bool,
    /// Whether or not to allow high DPI mode when creating the window
    #[default = r#"true"#]
    pub allow_highdpi: bool,
    /// Number of samples for multisample anti-aliasing
    #[default = r#"NumSamples::One"#]
    pub samples: NumSamples,
}

impl WindowSetup {
    /// Set window title
    pub fn title(mut self, title: &str) -> Self {
        self.title = title.to_owned();
        self
    }

    /// Set the window's icon.
    pub fn icon(mut self, icon: &str) -> Self {
        self.icon = icon.to_owned();
        self
    }

    /// Set resizable
    pub fn resizable(mut self, resizable: bool) -> Self {
        self.resizable = resizable;
        self
    }

    /// Set allow_highdpi
    pub fn allow_highdpi(mut self, allow: bool) -> Self {
        self.allow_highdpi = allow;
        self
    }

    /// Set number of samples
    ///
    /// Returns None if given an invalid value
    /// (valid values are powers of 2 from 1 to 16)
    pub fn samples(mut self, samples: u32) -> Option<Self> {
        match NumSamples::from_u32(samples) {
            Some(s) => {
                self.samples = s;
                Some(self)
            }
            None => None,
        }
    }
}

/// Possible backends.
/// Currently, only OpenGL Core spec is supported,
/// but this lets you specify the version numbers.
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, SmartDefault)]
#[serde(tag = "type")]
pub enum Backend {
    /// Defaults to OpenGL 3.2, which is supported by basically
    /// every machine since 2009 or so (apart from the ones that don't)
    #[default]
    OpenGL {
        /// OpenGL major version
        #[default = r#"3"#]
        major: u8,
        /// OpenGL minor version
        #[default = r#"2"#]
        minor: u8,
    },
}

/// The possible number of samples for multisample anti-aliasing
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum NumSamples {
    /// One sample
    One = 1,
    /// Two samples
    Two = 2,
    /// Four samples
    Four = 4,
    /// Eight samples
    Eight = 8,
    /// Sixteen samples
    Sixteen = 16,
}

impl NumSamples {
    /// Create a NumSamples from a number.
    /// Returns None if i is invalid.
    pub fn from_u32(i: u32) -> Option<NumSamples> {
        match i {
            1 => Some(NumSamples::One),
            2 => Some(NumSamples::Two),
            4 => Some(NumSamples::Four),
            8 => Some(NumSamples::Eight),
            16 => Some(NumSamples::Sixteen),
            _ => None,
        }
    }
}

/// A structure containing configuration data
/// for the game engine.
///
/// Defaults:
///
/// ```rust,ignore
/// Conf {
///     window_mode: WindowMode::default(),
///     window_setup: WindowSetup::default(),
///     backend: Backend::OpenGL(3, 2),
/// }
/// ```
#[derive(Serialize, Deserialize, Debug, PartialEq, SmartDefault)]
pub struct Conf {
    /// Window setting information that can be set at runtime
    pub window_mode: WindowMode,
    /// Window setting information that must be set at init-time
    pub window_setup: WindowSetup,
    /// Backend configuration
    pub backend: Backend,
}

impl Conf {
    /// Same as Conf::default()
    pub fn new() -> Self {
        Self::default()
    }

    /// Load a TOML file from the given `Read` and attempts to parse
    /// a `Conf` from it.
    pub fn from_toml_file<R: io::Read>(file: &mut R) -> GameResult<Conf> {
        let mut s = String::new();
        file.read_to_string(&mut s)?;
        let decoded = toml::from_str(&s)?;
        Ok(decoded)
    }

    /// Saves the `Conf` to the given `Write` object,
    /// formatted as TOML.
    pub fn to_toml_file<W: io::Write>(&self, file: &mut W) -> GameResult<()> {
        let s = toml::to_vec(self)?;
        file.write_all(&s)?;
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use conf;

    /// Tries to encode and decode a `Conf` object
    /// and makes sure it gets the same result it had.
    #[test]
    fn encode_round_trip() {
        let c1 = conf::Conf::new();
        let mut writer = Vec::new();
        let _c = c1.to_toml_file(&mut writer).unwrap();
        let mut reader = writer.as_slice();
        let c2 = conf::Conf::from_toml_file(&mut reader).unwrap();
        assert_eq!(c1, c2);
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Nikita Pekin and the smexybot contributors
// See the README.md file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Provides the a command which allows a user to query the Wolfram|Alpha API.

extern crate wolfram_alpha;

use std::env;
use std::error::Error as StdError;

use hyper::Client;
use self::wolfram_alpha::Error as WolframError;
use self::wolfram_alpha::model::{Pod, QueryResult};
use serenity::client::Context;
use serenity::model::Message;
use serenity::utils::builder::{CreateEmbed, CreateEmbedField};

use util::{check_msg, random_colour};

lazy_static! {
    static ref PLUGIN: WolframPlugin = {
        let api_app_id = env::var("WOLFRAM_ALPHA_API_APP_ID")
            .expect("WOLFRAM_ALPHA_API_APP_ID env var not set");
        WolframPlugin::new(api_app_id)
    };
}

pub struct WolframPlugin {
    app_id: String,
    hyper_client: Client,
}

impl WolframPlugin {
    /// Returns a new instance of `WolframPlugin`.
    pub fn new(wolfram_alpha_api_app_id: String) -> Self {
        WolframPlugin {
            app_id: wolfram_alpha_api_app_id,
            hyper_client: Client::new(),
        }
    }

    fn query(&self, args: &[String]) -> Result<QueryResult, String> {
        let query = match args.len() {
            0 => return Err("Missing WolframAlpha query".to_owned()),
            _ => args.join(" "),
        };
        trace!("WolframAlpha query: {}", query);

        match wolfram_alpha::query::query(
            &self.hyper_client,
            &self.app_id,
            &query,
            None,
        ) {
            Ok(query_result) => Ok(query_result),
            Err(e) => {
                let description = match e {
                    WolframError::Xml(_) => "failed to parse response",
                    _ => e.description(),
                };
                Err(format!("Failed to query WolframAlpha: {}", description))
            },
        }
    }
}

pub fn handler(context: &Context, message: &Message, args: Vec<String>)
    -> Result<(), String>
{
    // TODO: handle this properly.
    if let Err(err) = context.broadcast_typing(message.channel_id) {
        return Err(format!("{:?}", err));
    }

    match PLUGIN.query(&args) {
        Ok(query_result) => {
            if query_result.success {
                // Format the `QueryResult` into Discord-ready output.
                let colour = random_colour();
                // TODO: get rid of the unwrap here.
                let pods = query_result.pod.unwrap();
                check_msg(context.send_message(
                    message.channel_id,
                    |m| m.embed(|e| format_pods(&pods, e).colour(colour)),
                ));
            } else if let Some(didyoumeans) = query_result.didyoumeans {
                    let colour = random_colour();
                    check_msg(context.send_message(
                        message.channel_id,
                        |m| m.embed(|e| e
                            .title("Query unsuccessful.")
                            .colour(colour)
                            .field(|f| {
                                let field = f.name("Did you mean:");

                                let mut description = String::new();
                                for item in &didyoumeans.didyoumean {
                                    description.push_str(&format!("* {}\n", item.value));
                                }
                                field.value(description.as_str())
                            })
                        ),
                    ));
            } else if let Some(error) = query_result.error {
                    let colour = random_colour();
                    check_msg(context.send_message(
                        message.channel_id,
                        |m| m.embed(|e| e
                            .title("Wolfram|Alpha returned an error.")
                            .colour(colour)
                            .field(|f| {
                                let field = f.name("Error");

                                let description = format!(
                                    "Code: {}\nMessage: {}",
                                    error.code,
                                    error.msg,
                                );
                                field.value(description.as_str())
                            })
                        ),
                    ));
            } else {
                check_msg(context.say("Query was unsuccessful. Perhaps try rewording it?"));
            }
        },
        Err(err) => check_msg(context.say(err.as_ref())),
    }

    Ok(())
}

fn format_pods(pods: &[Pod], embed: CreateEmbed) -> CreateEmbed {
    let mut iter = pods.iter();

    // First result is the interpretation.
    let interpretation = iter.next()
        .unwrap()
        .subpod[0]
        .plaintext
        .clone()
        .unwrap_or("".to_owned());
    //let mut embed = embed.title(&format!("**Interpretation**: `{}`", &interpretation));
    let mut embed = embed.title("Input interpretation");
    embed = embed.description(&format!("`{}`", interpretation));

    // TODO: first re-upload the image to an image host, prior the setting the
    // URL.
    if let Some(img) = pods.iter().skip(1).filter_map(|p| p.subpod.iter().filter_map(|s| s.img.clone()).next()).next() {
        embed = embed.image(|i| i.url(unescape(img.src.as_str()).as_ref()));
    }

    // If there is a primary pod, then only format and print that pod.
    if let Some(pod) = pods.iter().find(|p| p.primary == Some(true)) {
        embed = embed.field(|f| format_pod(pod, f));
    } else {
        // Parse all the remaining pods.
        for pod in iter {
            embed = embed.field(|f| format_pod(pod, f.inline(false)));
        }
    }

    embed
}

fn format_pod(pod: &Pod, f: CreateEmbedField) -> CreateEmbedField {
    let f = f.name(pod.title.as_ref());

    trace!("Formatting {} subpods", pod.subpod.len());
    let mut result = String::new();
    for subpod in &pod.subpod {
        let text = match subpod.plaintext {
            // If the text field is empty, add a message indicating as such
            // (discord doesn't like empty/just newline fields).
            Some(ref text) if text == "" || text == "\n" => {
                "[No text]".to_owned()
            },
            // Grab all the consecutive blobs of text.
            Some(ref text) => {
                trace!("Adding text: {}", text);
                text.to_owned()
            },
            // If there's no text, then there's usually an image.
            _ => {
                match subpod.img {
                    Some(ref img) => {
                        let url = unescape(img.src.as_ref());
                        trace!("Adding img src: {}", url);
                        url
                    },
                    None => break,
                }
            },
        };

        result.push_str(&format!("{}\n", &text));
    }

    // If the output is too long to fit in a single field, truncate it.
    if result.len() > 1024 {
        let truncation_msg = "... (output too long)".to_owned();
        result.truncate(1024 - truncation_msg.len());
        result.push_str(truncation_msg.as_str());
    }

    f.value(result.as_ref())
}

#[inline]
// TODO: find a better way to do this.
fn unescape(s: &str) -> String {
    s.replace("&", "&")
        .replace(">", ">")
        .replace("<", "<")
        .replace(""", "\"")
        .replace("'", "\'")
}
<commit_msg>fix: fix image URL generation for embed<commit_after>// Copyright (c) 2016 Nikita Pekin and the smexybot contributors
// See the README.md file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Provides the a command which allows a user to query the Wolfram|Alpha API.

extern crate wolfram_alpha;

use std::env;
use std::error::Error as StdError;

use hyper::Client;
use self::wolfram_alpha::Error as WolframError;
use self::wolfram_alpha::model::{Pod, QueryResult};
use serenity::client::Context;
use serenity::model::Message;
use serenity::utils::builder::{CreateEmbed, CreateEmbedField};

use util::{check_msg, random_colour};

lazy_static! {
    static ref PLUGIN: WolframPlugin = {
        let api_app_id = env::var("WOLFRAM_ALPHA_API_APP_ID")
            .expect("WOLFRAM_ALPHA_API_APP_ID env var not set");
        WolframPlugin::new(api_app_id)
    };
}

pub struct WolframPlugin {
    app_id: String,
    hyper_client: Client,
}

impl WolframPlugin {
    /// Returns a new instance of `WolframPlugin`.
    pub fn new(wolfram_alpha_api_app_id: String) -> Self {
        WolframPlugin {
            app_id: wolfram_alpha_api_app_id,
            hyper_client: Client::new(),
        }
    }

    fn query(&self, args: &[String]) -> Result<QueryResult, String> {
        let query = match args.len() {
            0 => return Err("Missing WolframAlpha query".to_owned()),
            _ => args.join(" "),
        };
        trace!("WolframAlpha query: {}", query);

        match wolfram_alpha::query::query(
            &self.hyper_client,
            &self.app_id,
            &query,
            None,
        ) {
            Ok(query_result) => Ok(query_result),
            Err(e) => {
                let description = match e {
                    WolframError::Xml(_) => "failed to parse response",
                    _ => e.description(),
                };
                Err(format!("Failed to query WolframAlpha: {}", description))
            },
        }
    }
}

pub fn handler(context: &Context, message: &Message, args: Vec<String>)
    -> Result<(), String>
{
    // TODO: handle this properly.
    if let Err(err) = context.broadcast_typing(message.channel_id) {
        return Err(format!("{:?}", err));
    }

    match PLUGIN.query(&args) {
        Ok(query_result) => {
            if query_result.success {
                // Format the `QueryResult` into Discord-ready output.
                let colour = random_colour();
                // TODO: get rid of the unwrap here.
                let pods = query_result.pod.unwrap();
                check_msg(context.send_message(
                    message.channel_id,
                    |m| m.embed(|e| format_pods(&pods, e).colour(colour)),
                ));
            } else if let Some(didyoumeans) = query_result.didyoumeans {
                    let colour = random_colour();
                    check_msg(context.send_message(
                        message.channel_id,
                        |m| m.embed(|e| e
                            .title("Query unsuccessful.")
                            .colour(colour)
                            .field(|f| {
                                let field = f.name("Did you mean:");

                                let mut description = String::new();
                                for item in &didyoumeans.didyoumean {
                                    description.push_str(&format!("* {}\n", item.value));
                                }
                                field.value(description.as_str())
                            })
                        ),
                    ));
            } else if let Some(error) = query_result.error {
                    let colour = random_colour();
                    check_msg(context.send_message(
                        message.channel_id,
                        |m| m.embed(|e| e
                            .title("Wolfram|Alpha returned an error.")
                            .colour(colour)
                            .field(|f| {
                                let field = f.name("Error");

                                let description = format!(
                                    "Code: {}\nMessage: {}",
                                    error.code,
                                    error.msg,
                                );
                                field.value(description.as_str())
                            })
                        ),
                    ));
            } else {
                check_msg(context.say("Query was unsuccessful. Perhaps try rewording it?"));
            }
        },
        Err(err) => check_msg(context.say(err.as_ref())),
    }

    Ok(())
}

fn format_pods(pods: &[Pod], embed: CreateEmbed) -> CreateEmbed {
    let mut iter = pods.iter();

    // First result is the interpretation.
    let interpretation = iter.next()
        .unwrap()
        .subpod[0]
        .plaintext
        .clone()
        .unwrap_or("".to_owned());
    //let mut embed = embed.title(&format!("**Interpretation**: `{}`", &interpretation));
    let mut embed = embed.title("Input interpretation");
    embed = embed.description(&format!("`{}`", interpretation));

    // TODO: first re-upload the image to an image host, prior the setting the
    // URL.
    if let Some(img) = pods.iter().skip(1).filter_map(|p| p.subpod.iter().filter_map(|s| s.img.clone()).next()).next() {
        embed = embed.image(unescape(img.src.as_str()).as_ref());
    }

    // If there is a primary pod, then only format and print that pod.
    if let Some(pod) = pods.iter().find(|p| p.primary == Some(true)) {
        embed = embed.field(|f| format_pod(pod, f));
    } else {
        // Parse all the remaining pods.
        for pod in iter {
            embed = embed.field(|f| format_pod(pod, f.inline(false)));
        }
    }

    embed
}

fn format_pod(pod: &Pod, f: CreateEmbedField) -> CreateEmbedField {
    let f = f.name(pod.title.as_ref());

    trace!("Formatting {} subpods", pod.subpod.len());
    let mut result = String::new();
    for subpod in &pod.subpod {
        let text = match subpod.plaintext {
            // If the text field is empty, add a message indicating as such
            // (discord doesn't like empty/just newline fields).
            Some(ref text) if text == "" || text == "\n" => {
                "[No text]".to_owned()
            },
            // Grab all the consecutive blobs of text.
            Some(ref text) => {
                trace!("Adding text: {}", text);
                text.to_owned()
            },
            // If there's no text, then there's usually an image.
            _ => {
                match subpod.img {
                    Some(ref img) => {
                        let url = unescape(img.src.as_ref());
                        trace!("Adding img src: {}", url);
                        url
                    },
                    None => break,
                }
            },
        };

        result.push_str(&format!("{}\n", &text));
    }

    // If the output is too long to fit in a single field, truncate it.
    if result.len() > 1024 {
        let truncation_msg = "... (output too long)".to_owned();
        result.truncate(1024 - truncation_msg.len());
        result.push_str(truncation_msg.as_str());
    }

    f.value(result.as_ref())
}

#[inline]
// TODO: find a better way to do this.
fn unescape(s: &str) -> String {
    s.replace("&", "&")
        .replace(">", ">")
        .replace("<", "<")
        .replace(""", "\"")
        .replace("'", "\'")
}
<|endoftext|>


<commit_before>//!
//! Desync pipes provide a way to generate and process streams via a `Desync` object
//!
//! Pipes are an excellent way to interface `Desync` objects and the futures library. Piping
//! a stream into a `Desync` object is equivalent to spawning it with an executor, except
//! without the need to dedicate a thread to running it.
//!
//! There are two kinds of pipe. The `pipe_in` function creates a pipe that processes each
//! value made available from a stream on a desync object as they arrive, producing no
//! results. This is useful for cases where a `Desync` object is being used as the endpoint
//! for some data processing (for example, to insert the results of an operation into an
//! asynchronous database object).
//!
//! The `pipe` function pipes data through an object. For every input value, it produces
//! an output value. This is good for creating streams that perform some kind of asynchronous
//! processing operation or that need to access data from inside a `Desync` object.
//!
//! Here's an example of using `pipe_in` to store data in a `HashSet`:
//!
//! ```
//! # extern crate futures;
//! # extern crate desync;
//! # use std::collections::HashSet;
//! # use std::sync::*;
//! #
//! use futures::sync::mpsc;
//! use futures::executor;
//! use desync::*;
//!
//! let desync_hashset      = Arc::new(Desync::new(HashSet::new()));
//! let (sender, receiver)  = mpsc::channel(5);
//!
//! pipe_in(Arc::clone(&desync_hashset), receiver, |hashset, value| { value.map(|value| hashset.insert(value)); });
//!
//! let mut sender = executor::spawn(sender);
//! sender.wait_send("Test".to_string());
//! sender.wait_send("Another value".to_string());
//! #
//! # assert!(desync_hashset.sync(|hashset| hashset.contains(&("Test".to_string()))))
//! ```
//!

use super::desync::*;

use futures::*;
use futures::executor;
use futures::executor::Spawn;

use std::sync::*;
use std::result::Result;
use std::collections::VecDeque;

lazy_static! {
    /// The shared queue where we monitor for updates to the active pipe streams
    static ref PIPE_MONITOR: PipeMonitor = PipeMonitor::new();
}

/// The maximum number of items to queue on a pipe stream before we stop accepting new input
const PIPE_BACKPRESSURE_COUNT: usize = 5;

///
/// Pipes a stream into a desync object. Whenever an item becomes available on the stream, the
/// processing function is called asynchronously with the item that was received.
///
/// This takes a weak reference to the passed in `Desync` object, so the pipe will stop if it's
/// the only thing referencing this object.
///
/// Piping a stream to a `Desync` like this will cause it to start executing: ie, this is
/// similar to calling `executor::spawn(stream)`, except that the stream will immediately
/// start draining into the `Desync` object.
///
pub fn pipe_in<Core, S, ProcessFn>(desync: Arc<Desync<Core>>, stream: S, process: ProcessFn)
where   Core:       'static+Send,
        S:          'static+Send+Stream,
        S::Item:    Send,
        S::Error:   Send,
        ProcessFn:  'static+Send+FnMut(&mut Core, Result<S::Item, S::Error>) -> () {

    // Need a mutable version of the stream
    let mut stream = stream;

    // We stop processing once the desync object is no longer used anywhere else
    let desync = Arc::downgrade(&desync);

    // Wrap the process fn up so we can call it asynchronously
    // (it doesn't really need to be in a mutex as it's only called by our object but we need to make it pass Rust's checks and we don't have a way to specify this at the moment)
    let process = Arc::new(Mutex::new(process));

    // Monitor the stream
    PIPE_MONITOR.monitor(move || {
        loop {
            let desync      = desync.upgrade();

            if let Some(desync) = desync {
                // Read the current status of the stream
                let process     = Arc::clone(&process);
                let next        = stream.poll();

                match next {
                    // Just wait if the stream is not ready
                    Ok(Async::NotReady) => { return Ok(Async::NotReady); },

                    // Stop processing when the stream is finished
                    Ok(Async::Ready(None)) => { return Ok(Async::Ready(())); }

                    // Stream returned a value
                    Ok(Async::Ready(Some(next))) => {
                        // Process the value on the stream
                        desync.async(move |core| {
                            let mut process = process.lock().unwrap();
                            let process     = &mut *process;
                            process(core, Ok(next));
                        });
                    },

                    // Stream returned an error
                    Err(e) => {
                        // Process the error on the stream
                        desync.async(move |core| {
                            let mut process = process.lock().unwrap();
                            let process     = &mut *process;
                            process(core, Err(e));
                        });
                    },
                }
            } else {
                // The desync target is no longer available - indicate that we've completed monitoring
                return Ok(Async::Ready(()));
            }
        }
    });
}

///
/// Pipes a stream into this object. Whenever an item becomes available on the stream, the
/// processing function is called asynchronously with the item that was received. The
/// return value is placed onto the output stream.
///
/// Unlike `pipe_in`, this keeps a strong reference to the `Desync` object so the processing
/// will continue so long as the input stream has data and the output stream is not dropped.
///
/// The input stream will start executing and reading values immediately when this is called.
/// Dropping the output stream will cause the pipe to be closed (the input stream will be
/// dropped and no further processing will occur).
///
/// This example demonstrates how to create a simple demonstration pipe that takes hashset values
/// and returns a stream indicating whether or not they were already included:
///
/// ```
/// # extern crate futures;
/// # extern crate desync;
/// # use std::collections::HashSet;
/// # use std::sync::*;
/// #
/// use futures::sync::mpsc;
/// use futures::executor;
/// use desync::*;
///
/// let desync_hashset      = Arc::new(Desync::new(HashSet::new()));
/// let (sender, receiver)  = mpsc::channel::<String>(5);
///
/// let value_inserted = pipe(Arc::clone(&desync_hashset), receiver,
///     |hashset, value| { value.map(|value| (value.clone(), hashset.insert(value))) });
///
/// let mut sender = executor::spawn(sender);
/// sender.wait_send("Test".to_string());
/// sender.wait_send("Another value".to_string());
/// sender.wait_send("Test".to_string());
///
/// let mut value_inserted = executor::spawn(value_inserted);
/// assert!(value_inserted.wait_stream() == Some(Ok(("Test".to_string(), true))));
/// assert!(value_inserted.wait_stream() == Some(Ok(("Another value".to_string(), true))));
/// assert!(value_inserted.wait_stream() == Some(Ok(("Test".to_string(), false))));
/// ```
///
pub fn pipe<Core, S, Output, OutputErr, ProcessFn>(desync: Arc<Desync<Core>>, stream: S, process: ProcessFn) -> PipeStream<Output, OutputErr>
where   Core:       'static+Send,
        S:          'static+Send+Stream,
        S::Item:    Send,
        S::Error:   Send,
        Output:     'static+Send,
        OutputErr:  'static+Send,
        ProcessFn:  'static+Send+FnMut(&mut Core, Result<S::Item, S::Error>) -> Result<Output, OutputErr> {

    // Fetch the input stream and prepare the process function for async calling
    let mut input_stream    = stream;
    let process             = Arc::new(Mutex::new(process));

    // Create the output stream
    let output_stream   = PipeStream::new();
    let stream_core     = Arc::clone(&output_stream.core);
    let stream_core     = Arc::downgrade(&stream_core);

    // Monitor the input stream and pass data to the output stream
    PIPE_MONITOR.monitor(move || {
        loop {
            let stream_core = stream_core.upgrade();

            if let Some(stream_core) = stream_core {
                // Defer processing if the stream core is full
                {
                    // Fetch the core
                    let mut stream_core = stream_core.lock().unwrap();

                    // If the pending queue is full, then stop processing events
                    if stream_core.pending.len() >= stream_core.max_pipe_depth {
                        // Wake when the stream accepts some input
                        stream_core.backpressure_release_notify = Some(task::current());

                        // Go back to sleep without reading from the stream
                        return Ok(Async::NotReady);
                    }
                }

                // Read the current status of the stream
                let process         = Arc::clone(&process);
                let next            = input_stream.poll();
                let mut next_item;

                // Work out what the next item to pass to the process function should be
                match next {
                    // Just wait if the stream is not ready
                    Ok(Async::NotReady) => { return Ok(Async::NotReady); },

                    // Stop processing when the input stream is finished
                    Ok(Async::Ready(None)) => {
                        // Mark the target stream as closed
                        let notify = {
                            let mut stream_core = stream_core.lock().unwrap();
                            stream_core.closed = true;
                            stream_core.notify.take()
                        };
                        notify.map(|notify| notify.notify());

                        // Pipe has finished
                        return Ok(Async::Ready(()));
                    }

                    // Stream returned a value
                    Ok(Async::Ready(Some(next))) => next_item = Ok(next),

                    // Stream returned an error
                    Err(e) => next_item = Err(e),
                }

                // Send the next item to be processed
                desync.async(move |core| {
                    // Process the next item
                    let mut process     = process.lock().unwrap();
                    let process         = &mut *process;
                    let next_item       = process(core, next_item);

                    // Send to the pipe stream
                    let notify = {
                        let mut stream_core = stream_core.lock().unwrap();

                        stream_core.pending.push_back(next_item);
                        stream_core.notify.take()
                    };
                    notify.map(|notify| notify.notify());
                });

            } else {
                // We stop processing once nothing is reading from the target stream
                return Ok(Async::Ready(()));
            }
        }
    });

    // The pipe stream is the result
    output_stream
}

///
/// The shared data for a pipe stream
///
struct PipeStreamCore<Item, Error>  {
    /// The maximum number of items we allow to be queued in this stream before producing backpressure
    max_pipe_depth: usize,

    /// The pending data for this stream
    pending: VecDeque<Result<Item, Error>>,

    /// True if the input stream has closed (the stream is closed once this is true and there are no more pending items)
    closed: bool,

    /// The task to notify when the stream changes
    notify: Option<task::Task>,

    /// The task to notify when we reduce the amount of pending data
    backpressure_release_notify: Option<task::Task>
}

///
/// A stream generated by a pipe
///
pub struct PipeStream<Item, Error> {
    core: Arc<Mutex<PipeStreamCore<Item, Error>>>
}

impl<Item, Error> PipeStream<Item, Error> {
    ///
    /// Creates a new, empty, pipestream
    ///
    fn new() -> PipeStream<Item, Error> {
        PipeStream {
            core: Arc::new(Mutex::new(PipeStreamCore {
                max_pipe_depth:                 PIPE_BACKPRESSURE_COUNT,
                pending:                        VecDeque::new(),
                closed:                         false,
                notify:                         None,
                backpressure_release_notify:    None
            }))
        }
    }

    ///
    /// Sets the number of items that this pipe stream will buffer before producing backpressure
    ///
    /// If this call is not made, this will be set to 5.
    ///
    pub fn set_backpressure_depth(&mut self, max_depth: usize) {
        self.core.lock().unwrap().max_pipe_depth = max_depth;
    }
}

impl<Item, Error> Drop for PipeStream<Item, Error> {
    fn drop(&mut self) {
        let mut core = self.core.lock().unwrap();

        // Flush the pending queue
        core.pending = VecDeque::new();

        // TODO: wake the monitor and stop listening to the source stream
        // (Right now this will happen next time the source stream produces data)
    }
}

impl<Item, Error> Stream for PipeStream<Item, Error> {
    type Item   = Item;
    type Error  = Error;

    fn poll(&mut self) -> Poll<Option<Item>, Error> {
        // Fetch the core
        let mut core = self.core.lock().unwrap();

        if let Some(item) = core.pending.pop_front() {
            // Value waiting at the start of the stream
            core.backpressure_release_notify.take().map(|notify| notify.notify());

            match item {
                Ok(item)    => Ok(Async::Ready(Some(item))),
                Err(erm)    => Err(erm)
            }
        } else if core.closed {
            // No more data will be returned from this stream
            Ok(Async::Ready(None))
        } else {
            // Stream not ready
            core.notify = Some(task::current());

            Ok(Async::NotReady)
        }
    }
}

///
/// The main polling component for that implements the stream pipes
///
struct PipeMonitor {
}

///
/// Provides the 'Notify' interface for a polling function with a particular ID
///
struct PipeNotify<PollFn: Send> {
    next_poll: Arc<Desync<Option<Spawn<PollFn>>>>
}

impl PipeMonitor {
    ///
    /// Creates a new poll thread
    ///
    pub fn new() -> PipeMonitor {
        PipeMonitor {
        }
    }

    ///
    /// Performs a polling operation on a poll
    ///
    fn poll<PollFn>(this_poll: &mut Option<Spawn<PollFn>>, next_poll: Arc<Desync<Option<Spawn<PollFn>>>>)
    where PollFn: 'static+Send+Future<Item=(), Error=()> {
        // If the polling function exists...
        if let Some(mut poll) = this_poll.take() {
            // Create a notification
            let notify = PipeNotify {
                next_poll: next_poll
            };
            let notify = Arc::new(notify);

            // Poll the function
            let poll_result = poll.poll_future_notify(&notify, 0);

            // Keep the polling function alive if it has not finished yet
            if poll_result != Ok(Async::Ready(())) {
                // The take() call means that the polling won't continue unless we pass it forward like this
                *this_poll = Some(poll);
            }
        }
    }

    ///
    /// Adds a polling function to the current thread. It will be called using the futures
    /// notification system (ie, can call things like the stream poll function)
    ///
    pub fn monitor<PollFn>(&self, poll_fn: PollFn)
    where PollFn: 'static+Send+FnMut() -> Poll<(), ()> {
        // Turn the polling function into a future (it will complete when monitoring is complete)
        let poll_fn     = future::poll_fn(poll_fn);

        // Spawn it with an executor
        let poll_fn     = executor::spawn(poll_fn);

        // Create a desync object for polling
        let poll_fn     = Arc::new(Desync::new(Some(poll_fn)));
        let next_poll   = Arc::clone(&poll_fn);

        // Perform the initial polling
        poll_fn.sync(move |poll_fn| Self::poll(poll_fn, next_poll));
    }
}

impl<PollFn> executor::Notify for PipeNotify<PollFn>
where PollFn: 'static+Send+Future<Item=(), Error=()> {
    fn notify(&self, _id: usize) {
        // Poll the future whenever we're notified
        let next_poll = Arc::clone(&self.next_poll);
        self.next_poll.sync(move |poll_fn| PipeMonitor::poll(poll_fn, next_poll));
    }
}
<commit_msg>Fix a bug where there's a race condition between the pipe closing and the last input arriving (desync is always in order)<commit_after>//!
//! Desync pipes provide a way to generate and process streams via a `Desync` object
//!
//! Pipes are an excellent way to interface `Desync` objects and the futures library. Piping
//! a stream into a `Desync` object is equivalent to spawning it with an executor, except
//! without the need to dedicate a thread to running it.
//!
//! There are two kinds of pipe. The `pipe_in` function creates a pipe that processes each
//! value made available from a stream on a desync object as they arrive, producing no
//! results. This is useful for cases where a `Desync` object is being used as the endpoint
//! for some data processing (for example, to insert the results of an operation into an
//! asynchronous database object).
//!
//! The `pipe` function pipes data through an object. For every input value, it produces
//! an output value. This is good for creating streams that perform some kind of asynchronous
//! processing operation or that need to access data from inside a `Desync` object.
//!
//! Here's an example of using `pipe_in` to store data in a `HashSet`:
//!
//! ```
//! # extern crate futures;
//! # extern crate desync;
//! # use std::collections::HashSet;
//! # use std::sync::*;
//! #
//! use futures::sync::mpsc;
//! use futures::executor;
//! use desync::*;
//!
//! let desync_hashset      = Arc::new(Desync::new(HashSet::new()));
//! let (sender, receiver)  = mpsc::channel(5);
//!
//! pipe_in(Arc::clone(&desync_hashset), receiver, |hashset, value| { value.map(|value| hashset.insert(value)); });
//!
//! let mut sender = executor::spawn(sender);
//! sender.wait_send("Test".to_string());
//! sender.wait_send("Another value".to_string());
//! #
//! # assert!(desync_hashset.sync(|hashset| hashset.contains(&("Test".to_string()))))
//! ```
//!

use super::desync::*;

use futures::*;
use futures::executor;
use futures::executor::Spawn;

use std::sync::*;
use std::result::Result;
use std::collections::VecDeque;

lazy_static! {
    /// The shared queue where we monitor for updates to the active pipe streams
    static ref PIPE_MONITOR: PipeMonitor = PipeMonitor::new();
}

/// The maximum number of items to queue on a pipe stream before we stop accepting new input
const PIPE_BACKPRESSURE_COUNT: usize = 5;

///
/// Pipes a stream into a desync object. Whenever an item becomes available on the stream, the
/// processing function is called asynchronously with the item that was received.
///
/// This takes a weak reference to the passed in `Desync` object, so the pipe will stop if it's
/// the only thing referencing this object.
///
/// Piping a stream to a `Desync` like this will cause it to start executing: ie, this is
/// similar to calling `executor::spawn(stream)`, except that the stream will immediately
/// start draining into the `Desync` object.
///
pub fn pipe_in<Core, S, ProcessFn>(desync: Arc<Desync<Core>>, stream: S, process: ProcessFn)
where   Core:       'static+Send,
        S:          'static+Send+Stream,
        S::Item:    Send,
        S::Error:   Send,
        ProcessFn:  'static+Send+FnMut(&mut Core, Result<S::Item, S::Error>) -> () {

    // Need a mutable version of the stream
    let mut stream = stream;

    // We stop processing once the desync object is no longer used anywhere else
    let desync = Arc::downgrade(&desync);

    // Wrap the process fn up so we can call it asynchronously
    // (it doesn't really need to be in a mutex as it's only called by our object but we need to make it pass Rust's checks and we don't have a way to specify this at the moment)
    let process = Arc::new(Mutex::new(process));

    // Monitor the stream
    PIPE_MONITOR.monitor(move || {
        loop {
            let desync      = desync.upgrade();

            if let Some(desync) = desync {
                // Read the current status of the stream
                let process     = Arc::clone(&process);
                let next        = stream.poll();

                match next {
                    // Just wait if the stream is not ready
                    Ok(Async::NotReady) => { return Ok(Async::NotReady); },

                    // Stop processing when the stream is finished
                    Ok(Async::Ready(None)) => { return Ok(Async::Ready(())); }

                    // Stream returned a value
                    Ok(Async::Ready(Some(next))) => {
                        // Process the value on the stream
                        desync.async(move |core| {
                            let mut process = process.lock().unwrap();
                            let process     = &mut *process;
                            process(core, Ok(next));
                        });
                    },

                    // Stream returned an error
                    Err(e) => {
                        // Process the error on the stream
                        desync.async(move |core| {
                            let mut process = process.lock().unwrap();
                            let process     = &mut *process;
                            process(core, Err(e));
                        });
                    },
                }
            } else {
                // The desync target is no longer available - indicate that we've completed monitoring
                return Ok(Async::Ready(()));
            }
        }
    });
}

///
/// Pipes a stream into this object. Whenever an item becomes available on the stream, the
/// processing function is called asynchronously with the item that was received. The
/// return value is placed onto the output stream.
///
/// Unlike `pipe_in`, this keeps a strong reference to the `Desync` object so the processing
/// will continue so long as the input stream has data and the output stream is not dropped.
///
/// The input stream will start executing and reading values immediately when this is called.
/// Dropping the output stream will cause the pipe to be closed (the input stream will be
/// dropped and no further processing will occur).
///
/// This example demonstrates how to create a simple demonstration pipe that takes hashset values
/// and returns a stream indicating whether or not they were already included:
///
/// ```
/// # extern crate futures;
/// # extern crate desync;
/// # use std::collections::HashSet;
/// # use std::sync::*;
/// #
/// use futures::sync::mpsc;
/// use futures::executor;
/// use desync::*;
///
/// let desync_hashset      = Arc::new(Desync::new(HashSet::new()));
/// let (sender, receiver)  = mpsc::channel::<String>(5);
///
/// let value_inserted = pipe(Arc::clone(&desync_hashset), receiver,
///     |hashset, value| { value.map(|value| (value.clone(), hashset.insert(value))) });
///
/// let mut sender = executor::spawn(sender);
/// sender.wait_send("Test".to_string());
/// sender.wait_send("Another value".to_string());
/// sender.wait_send("Test".to_string());
///
/// let mut value_inserted = executor::spawn(value_inserted);
/// assert!(value_inserted.wait_stream() == Some(Ok(("Test".to_string(), true))));
/// assert!(value_inserted.wait_stream() == Some(Ok(("Another value".to_string(), true))));
/// assert!(value_inserted.wait_stream() == Some(Ok(("Test".to_string(), false))));
/// ```
///
pub fn pipe<Core, S, Output, OutputErr, ProcessFn>(desync: Arc<Desync<Core>>, stream: S, process: ProcessFn) -> PipeStream<Output, OutputErr>
where   Core:       'static+Send,
        S:          'static+Send+Stream,
        S::Item:    Send,
        S::Error:   Send,
        Output:     'static+Send,
        OutputErr:  'static+Send,
        ProcessFn:  'static+Send+FnMut(&mut Core, Result<S::Item, S::Error>) -> Result<Output, OutputErr> {

    // Fetch the input stream and prepare the process function for async calling
    let mut input_stream    = stream;
    let process             = Arc::new(Mutex::new(process));

    // Create the output stream
    let output_stream   = PipeStream::new();
    let stream_core     = Arc::clone(&output_stream.core);
    let stream_core     = Arc::downgrade(&stream_core);

    // Monitor the input stream and pass data to the output stream
    PIPE_MONITOR.monitor(move || {
        loop {
            let stream_core = stream_core.upgrade();

            if let Some(stream_core) = stream_core {
                // Defer processing if the stream core is full
                {
                    // Fetch the core
                    let mut stream_core = stream_core.lock().unwrap();

                    // If the pending queue is full, then stop processing events
                    if stream_core.pending.len() >= stream_core.max_pipe_depth {
                        // Wake when the stream accepts some input
                        stream_core.backpressure_release_notify = Some(task::current());

                        // Go back to sleep without reading from the stream
                        return Ok(Async::NotReady);
                    }
                }

                // Read the current status of the stream
                let process         = Arc::clone(&process);
                let next            = input_stream.poll();
                let mut next_item;

                // Work out what the next item to pass to the process function should be
                match next {
                    // Just wait if the stream is not ready
                    Ok(Async::NotReady) => { return Ok(Async::NotReady); },

                    // Stop processing when the input stream is finished
                    Ok(Async::Ready(None)) => {
                        desync.async(move |_core| {
                            // Mark the target stream as closed
                            let notify = {
                                let mut stream_core = stream_core.lock().unwrap();
                                stream_core.closed = true;
                                stream_core.notify.take()
                            };
                            notify.map(|notify| notify.notify());
                        });

                        // Pipe has finished
                        return Ok(Async::Ready(()));
                    }

                    // Stream returned a value
                    Ok(Async::Ready(Some(next))) => next_item = Ok(next),

                    // Stream returned an error
                    Err(e) => next_item = Err(e),
                }

                // Send the next item to be processed
                desync.async(move |core| {
                    // Process the next item
                    let mut process     = process.lock().unwrap();
                    let process         = &mut *process;
                    let next_item       = process(core, next_item);

                    // Send to the pipe stream
                    let notify = {
                        let mut stream_core = stream_core.lock().unwrap();

                        stream_core.pending.push_back(next_item);
                        stream_core.notify.take()
                    };
                    notify.map(|notify| notify.notify());
                });

            } else {
                // We stop processing once nothing is reading from the target stream
                return Ok(Async::Ready(()));
            }
        }
    });

    // The pipe stream is the result
    output_stream
}

///
/// The shared data for a pipe stream
///
struct PipeStreamCore<Item, Error>  {
    /// The maximum number of items we allow to be queued in this stream before producing backpressure
    max_pipe_depth: usize,

    /// The pending data for this stream
    pending: VecDeque<Result<Item, Error>>,

    /// True if the input stream has closed (the stream is closed once this is true and there are no more pending items)
    closed: bool,

    /// The task to notify when the stream changes
    notify: Option<task::Task>,

    /// The task to notify when we reduce the amount of pending data
    backpressure_release_notify: Option<task::Task>
}

///
/// A stream generated by a pipe
///
pub struct PipeStream<Item, Error> {
    core: Arc<Mutex<PipeStreamCore<Item, Error>>>
}

impl<Item, Error> PipeStream<Item, Error> {
    ///
    /// Creates a new, empty, pipestream
    ///
    fn new() -> PipeStream<Item, Error> {
        PipeStream {
            core: Arc::new(Mutex::new(PipeStreamCore {
                max_pipe_depth:                 PIPE_BACKPRESSURE_COUNT,
                pending:                        VecDeque::new(),
                closed:                         false,
                notify:                         None,
                backpressure_release_notify:    None
            }))
        }
    }

    ///
    /// Sets the number of items that this pipe stream will buffer before producing backpressure
    ///
    /// If this call is not made, this will be set to 5.
    ///
    pub fn set_backpressure_depth(&mut self, max_depth: usize) {
        self.core.lock().unwrap().max_pipe_depth = max_depth;
    }
}

impl<Item, Error> Drop for PipeStream<Item, Error> {
    fn drop(&mut self) {
        let mut core = self.core.lock().unwrap();

        // Flush the pending queue
        core.pending = VecDeque::new();

        // TODO: wake the monitor and stop listening to the source stream
        // (Right now this will happen next time the source stream produces data)
    }
}

impl<Item, Error> Stream for PipeStream<Item, Error> {
    type Item   = Item;
    type Error  = Error;

    fn poll(&mut self) -> Poll<Option<Item>, Error> {
        // Fetch the core
        let mut core = self.core.lock().unwrap();

        if let Some(item) = core.pending.pop_front() {
            // Value waiting at the start of the stream
            core.backpressure_release_notify.take().map(|notify| notify.notify());

            match item {
                Ok(item)    => Ok(Async::Ready(Some(item))),
                Err(erm)    => Err(erm)
            }
        } else if core.closed {
            // No more data will be returned from this stream
            Ok(Async::Ready(None))
        } else {
            // Stream not ready
            core.notify = Some(task::current());

            Ok(Async::NotReady)
        }
    }
}

///
/// The main polling component for that implements the stream pipes
///
struct PipeMonitor {
}

///
/// Provides the 'Notify' interface for a polling function with a particular ID
///
struct PipeNotify<PollFn: Send> {
    next_poll: Arc<Desync<Option<Spawn<PollFn>>>>
}

impl PipeMonitor {
    ///
    /// Creates a new poll thread
    ///
    pub fn new() -> PipeMonitor {
        PipeMonitor {
        }
    }

    ///
    /// Performs a polling operation on a poll
    ///
    fn poll<PollFn>(this_poll: &mut Option<Spawn<PollFn>>, next_poll: Arc<Desync<Option<Spawn<PollFn>>>>)
    where PollFn: 'static+Send+Future<Item=(), Error=()> {
        // If the polling function exists...
        if let Some(mut poll) = this_poll.take() {
            // Create a notification
            let notify = PipeNotify {
                next_poll: next_poll
            };
            let notify = Arc::new(notify);

            // Poll the function
            let poll_result = poll.poll_future_notify(&notify, 0);

            // Keep the polling function alive if it has not finished yet
            if poll_result != Ok(Async::Ready(())) {
                // The take() call means that the polling won't continue unless we pass it forward like this
                *this_poll = Some(poll);
            }
        }
    }

    ///
    /// Adds a polling function to the current thread. It will be called using the futures
    /// notification system (ie, can call things like the stream poll function)
    ///
    pub fn monitor<PollFn>(&self, poll_fn: PollFn)
    where PollFn: 'static+Send+FnMut() -> Poll<(), ()> {
        // Turn the polling function into a future (it will complete when monitoring is complete)
        let poll_fn     = future::poll_fn(poll_fn);

        // Spawn it with an executor
        let poll_fn     = executor::spawn(poll_fn);

        // Create a desync object for polling
        let poll_fn     = Arc::new(Desync::new(Some(poll_fn)));
        let next_poll   = Arc::clone(&poll_fn);

        // Perform the initial polling
        poll_fn.sync(move |poll_fn| Self::poll(poll_fn, next_poll));
    }
}

impl<PollFn> executor::Notify for PipeNotify<PollFn>
where PollFn: 'static+Send+Future<Item=(), Error=()> {
    fn notify(&self, _id: usize) {
        // Poll the future whenever we're notified
        let next_poll = Arc::clone(&self.next_poll);
        self.next_poll.sync(move |poll_fn| PipeMonitor::poll(poll_fn, next_poll));
    }
}
<|endoftext|>