<commit_before>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Crowbook is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

//!  Note: this documentation is relative to `crowbook` *as a library*.
//!  For documentation regarding the *program* `crowbook`, see
//!  [the Github page](https://github.com/lise-henry/crowbook).
//!
//! # Book
//!
//! The central structure of Crowbook is `Book`, who coordinates everything.
//!
//! Its roles are:
//!
//! * reading a book configuration file and setting the book options accordingly
//! * reading the chapters (written in Markdown) listed in this configuration file
//!   to `Parser`, get back an AST and store it in memory
//! * call `HtmlRenderer`, `EpubRenderer`, `LatexRenderer` and/or `OdtRenderer`
//!   according to the book's parameters and generate the appopriate files.
//!
//! ## Example
//!
//! ```ignore
//! use crowbook::{Book, InfoLevel};
//! // Reads configuration file "foo.book" (and set verbosity do default level)
//! let mut book = Book::new_from_file("foo.book", InfoLevel::Info, &[]).unwrap();
//! // Render all formats according to this configuration file
//! book.render_all().unwrap();
//! ```
//!
//! This is basically the code for the `crowbook` binary (though it contains a
//! bit more error handling, checking parameters from command line and so on).
//! This is, however, not very interesting for a library usage.
//!
//! The `Book` structure, however, exposes its `chapter` fields, which contains
//! a vector with an element by chapter. With it, you can access the Markdown
//! for all chapters represented as an Abstact Syntax Tree (i.e., a vector of `Token`s).
//! It is thus possible to create a new renderer (or manipulate this AST in other ways).
//!
//! # Parser
//!
//! It is also possible to directly use `Parser` to transform some markdown string or file
//! to this AST:
//!
//! ```
//! use crowbook::{Parser,Token};
//! let mut parser = Parser::new();
//! let result = parser.parse("Some *valid* Markdown").unwrap();
//! assert_eq!(format!("{:?}", result),
//! r#"[Paragraph([Str("Some "), Emphasis([Str("valid")]), Str(" Markdown")])]"#);
//! ```
//!
//! Of course, you probably want to do something else with this AST than display it.
//! Let's assume you want to count the number of links in a document.
//!
//! ```
//! use crowbook::{Parser,Token};
//! fn count(ast: &[Token]) -> u32 {
//!    let mut n = 0;
//!    for token in ast {
//!        match *token {
//!            // It's a link, increase counter
//!            Token::Link(_,_,_) => n += 1,
//!            // It's not a link, let's count the number of links
//!            // inside of the inner element (if there is one)
//!           _ => {
//!                if let Some(sub_ast) = token.inner() {
//!                    n += count(sub_ast);
//!                }
//!            }
//!       }
//!    }
//!    n
//! }
//!
//! let md = "# Here's a [link](http://foo.bar) #\n And *another [one](http://foo.bar)* !";
//!
//! let mut parser = Parser::new();
//! let ast = parser.parse(md).unwrap();
//! assert_eq!(count(&ast), 2);
//! ```

extern crate pulldown_cmark as cmark;
extern crate mustache;
extern crate chrono;
extern crate uuid;
extern crate yaml_rust;
extern crate mime_guess;
extern crate walkdir;
extern crate rustc_serialize;
extern crate crossbeam;
extern crate crowbook_text_processing;

#[cfg(feature = "proofread")]
extern crate hyper;
#[cfg(feature = "proofread")]
extern crate url;
#[cfg(feature = "proofread")]
extern crate caribon;

pub mod cleaner;

pub use parser::Parser;
pub use book::Book;
pub use bookoption::BookOption;
pub use bookoptions::BookOptions;
pub use html::HtmlRenderer;
pub use epub::EpubRenderer;
pub use latex::LatexRenderer;
pub use odt::OdtRenderer;
pub use error::{Result, Error, Source};
pub use token::Token;
pub use token::Data;
pub use number::Number;
pub use resource_handler::ResourceHandler;
pub use logger::{Logger, InfoLevel};
pub use html_dir::HtmlDirRenderer;
pub use html_single::HtmlSingleRenderer;
pub use renderer::Renderer;

#[macro_use]
mod html;
mod html_dir;
mod error;
mod book;
mod epub;
mod latex;
mod odt;
mod parser;
mod token;

mod number;
mod resource_handler;
mod logger;
mod bookoptions;
mod lang;
mod renderer;
mod html_single;

mod toc;
mod zipper;
mod templates;
mod bookoption;

#[cfg(feature = "proofread")]
mod grammar_check;
#[cfg(feature = "proofread")]
mod text_view;

#[cfg(test)]
mod tests;

<commit_msg>Make cleaner module private<commit_after>// Copyright (C) 2016 Élisabeth HENRY.
//
// This file is part of Crowbook.
//
// Crowbook is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Crowbook is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with Crowbook.  If not, see <http://www.gnu.org/licenses/>.

//!  Note: this documentation is relative to `crowbook` *as a library*.
//!  For documentation regarding the *program* `crowbook`, see
//!  [the Github page](https://github.com/lise-henry/crowbook).
//!
//! # Book
//!
//! The central structure of Crowbook is `Book`, who coordinates everything.
//!
//! Its roles are:
//!
//! * reading a book configuration file and setting the book options accordingly
//! * reading the chapters (written in Markdown) listed in this configuration file
//!   to `Parser`, get back an AST and store it in memory
//! * call `HtmlRenderer`, `EpubRenderer`, `LatexRenderer` and/or `OdtRenderer`
//!   according to the book's parameters and generate the appopriate files.
//!
//! ## Example
//!
//! ```ignore
//! use crowbook::{Book, InfoLevel};
//! // Reads configuration file "foo.book" (and set verbosity do default level)
//! let mut book = Book::new_from_file("foo.book", InfoLevel::Info, &[]).unwrap();
//! // Render all formats according to this configuration file
//! book.render_all().unwrap();
//! ```
//!
//! This is basically the code for the `crowbook` binary (though it contains a
//! bit more error handling, checking parameters from command line and so on).
//! This is, however, not very interesting for a library usage.
//!
//! The `Book` structure, however, exposes its `chapter` fields, which contains
//! a vector with an element by chapter. With it, you can access the Markdown
//! for all chapters represented as an Abstact Syntax Tree (i.e., a vector of `Token`s).
//! It is thus possible to create a new renderer (or manipulate this AST in other ways).
//!
//! # Parser
//!
//! It is also possible to directly use `Parser` to transform some markdown string or file
//! to this AST:
//!
//! ```
//! use crowbook::{Parser,Token};
//! let mut parser = Parser::new();
//! let result = parser.parse("Some *valid* Markdown").unwrap();
//! assert_eq!(format!("{:?}", result),
//! r#"[Paragraph([Str("Some "), Emphasis([Str("valid")]), Str(" Markdown")])]"#);
//! ```
//!
//! Of course, you probably want to do something else with this AST than display it.
//! Let's assume you want to count the number of links in a document.
//!
//! ```
//! use crowbook::{Parser,Token};
//! fn count(ast: &[Token]) -> u32 {
//!    let mut n = 0;
//!    for token in ast {
//!        match *token {
//!            // It's a link, increase counter
//!            Token::Link(_,_,_) => n += 1,
//!            // It's not a link, let's count the number of links
//!            // inside of the inner element (if there is one)
//!           _ => {
//!                if let Some(sub_ast) = token.inner() {
//!                    n += count(sub_ast);
//!                }
//!            }
//!       }
//!    }
//!    n
//! }
//!
//! let md = "# Here's a [link](http://foo.bar) #\n And *another [one](http://foo.bar)* !";
//!
//! let mut parser = Parser::new();
//! let ast = parser.parse(md).unwrap();
//! assert_eq!(count(&ast), 2);
//! ```

extern crate pulldown_cmark as cmark;
extern crate mustache;
extern crate chrono;
extern crate uuid;
extern crate yaml_rust;
extern crate mime_guess;
extern crate walkdir;
extern crate rustc_serialize;
extern crate crossbeam;
extern crate crowbook_text_processing;

#[cfg(feature = "proofread")]
extern crate hyper;
#[cfg(feature = "proofread")]
extern crate url;
#[cfg(feature = "proofread")]
extern crate caribon;

pub use parser::Parser;
pub use book::Book;
pub use bookoption::BookOption;
pub use bookoptions::BookOptions;
pub use html::HtmlRenderer;
pub use epub::EpubRenderer;
pub use latex::LatexRenderer;
pub use odt::OdtRenderer;
pub use error::{Result, Error, Source};
pub use token::Token;
pub use token::Data;
pub use number::Number;
pub use resource_handler::ResourceHandler;
pub use logger::{Logger, InfoLevel};
pub use html_dir::HtmlDirRenderer;
pub use html_single::HtmlSingleRenderer;
pub use renderer::Renderer;

#[macro_use] mod html;
mod html_dir;
mod error;
mod book;
mod epub;
mod latex;
mod odt;
mod parser;
mod token;
mod cleaner;

mod number;
mod resource_handler;
mod logger;
mod bookoptions;
mod lang;
mod renderer;
mod html_single;

mod toc;
mod zipper;
mod templates;
mod bookoption;

#[cfg(feature = "proofread")]
mod grammar_check;
#[cfg(feature = "proofread")]
mod text_view;

#[cfg(test)]
mod tests;

<|endoftext|>


<commit_before>
import option::none;
import option::some;
import util::orb;

export vbuf;
export init_fn;
export init_elt;
export buf;
export len;
export print_debug_info;
export slice;
export pop;
export push;
export reversed;
export member;

type vbuf = rustrt::vbuf;

type operator2[T, U, V] = fn(&T, &U) -> V ;

type array[T] = vec[mutable? T];

native "rust" mod rustrt {
    type vbuf;
    fn vec_buf[T](v: vec[T], offset: uint) -> vbuf;
    fn vec_len[T](v: vec[T]) -> uint;

    /**
     * Sometimes we modify the vec internal data via vec_buf and need to
     * update the vec's fill length accordingly.
     */
    fn vec_len_set[T](v: vec[T], n: uint);

    /**
     * The T in vec_alloc[T, U] is the type of the vec to allocate.  The
     * U is the type of an element in the vec.  So to allocate a vec[U] we
     * want to invoke this as vec_alloc[vec[U], U].
     */
    fn vec_alloc[T, U](n_elts: uint) -> vec[U];
    fn vec_alloc_mut[T, U](n_elts: uint) -> vec[mutable U];
    fn refcount[T](v: vec[T]) -> uint;
    fn vec_print_debug_info[T](v: vec[T]);
    fn vec_from_vbuf[T](v: vbuf, n_elts: uint) -> vec[T];
    fn unsafe_vec_to_mut[T](v: vec[T]) -> vec[mutable T];
}

fn alloc[T](n_elts: uint) -> vec[T] {
    ret rustrt::vec_alloc[vec[T], T](n_elts);
}

fn alloc_mut[T](n_elts: uint) -> vec[mutable T] {
    ret rustrt::vec_alloc_mut[vec[mutable T], T](n_elts);
}

fn refcount[T](v: array[T]) -> uint {
    let r = rustrt::refcount[T](v);
    if r == dbg::const_refcount { ret r; } else { ret r - 1u; }
}

fn vec_from_vbuf[T](v: vbuf, n_elts: uint) -> vec[T] {
    ret rustrt::vec_from_vbuf[T](v, n_elts);
}

// FIXME: Remove me; this is a botch to get around rustboot's bad typechecker.
fn empty[T]() -> vec[T] { ret alloc[T](0u); }

// FIXME: Remove me; this is a botch to get around rustboot's bad typechecker.
fn empty_mut[T]() -> vec[mutable T] { ret alloc_mut[T](0u); }

type init_op[T] = fn(uint) -> T ;

fn init_fn[@T](op: &init_op[T], n_elts: uint) -> vec[T] {
    let v: vec[T] = alloc[T](n_elts);
    let i: uint = 0u;
    while i < n_elts { v += [op(i)]; i += 1u; }
    ret v;
}

fn init_fn_mut[@T](op: &init_op[T], n_elts: uint) -> vec[mutable T] {
    let v: vec[mutable T] = alloc_mut[T](n_elts);
    let i: uint = 0u;
    while i < n_elts { v += [mutable op(i)]; i += 1u; }
    ret v;
}

// init_elt: creates and returns a vector of length n_elts, filled with
// that many copies of element t.
fn init_elt[@T](t: &T, n_elts: uint) -> vec[T] {
    /**
     * FIXME (issue #81): should be:
     *
     * fn elt_op[T](&T x, uint i) -> T { ret x; }
     * let init_op[T] inner = bind elt_op[T](t, _);
     * ret init_fn[T](inner, n_elts);
     */

    let v: vec[T] = alloc[T](n_elts);
    let i: uint = n_elts;
    while i > 0u { i -= 1u; v += [t]; }
    ret v;
}

fn init_elt_mut[@T](t: &T, n_elts: uint) -> vec[mutable T] {
    let v: vec[mutable T] = alloc_mut[T](n_elts);
    let i: uint = n_elts;
    while i > 0u { i -= 1u; v += [mutable t]; }
    ret v;
}

fn buf[T](v: array[T]) -> vbuf { ret rustrt::vec_buf[T](v, 0u); }

fn len[T](v: array[T]) -> uint { ret rustrt::vec_len[T](v); }

fn len_set[T](v: array[T], n: uint) { rustrt::vec_len_set[T](v, n); }

fn buf_off[T](v: array[T], offset: uint) -> vbuf {
    assert (offset < len[T](v));
    ret rustrt::vec_buf[T](v, offset);
}

fn print_debug_info[T](v: array[T]) { rustrt::vec_print_debug_info[T](v); }

// FIXME: typestate precondition (list is non-empty)
// Returns the last element of v.
fn last[@T](v: array[T]) -> option::t[T] {
    let l = len[T](v);
    if l == 0u { ret none[T]; }
    ret some[T](v.(l - 1u));
}

// Returns elements from [start..end) from v.
fn slice[@T](v: array[T], start: uint, end: uint) -> vec[T] {
    assert (start <= end);
    assert (end <= len[T](v));
    let result = alloc[T](end - start);
    let i: uint = start;
    while i < end { result += [v.(i)]; i += 1u; }
    ret result;
}

// FIXME: Should go away eventually.
fn slice_mut[@T](v: array[T], start: uint, end: uint) -> vec[mutable T] {
    assert (start <= end);
    assert (end <= len[T](v));
    let result = alloc_mut[T](end - start);
    let i: uint = start;
    while i < end { result += [mutable v.(i)]; i += 1u; }
    ret result;
}

fn shift[@T](v: &mutable array[T]) -> T {
    let ln = len[T](v);
    assert (ln > 0u);
    let e = v.(0);
    v = slice[T](v, 1u, ln);
    ret e;
}

fn pop[@T](v: &mutable array[T]) -> T {
    let ln = len[T](v);
    assert (ln > 0u);
    ln -= 1u;
    let e = v.(ln);
    v = slice[T](v, 0u, ln);
    ret e;
}

fn top[@T](v: &array[T]) -> T {
    let ln = len[T](v);
    assert (ln > 0u);
    ret v.(ln - 1u);
}

fn push[@T](v: &mutable array[T], t: &T) { v += [t]; }

fn unshift[@T](v: &mutable array[T], t: &T) {
    let rs = alloc[T](len[T](v) + 1u);
    rs += [t];
    rs += v;
    v = rs;
}

fn grow[@T](v: &mutable array[T], n: uint, initval: &T) {
    let i: uint = n;
    while i > 0u { i -= 1u; v += [initval]; }
}

fn grow_set[@T](v: &mutable vec[mutable T], index: uint, initval: &T,
               val: &T) {
    let length = vec::len(v);
    if index >= length { grow(v, index - length + 1u, initval); }
    v.(index) = val;
}

fn grow_init_fn[@T](v: &mutable array[T], n: uint, init_fn: fn() -> T ) {
    let i: uint = n;
    while i > 0u { i -= 1u; v += [init_fn()]; }
}

fn grow_init_fn_set[@T](v: &mutable array[T], index: uint, init_fn: fn() -> T,
                       val: &T) {
    let length = vec::len(v);
    if index >= length { grow_init_fn(v, index - length + 1u, init_fn); }
    v.(index) = val;
}

fn map[@T, @U](f: &fn(&T) -> U , v: &vec[T]) -> vec[U] {
    let rs: vec[U] = alloc[U](len[T](v));
    for ve: T  in v { rs += [f(ve)]; }
    ret rs;
}

fn filter_map[@T, @U](f: &fn(&T) -> option::t[U] , v: &vec[T]) -> vec[U] {
    let rs: vec[U] = [];
    for ve: T  in v { alt f(ve) { some(elt) { rs += [elt]; } none. { } } }
    ret rs;
}

fn map2[@T, @U, @V](f: &operator2[T, U, V], v0: &vec[T], v1: &vec[U])
    -> vec[V] {
    let v0_len = len[T](v0);
    if v0_len != len[U](v1) { fail; }
    let u: vec[V] = alloc[V](v0_len);
    let i = 0u;
    while i < v0_len { u += [f({ v0.(i) }, { v1.(i) })]; i += 1u; }
    ret u;
}

fn find[@T](f: fn(&T) -> bool , v: &vec[T]) -> option::t[T] {
    for elt: T  in v { if f(elt) { ret some[T](elt); } }
    ret none[T];
}

fn position[@T](x: &T, v: &array[T]) -> option::t[uint] {
    let i: uint = 0u;
    while i < len(v) { if x == v.(i) { ret some[uint](i); } i += 1u; }
    ret none[uint];
}

fn position_pred[T](f: fn(&T) -> bool , v: &vec[T]) -> option::t[uint] {
    let i: uint = 0u;
    while i < len(v) { if f(v.(i)) { ret some[uint](i); } i += 1u; }
    ret none[uint];
}

fn member[T](x: &T, v: &array[T]) -> bool {
    for elt: T in v { if x == elt { ret true; } }
    ret false;
}

fn count[T](x: &T, v: &array[T]) -> uint {
    let cnt = 0u;
    for elt: T  in v { if x == elt { cnt += 1u; } }
    ret cnt;
}

fn foldl[@T, @U](p: fn(&U, &T) -> U , z: &U, v: &vec[T]) -> U {
    let sz = len[T](v);
    if sz == 0u {
        ret z;
    } else {
        let rest = slice[T](v, 1u, sz);
        ret p(foldl[T, U](p, z, rest), v.(0));
    }
}

fn unzip[@T, @U](v: &vec[{_0: T, _1: U}]) -> {_0: vec[T], _1: vec[U]} {
    let sz = len(v);
    if sz == 0u {
        ret {_0: alloc[T](0u), _1: alloc[U](0u)};
    } else {
        let rest = slice(v, 1u, sz);
        let tl = unzip[T, U](rest);
        let a = [v.(0)._0];
        let b = [v.(0)._1];
        ret {_0: a + tl._0, _1: b + tl._1};
    }
}

// FIXME make the lengths being equal a constraint
fn zip[@T, @U](v: &vec[T], u: &vec[U]) -> vec[{_0: T, _1: U}] {
    let sz = len(v);
    assert (sz == len(u));
    if sz == 0u {
        ret alloc(0u);
    } else {
        let rest = zip(slice(v, 1u, sz), slice(u, 1u, sz));
        vec::push(rest, {_0: v.(0), _1: u.(0)});
        ret rest;
    }
}

fn or(v: &vec[bool]) -> bool {
    let f = orb;
    ret foldl[bool, bool](f, false, v);
}

fn any[T](f: &fn(&T) -> bool , v: &vec[T]) -> bool {
    for t: T  in v { if f(t) { ret true; } }
    ret false;
}
fn all[T](f: &fn(&T) -> bool , v: &vec[T]) -> bool {
    for t: T  in v { if !f(t) { ret false; } }
    ret true;
}

fn clone[@T](v: &vec[T]) -> vec[T] { ret slice[T](v, 0u, len[T](v)); }

fn plus_option[@T](v: &mutable vec[T], o: &option::t[T]) {
    alt o { none. { } some(x) { v += [x]; } }
}

fn cat_options[@T](v: &vec[option::t[T]]) -> vec[T] {
    let rs: vec[T] = [];
    for o: option::t[T]  in v { alt o { none. { } some(t) { rs += [t]; } } }
    ret rs;
}

// TODO: Remove in favor of built-in "freeze" operation when it's implemented.
fn freeze[@T](v: vec[mutable T]) -> vec[T] {
    let result: vec[T] = [];
    for elem: T  in v { result += [elem]; }
    ret result;
}

// Swaps two elements in a vector
fn swap[@T](v: &vec[mutable T], a: uint, b: uint) {
    let t: T = v.(a);
    v.(a) = v.(b);
    v.(b) = t;
}

// In place vector reversal
fn reverse[@T](v: &vec[mutable T]) {
    let i: uint = 0u;
    let ln = len[T](v);
    while i < ln / 2u { swap(v, i, ln - i - 1u); i += 1u; }
}

// Functional vector reversal. Returns a reversed copy of v.
fn reversed[@T](v: vec[T]) -> vec[T] {
    let rs: vec[T] = [];
    let i = len[T](v);
    if i == 0u { ret rs; } else { i -= 1u; }
    while i != 0u { push[T](rs, v.(i)); i -= 1u; }
    push[T](rs, v.(0));
    ret rs;
}

/// Truncates the vector to length `new_len`.
/// FIXME: This relies on a typechecker bug (covariance vs. invariance).
fn truncate[@T](v: &mutable vec[mutable? T], new_len: uint) {
    v = slice[T](v, 0u, new_len);
}
// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<commit_msg>Remove more exports from std::vec<commit_after>
import option::none;
import option::some;
import util::orb;

export vbuf;
export buf;
export len;

type vbuf = rustrt::vbuf;

type operator2[T, U, V] = fn(&T, &U) -> V ;

type array[T] = vec[mutable? T];

native "rust" mod rustrt {
    type vbuf;
    fn vec_buf[T](v: vec[T], offset: uint) -> vbuf;
    fn vec_len[T](v: vec[T]) -> uint;

    /**
     * Sometimes we modify the vec internal data via vec_buf and need to
     * update the vec's fill length accordingly.
     */
    fn vec_len_set[T](v: vec[T], n: uint);

    /**
     * The T in vec_alloc[T, U] is the type of the vec to allocate.  The
     * U is the type of an element in the vec.  So to allocate a vec[U] we
     * want to invoke this as vec_alloc[vec[U], U].
     */
    fn vec_alloc[T, U](n_elts: uint) -> vec[U];
    fn vec_alloc_mut[T, U](n_elts: uint) -> vec[mutable U];
    fn refcount[T](v: vec[T]) -> uint;
    fn vec_print_debug_info[T](v: vec[T]);
    fn vec_from_vbuf[T](v: vbuf, n_elts: uint) -> vec[T];
    fn unsafe_vec_to_mut[T](v: vec[T]) -> vec[mutable T];
}

fn alloc[T](n_elts: uint) -> vec[T] {
    ret rustrt::vec_alloc[vec[T], T](n_elts);
}

fn alloc_mut[T](n_elts: uint) -> vec[mutable T] {
    ret rustrt::vec_alloc_mut[vec[mutable T], T](n_elts);
}

fn refcount[T](v: array[T]) -> uint {
    let r = rustrt::refcount[T](v);
    if r == dbg::const_refcount { ret r; } else { ret r - 1u; }
}

fn vec_from_vbuf[T](v: vbuf, n_elts: uint) -> vec[T] {
    ret rustrt::vec_from_vbuf[T](v, n_elts);
}

// FIXME: Remove me; this is a botch to get around rustboot's bad typechecker.
fn empty[T]() -> vec[T] { ret alloc[T](0u); }

// FIXME: Remove me; this is a botch to get around rustboot's bad typechecker.
fn empty_mut[T]() -> vec[mutable T] { ret alloc_mut[T](0u); }

type init_op[T] = fn(uint) -> T ;

fn init_fn[@T](op: &init_op[T], n_elts: uint) -> vec[T] {
    let v: vec[T] = alloc[T](n_elts);
    let i: uint = 0u;
    while i < n_elts { v += [op(i)]; i += 1u; }
    ret v;
}

fn init_fn_mut[@T](op: &init_op[T], n_elts: uint) -> vec[mutable T] {
    let v: vec[mutable T] = alloc_mut[T](n_elts);
    let i: uint = 0u;
    while i < n_elts { v += [mutable op(i)]; i += 1u; }
    ret v;
}

// init_elt: creates and returns a vector of length n_elts, filled with
// that many copies of element t.
fn init_elt[@T](t: &T, n_elts: uint) -> vec[T] {
    /**
     * FIXME (issue #81): should be:
     *
     * fn elt_op[T](&T x, uint i) -> T { ret x; }
     * let init_op[T] inner = bind elt_op[T](t, _);
     * ret init_fn[T](inner, n_elts);
     */

    let v: vec[T] = alloc[T](n_elts);
    let i: uint = n_elts;
    while i > 0u { i -= 1u; v += [t]; }
    ret v;
}

fn init_elt_mut[@T](t: &T, n_elts: uint) -> vec[mutable T] {
    let v: vec[mutable T] = alloc_mut[T](n_elts);
    let i: uint = n_elts;
    while i > 0u { i -= 1u; v += [mutable t]; }
    ret v;
}

fn buf[T](v: array[T]) -> vbuf { ret rustrt::vec_buf[T](v, 0u); }

fn len[T](v: array[T]) -> uint { ret rustrt::vec_len[T](v); }

fn len_set[T](v: array[T], n: uint) { rustrt::vec_len_set[T](v, n); }

fn buf_off[T](v: array[T], offset: uint) -> vbuf {
    assert (offset < len[T](v));
    ret rustrt::vec_buf[T](v, offset);
}

fn print_debug_info[T](v: array[T]) { rustrt::vec_print_debug_info[T](v); }

// FIXME: typestate precondition (list is non-empty)
// Returns the last element of v.
fn last[@T](v: array[T]) -> option::t[T] {
    let l = len[T](v);
    if l == 0u { ret none[T]; }
    ret some[T](v.(l - 1u));
}

// Returns elements from [start..end) from v.
fn slice[@T](v: array[T], start: uint, end: uint) -> vec[T] {
    assert (start <= end);
    assert (end <= len[T](v));
    let result = alloc[T](end - start);
    let i: uint = start;
    while i < end { result += [v.(i)]; i += 1u; }
    ret result;
}

// FIXME: Should go away eventually.
fn slice_mut[@T](v: array[T], start: uint, end: uint) -> vec[mutable T] {
    assert (start <= end);
    assert (end <= len[T](v));
    let result = alloc_mut[T](end - start);
    let i: uint = start;
    while i < end { result += [mutable v.(i)]; i += 1u; }
    ret result;
}

fn shift[@T](v: &mutable array[T]) -> T {
    let ln = len[T](v);
    assert (ln > 0u);
    let e = v.(0);
    v = slice[T](v, 1u, ln);
    ret e;
}

fn pop[@T](v: &mutable array[T]) -> T {
    let ln = len[T](v);
    assert (ln > 0u);
    ln -= 1u;
    let e = v.(ln);
    v = slice[T](v, 0u, ln);
    ret e;
}

fn top[@T](v: &array[T]) -> T {
    let ln = len[T](v);
    assert (ln > 0u);
    ret v.(ln - 1u);
}

fn push[@T](v: &mutable array[T], t: &T) { v += [t]; }

fn unshift[@T](v: &mutable array[T], t: &T) {
    let rs = alloc[T](len[T](v) + 1u);
    rs += [t];
    rs += v;
    v = rs;
}

fn grow[@T](v: &mutable array[T], n: uint, initval: &T) {
    let i: uint = n;
    while i > 0u { i -= 1u; v += [initval]; }
}

fn grow_set[@T](v: &mutable vec[mutable T], index: uint, initval: &T,
               val: &T) {
    let length = vec::len(v);
    if index >= length { grow(v, index - length + 1u, initval); }
    v.(index) = val;
}

fn grow_init_fn[@T](v: &mutable array[T], n: uint, init_fn: fn() -> T ) {
    let i: uint = n;
    while i > 0u { i -= 1u; v += [init_fn()]; }
}

fn grow_init_fn_set[@T](v: &mutable array[T], index: uint, init_fn: fn() -> T,
                       val: &T) {
    let length = vec::len(v);
    if index >= length { grow_init_fn(v, index - length + 1u, init_fn); }
    v.(index) = val;
}

fn map[@T, @U](f: &fn(&T) -> U , v: &vec[T]) -> vec[U] {
    let rs: vec[U] = alloc[U](len[T](v));
    for ve: T  in v { rs += [f(ve)]; }
    ret rs;
}

fn filter_map[@T, @U](f: &fn(&T) -> option::t[U] , v: &vec[T]) -> vec[U] {
    let rs: vec[U] = [];
    for ve: T  in v { alt f(ve) { some(elt) { rs += [elt]; } none. { } } }
    ret rs;
}

fn map2[@T, @U, @V](f: &operator2[T, U, V], v0: &vec[T], v1: &vec[U])
    -> vec[V] {
    let v0_len = len[T](v0);
    if v0_len != len[U](v1) { fail; }
    let u: vec[V] = alloc[V](v0_len);
    let i = 0u;
    while i < v0_len { u += [f({ v0.(i) }, { v1.(i) })]; i += 1u; }
    ret u;
}

fn find[@T](f: fn(&T) -> bool , v: &vec[T]) -> option::t[T] {
    for elt: T  in v { if f(elt) { ret some[T](elt); } }
    ret none[T];
}

fn position[@T](x: &T, v: &array[T]) -> option::t[uint] {
    let i: uint = 0u;
    while i < len(v) { if x == v.(i) { ret some[uint](i); } i += 1u; }
    ret none[uint];
}

fn position_pred[T](f: fn(&T) -> bool , v: &vec[T]) -> option::t[uint] {
    let i: uint = 0u;
    while i < len(v) { if f(v.(i)) { ret some[uint](i); } i += 1u; }
    ret none[uint];
}

fn member[T](x: &T, v: &array[T]) -> bool {
    for elt: T in v { if x == elt { ret true; } }
    ret false;
}

fn count[T](x: &T, v: &array[T]) -> uint {
    let cnt = 0u;
    for elt: T  in v { if x == elt { cnt += 1u; } }
    ret cnt;
}

fn foldl[@T, @U](p: fn(&U, &T) -> U , z: &U, v: &vec[T]) -> U {
    let sz = len[T](v);
    if sz == 0u {
        ret z;
    } else {
        let rest = slice[T](v, 1u, sz);
        ret p(foldl[T, U](p, z, rest), v.(0));
    }
}

fn unzip[@T, @U](v: &vec[{_0: T, _1: U}]) -> {_0: vec[T], _1: vec[U]} {
    let sz = len(v);
    if sz == 0u {
        ret {_0: alloc[T](0u), _1: alloc[U](0u)};
    } else {
        let rest = slice(v, 1u, sz);
        let tl = unzip[T, U](rest);
        let a = [v.(0)._0];
        let b = [v.(0)._1];
        ret {_0: a + tl._0, _1: b + tl._1};
    }
}

// FIXME make the lengths being equal a constraint
fn zip[@T, @U](v: &vec[T], u: &vec[U]) -> vec[{_0: T, _1: U}] {
    let sz = len(v);
    assert (sz == len(u));
    if sz == 0u {
        ret alloc(0u);
    } else {
        let rest = zip(slice(v, 1u, sz), slice(u, 1u, sz));
        push(rest, {_0: v.(0), _1: u.(0)});
        ret rest;
    }
}

fn or(v: &vec[bool]) -> bool {
    let f = orb;
    ret foldl[bool, bool](f, false, v);
}

fn any[T](f: &fn(&T) -> bool , v: &vec[T]) -> bool {
    for t: T  in v { if f(t) { ret true; } }
    ret false;
}
fn all[T](f: &fn(&T) -> bool , v: &vec[T]) -> bool {
    for t: T  in v { if !f(t) { ret false; } }
    ret true;
}

fn clone[@T](v: &vec[T]) -> vec[T] { ret slice[T](v, 0u, len[T](v)); }

fn plus_option[@T](v: &mutable vec[T], o: &option::t[T]) {
    alt o { none. { } some(x) { v += [x]; } }
}

fn cat_options[@T](v: &vec[option::t[T]]) -> vec[T] {
    let rs: vec[T] = [];
    for o: option::t[T]  in v { alt o { none. { } some(t) { rs += [t]; } } }
    ret rs;
}

// TODO: Remove in favor of built-in "freeze" operation when it's implemented.
fn freeze[@T](v: vec[mutable T]) -> vec[T] {
    let result: vec[T] = [];
    for elem: T  in v { result += [elem]; }
    ret result;
}

// Swaps two elements in a vector
fn swap[@T](v: &vec[mutable T], a: uint, b: uint) {
    let t: T = v.(a);
    v.(a) = v.(b);
    v.(b) = t;
}

// In place vector reversal
fn reverse[@T](v: &vec[mutable T]) {
    let i: uint = 0u;
    let ln = len[T](v);
    while i < ln / 2u { swap(v, i, ln - i - 1u); i += 1u; }
}

// Functional vector reversal. Returns a reversed copy of v.
fn reversed[@T](v: vec[T]) -> vec[T] {
    let rs: vec[T] = [];
    let i = len[T](v);
    if i == 0u { ret rs; } else { i -= 1u; }
    while i != 0u { push[T](rs, v.(i)); i -= 1u; }
    push[T](rs, v.(0));
    ret rs;
}

/// Truncates the vector to length `new_len`.
/// FIXME: This relies on a typechecker bug (covariance vs. invariance).
fn truncate[@T](v: &mutable vec[mutable? T], new_len: uint) {
    v = slice[T](v, 0u, new_len);
}
// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<|endoftext|>


<commit_before>//! Types for the *m.room.encrypted* event.

use std::collections::BTreeMap;

use js_int::UInt;
use ruma_events_macros::MessageEventContent;
use ruma_identifiers::DeviceIdBox;
use serde::{Deserialize, Serialize};

use crate::{room::message::Relation, MessageEvent};

/// An event that has been encrypted.
pub type EncryptedEvent = MessageEvent<EncryptedEventContent>;

/// The payload for `EncryptedEvent`.
#[derive(Clone, Debug, Deserialize, Serialize, MessageEventContent)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[ruma_event(type = "m.room.encrypted")]
#[serde(tag = "algorithm")]
pub enum EncryptedEventContent {
    /// An event encrypted with *m.olm.v1.curve25519-aes-sha2*.
    #[serde(rename = "m.olm.v1.curve25519-aes-sha2")]
    OlmV1Curve25519AesSha2(OlmV1Curve25519AesSha2Content),

    /// An event encrypted with *m.megolm.v1.aes-sha2*.
    #[serde(rename = "m.megolm.v1.aes-sha2")]
    MegolmV1AesSha2(MegolmV1AesSha2Content),
}

/// The to-device version of the payload for the `EncryptedEvent`.
pub type EncryptedToDeviceEventContent = EncryptedEventContent;

/// The payload for `EncryptedEvent` using the *m.olm.v1.curve25519-aes-sha2* algorithm.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct OlmV1Curve25519AesSha2Content {
    /// A map from the recipient Curve25519 identity key to ciphertext information.
    pub ciphertext: BTreeMap<String, CiphertextInfo>,

    /// The Curve25519 key of the sender.
    pub sender_key: String,
}

impl OlmV1Curve25519AesSha2Content {
    /// Creates a new `OlmV1Curve25519AesSha2Content` with the given ciphertext and sender key.
    pub fn new(ciphertext: BTreeMap<String, CiphertextInfo>, sender_key: String) -> Self {
        Self { ciphertext, sender_key }
    }
}

/// Ciphertext information holding the ciphertext and message type.
///
/// Used for messages encrypted with the *m.olm.v1.curve25519-aes-sha2* algorithm.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct CiphertextInfo {
    /// The encrypted payload.
    pub body: String,

    /// The Olm message type.
    #[serde(rename = "type")]
    pub message_type: UInt,
}

impl CiphertextInfo {
    /// Creates a new `CiphertextInfo` with the given body and type.
    pub fn new(body: String, message_type: UInt) -> Self {
        Self { body, message_type }
    }
}

/// The payload for `EncryptedEvent` using the *m.megolm.v1.aes-sha2* algorithm.
///
/// To create an instance of this type, first create a `MegolmV1AesSha2ContentInit` and convert it
/// via `MegolmV1AesSha2Content::from` / `.into()`.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct MegolmV1AesSha2Content {
    /// The encrypted content of the event.
    pub ciphertext: String,

    /// The Curve25519 key of the sender.
    pub sender_key: String,

    /// The ID of the sending device.
    pub device_id: DeviceIdBox,

    /// The ID of the session used to encrypt the message.
    pub session_id: String,

    /// Information about related messages for
    /// [rich replies](https://matrix.org/docs/spec/client_server/r0.6.1#rich-replies).
    #[serde(rename = "m.relates_to", skip_serializing_if = "Option::is_none")]
    pub relates_to: Option<Relation>,
}

/// Mandatory initial set of fields of `MegolmV1AesSha2Content`.
///
/// This struct will not be updated even if additional fields are added to `MegolmV1AesSha2Content`
/// in a new (non-breaking) release of the Matrix specification.
#[derive(Clone, Debug)]
pub struct MegolmV1AesSha2ContentInit {
    /// The encrypted content of the event.
    pub ciphertext: String,

    /// The Curve25519 key of the sender.
    pub sender_key: String,

    /// The ID of the sending device.
    pub device_id: DeviceIdBox,

    /// The ID of the session used to encrypt the message.
    pub session_id: String,
}

impl From<MegolmV1AesSha2ContentInit> for MegolmV1AesSha2Content {
    /// Creates a new `MegolmV1AesSha2Content` from the given init struct.
    fn from(init: MegolmV1AesSha2ContentInit) -> Self {
        let MegolmV1AesSha2ContentInit { ciphertext, sender_key, device_id, session_id } = init;
        Self { ciphertext, sender_key, device_id, session_id, relates_to: None }
    }
}

#[cfg(test)]
mod tests {
    use matches::assert_matches;
    use ruma_serde::Raw;
    use serde_json::{from_value as from_json_value, json, to_value as to_json_value};

    use super::{EncryptedEventContent, MegolmV1AesSha2Content};

    #[test]
    fn serialization() {
        let key_verification_start_content =
            EncryptedEventContent::MegolmV1AesSha2(MegolmV1AesSha2Content {
                ciphertext: "ciphertext".into(),
                sender_key: "sender_key".into(),
                device_id: "device_id".into(),
                session_id: "session_id".into(),
                relates_to: None,
            });

        let json_data = json!({
            "algorithm": "m.megolm.v1.aes-sha2",
            "ciphertext": "ciphertext",
            "sender_key": "sender_key",
            "device_id": "device_id",
            "session_id": "session_id"
        });

        assert_eq!(to_json_value(&key_verification_start_content).unwrap(), json_data);
    }

    #[test]
    fn deserialization() {
        let json_data = json!({
            "algorithm": "m.megolm.v1.aes-sha2",
            "ciphertext": "ciphertext",
            "sender_key": "sender_key",
            "device_id": "device_id",
            "session_id": "session_id"
        });

        assert_matches!(
            from_json_value::<Raw<EncryptedEventContent>>(json_data)
                .unwrap()
                .deserialize()
                .unwrap(),
            EncryptedEventContent::MegolmV1AesSha2(MegolmV1AesSha2Content {
                ciphertext,
                sender_key,
                device_id,
                session_id,
                relates_to: None,
            }) if ciphertext == "ciphertext"
                && sender_key == "sender_key"
                && device_id == "device_id"
                && session_id == "session_id"
        );
    }

    #[test]
    fn deserialization_olm() {
        let json_data = json!({
            "sender_key": "test_key",
            "ciphertext": {
                "test_curve_key": {
                    "body": "encrypted_body",
                    "type": 1
                }
            },
            "algorithm": "m.olm.v1.curve25519-aes-sha2"
        });
        let content = from_json_value::<Raw<EncryptedEventContent>>(json_data)
            .unwrap()
            .deserialize()
            .unwrap();

        match content {
            EncryptedEventContent::OlmV1Curve25519AesSha2(c) => {
                assert_eq!(c.sender_key, "test_key");
                assert_eq!(c.ciphertext.len(), 1);
                assert_eq!(c.ciphertext["test_curve_key"].body, "encrypted_body");
                assert_eq!(c.ciphertext["test_curve_key"].message_type, 1u16.into());
            }
            _ => panic!("Wrong content type, expected a OlmV1 content"),
        }
    }

    #[test]
    fn deserialization_failure() {
        assert!(from_json_value::<Raw<EncryptedEventContent>>(
            json!({ "algorithm": "m.megolm.v1.aes-sha2" })
        )
        .unwrap()
        .deserialize()
        .is_err());
    }
}
<commit_msg>events: Move m.relates_to to EncryptedEventContent<commit_after>//! Types for the *m.room.encrypted* event.

use std::collections::BTreeMap;

use js_int::UInt;
use ruma_events_macros::MessageEventContent;
use ruma_identifiers::DeviceIdBox;
use serde::{Deserialize, Serialize};

use crate::{room::message::Relation, MessageEvent};

/// An event that has been encrypted.
pub type EncryptedEvent = MessageEvent<EncryptedEventContent>;

/// The encryption scheme for `EncryptedEventContent`
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[serde(tag = "algorithm")]
pub enum EncryptedEventScheme {
    /// An event encrypted with *m.olm.v1.curve25519-aes-sha2*.
    #[serde(rename = "m.olm.v1.curve25519-aes-sha2")]
    OlmV1Curve25519AesSha2(OlmV1Curve25519AesSha2Content),

    /// An event encrypted with *m.megolm.v1.aes-sha2*.
    #[serde(rename = "m.megolm.v1.aes-sha2")]
    MegolmV1AesSha2(MegolmV1AesSha2Content),
}

/// The content payload for `EncryptedEvent`.
#[derive(Clone, Debug, Deserialize, Serialize, MessageEventContent)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[ruma_event(type = "m.room.encrypted")]
pub struct EncryptedEventContent {
    /// Encrypted event content
    #[serde(flatten)]
    pub scheme: EncryptedEventScheme,

    /// Information about related messages for
    /// [rich replies](https://matrix.org/docs/spec/client_server/r0.6.1#rich-replies).
    #[serde(rename = "m.relates_to", skip_serializing_if = "Option::is_none")]
    pub relates_to: Option<Relation>,
}

/// The to-device version of the payload for the `EncryptedEvent`.
pub type EncryptedToDeviceEventContent = EncryptedEventContent;

/// The payload for `EncryptedEvent` using the *m.olm.v1.curve25519-aes-sha2* algorithm.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct OlmV1Curve25519AesSha2Content {
    /// A map from the recipient Curve25519 identity key to ciphertext information.
    pub ciphertext: BTreeMap<String, CiphertextInfo>,

    /// The Curve25519 key of the sender.
    pub sender_key: String,
}

impl OlmV1Curve25519AesSha2Content {
    /// Creates a new `OlmV1Curve25519AesSha2Content` with the given ciphertext and sender key.
    pub fn new(ciphertext: BTreeMap<String, CiphertextInfo>, sender_key: String) -> Self {
        Self { ciphertext, sender_key }
    }
}

/// Ciphertext information holding the ciphertext and message type.
///
/// Used for messages encrypted with the *m.olm.v1.curve25519-aes-sha2* algorithm.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct CiphertextInfo {
    /// The encrypted payload.
    pub body: String,

    /// The Olm message type.
    #[serde(rename = "type")]
    pub message_type: UInt,
}

impl CiphertextInfo {
    /// Creates a new `CiphertextInfo` with the given body and type.
    pub fn new(body: String, message_type: UInt) -> Self {
        Self { body, message_type }
    }
}

/// The payload for `EncryptedEvent` using the *m.megolm.v1.aes-sha2* algorithm.
///
/// To create an instance of this type, first create a `MegolmV1AesSha2ContentInit` and convert it
/// via `MegolmV1AesSha2Content::from` / `.into()`.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct MegolmV1AesSha2Content {
    /// The encrypted content of the event.
    pub ciphertext: String,

    /// The Curve25519 key of the sender.
    pub sender_key: String,

    /// The ID of the sending device.
    pub device_id: DeviceIdBox,

    /// The ID of the session used to encrypt the message.
    pub session_id: String,
}

/// Mandatory initial set of fields of `MegolmV1AesSha2Content`.
///
/// This struct will not be updated even if additional fields are added to `MegolmV1AesSha2Content`
/// in a new (non-breaking) release of the Matrix specification.
#[derive(Clone, Debug)]
pub struct MegolmV1AesSha2ContentInit {
    /// The encrypted content of the event.
    pub ciphertext: String,

    /// The Curve25519 key of the sender.
    pub sender_key: String,

    /// The ID of the sending device.
    pub device_id: DeviceIdBox,

    /// The ID of the session used to encrypt the message.
    pub session_id: String,
}

impl From<MegolmV1AesSha2ContentInit> for MegolmV1AesSha2Content {
    /// Creates a new `MegolmV1AesSha2Content` from the given init struct.
    fn from(init: MegolmV1AesSha2ContentInit) -> Self {
        let MegolmV1AesSha2ContentInit { ciphertext, sender_key, device_id, session_id } = init;
        Self { ciphertext, sender_key, device_id, session_id }
    }
}

#[cfg(test)]
mod tests {
    use matches::assert_matches;
    use ruma_serde::Raw;
    use serde_json::{from_value as from_json_value, json, to_value as to_json_value};

    use super::{EncryptedEventContent, EncryptedEventScheme, MegolmV1AesSha2Content};
    use crate::room::message::{InReplyTo, Relation};
    use ruma_identifiers::event_id;

    #[test]
    fn serialization() {
        let key_verification_start_content = EncryptedEventContent {
            scheme: EncryptedEventScheme::MegolmV1AesSha2(MegolmV1AesSha2Content {
                ciphertext: "ciphertext".into(),
                sender_key: "sender_key".into(),
                device_id: "device_id".into(),
                session_id: "session_id".into(),
            }),
            relates_to: Some(Relation::Reply {
                in_reply_to: InReplyTo { event_id: event_id!("$h29iv0s8:example.com") },
            }),
        };

        let json_data = json!({
            "algorithm": "m.megolm.v1.aes-sha2",
            "ciphertext": "ciphertext",
            "sender_key": "sender_key",
            "device_id": "device_id",
            "session_id": "session_id",
            "m.relates_to": {
                "m.in_reply_to": {
                    "event_id": "$h29iv0s8:example.com"
                }
            },
        });

        assert_eq!(to_json_value(&key_verification_start_content).unwrap(), json_data);
    }

    #[test]
    fn deserialization() {
        let json_data = json!({
            "algorithm": "m.megolm.v1.aes-sha2",
            "ciphertext": "ciphertext",
            "sender_key": "sender_key",
            "device_id": "device_id",
            "session_id": "session_id",
            "m.relates_to": {
                "m.in_reply_to": {
                    "event_id": "$h29iv0s8:example.com"
                }
            },
        });

        let content: EncryptedEventContent =
            from_json_value::<Raw<EncryptedEventContent>>(json_data)
                .unwrap()
                .deserialize()
                .unwrap();

        assert_matches!(content.scheme, EncryptedEventScheme::MegolmV1AesSha2(MegolmV1AesSha2Content {
                ciphertext,
                sender_key,
                device_id,
                session_id,
            }) if ciphertext == "ciphertext"
                && sender_key == "sender_key"
                && device_id == "device_id"
                && session_id == "session_id"
        );

        assert_matches!(
            content.relates_to,
            Option::<Relation>::Some(Relation::Reply { in_reply_to })
                if in_reply_to.event_id == event_id!("$h29iv0s8:example.com")
        );
    }

    #[test]
    fn deserialization_olm() {
        let json_data = json!({
            "sender_key": "test_key",
            "ciphertext": {
                "test_curve_key": {
                    "body": "encrypted_body",
                    "type": 1
                }
            },
            "algorithm": "m.olm.v1.curve25519-aes-sha2"
        });
        let content = from_json_value::<Raw<EncryptedEventContent>>(json_data)
            .unwrap()
            .deserialize()
            .unwrap();

        match content.scheme {
            EncryptedEventScheme::OlmV1Curve25519AesSha2(c) => {
                assert_eq!(c.sender_key, "test_key");
                assert_eq!(c.ciphertext.len(), 1);
                assert_eq!(c.ciphertext["test_curve_key"].body, "encrypted_body");
                assert_eq!(c.ciphertext["test_curve_key"].message_type, 1u16.into());
            }
            _ => panic!("Wrong content type, expected a OlmV1 content"),
        }
    }

    #[test]
    fn deserialization_failure() {
        assert!(from_json_value::<Raw<EncryptedEventContent>>(
            json!({ "algorithm": "m.megolm.v1.aes-sha2" })
        )
        .unwrap()
        .deserialize()
        .is_err());
    }
}
<|endoftext|>


<commit_before>// $ cargo bench --features full --bench rust

#![feature(rustc_private)]

extern crate rustc_data_structures;
extern crate syntax;
extern crate syntax_pos;

#[path = "../tests/repo/mod.rs"]
mod repo;

use proc_macro2::TokenStream;
use rustc_data_structures::sync::Lrc;
use std::fs;
use std::str::FromStr;
use std::time::{Duration, Instant};
use syntax::edition::Edition;
use syntax::errors::{emitter::Emitter, DiagnosticBuilder, Handler};
use syntax::parse::ParseSess;
use syntax::source_map::{FilePathMapping, SourceMap};
use syntax_pos::FileName;

fn tokenstream_parse(content: &str) -> Result<(), ()> {
    TokenStream::from_str(content).map(drop).map_err(drop)
}

fn syn_parse(content: &str) -> Result<(), ()> {
    syn::parse_file(content).map(drop).map_err(drop)
}

fn libsyntax_parse(content: &str) -> Result<(), ()> {
    struct SilentEmitter;

    impl Emitter for SilentEmitter {
        fn emit_diagnostic(&mut self, _db: &DiagnosticBuilder) {}
    }

    syntax::with_globals(Edition::Edition2018, || {
        let cm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
        let emitter = Box::new(SilentEmitter);
        let handler = Handler::with_emitter(false, None, emitter);
        let sess = ParseSess::with_span_handler(handler, cm);
        if let Err(mut diagnostic) = syntax::parse::parse_crate_from_source_str(
            FileName::Custom("bench".to_owned()),
            content.to_owned(),
            &sess,
        ) {
            diagnostic.cancel();
            return Err(());
        };
        Ok(())
    })
}

fn read_from_disk(content: &str) -> Result<(), ()> {
    let _ = content;
    Ok(())
}

fn exec(mut codepath: impl FnMut(&str) -> Result<(), ()>) -> Duration {
    let begin = Instant::now();
    let mut success = 0;
    let mut total = 0;

    walkdir::WalkDir::new("tests/rust/src")
        .into_iter()
        .filter_entry(repo::base_dir_filter)
        .for_each(|entry| {
            let entry = entry.unwrap();
            let path = entry.path();
            if path.is_dir() {
                return;
            }
            let content = fs::read_to_string(path).unwrap();
            let ok = codepath(&content).is_ok();
            success += ok as usize;
            total += 1;
            if !ok {
                eprintln!("FAIL {}", path.display());
            }
        });

    assert_eq!(success, total);
    begin.elapsed()
}

fn main() {
    repo::clone_rust();

    macro_rules! testcases {
        ($($name:ident,)*) => {
            vec![
                $(
                    (stringify!($name), $name as fn(&str) -> Result<(), ()>),
                )*
            ]
        };
    }

    let mut lines = 0;
    let mut files = 0;
    exec(|content| {
        lines += content.lines().count();
        files += 1;
        Ok(())
    });
    eprintln!("\n{} lines in {} files", lines, files);

    for (name, f) in testcases!(
        read_from_disk,
        tokenstream_parse,
        syn_parse,
        libsyntax_parse,
    ) {
        eprint!("{:20}", format!("{}:", name));
        let elapsed = exec(f);
        eprintln!(
            "elapsed={}.{:03}s",
            elapsed.as_secs(),
            elapsed.subsec_millis(),
        );
    }
    eprintln!();
}
<commit_msg>Flag to benchmark syn only<commit_after>// $ cargo bench --features full --bench rust
//
// Syn only, useful for profiling:
// $ RUSTFLAGS='--cfg syn_only' cargo build --release --features full --bench rust

#![cfg_attr(not(syn_only), feature(rustc_private))]

#[cfg(not(syn_only))]
extern crate rustc_data_structures;
#[cfg(not(syn_only))]
extern crate syntax;
#[cfg(not(syn_only))]
extern crate syntax_pos;

#[path = "../tests/repo/mod.rs"]
mod repo;

#[cfg(not(syn_only))]
use proc_macro2::TokenStream;
#[cfg(not(syn_only))]
use rustc_data_structures::sync::Lrc;
#[cfg(not(syn_only))]
use std::str::FromStr;
#[cfg(not(syn_only))]
use syntax::edition::Edition;
#[cfg(not(syn_only))]
use syntax::errors::{emitter::Emitter, DiagnosticBuilder, Handler};
#[cfg(not(syn_only))]
use syntax::parse::ParseSess;
#[cfg(not(syn_only))]
use syntax::source_map::{FilePathMapping, SourceMap};
#[cfg(not(syn_only))]
use syntax_pos::FileName;

use std::fs;
use std::time::{Duration, Instant};

#[cfg(not(syn_only))]
fn tokenstream_parse(content: &str) -> Result<(), ()> {
    TokenStream::from_str(content).map(drop).map_err(drop)
}

fn syn_parse(content: &str) -> Result<(), ()> {
    syn::parse_file(content).map(drop).map_err(drop)
}

#[cfg(not(syn_only))]
fn libsyntax_parse(content: &str) -> Result<(), ()> {
    struct SilentEmitter;

    impl Emitter for SilentEmitter {
        fn emit_diagnostic(&mut self, _db: &DiagnosticBuilder) {}
    }

    syntax::with_globals(Edition::Edition2018, || {
        let cm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
        let emitter = Box::new(SilentEmitter);
        let handler = Handler::with_emitter(false, None, emitter);
        let sess = ParseSess::with_span_handler(handler, cm);
        if let Err(mut diagnostic) = syntax::parse::parse_crate_from_source_str(
            FileName::Custom("bench".to_owned()),
            content.to_owned(),
            &sess,
        ) {
            diagnostic.cancel();
            return Err(());
        };
        Ok(())
    })
}

#[cfg(not(syn_only))]
fn read_from_disk(content: &str) -> Result<(), ()> {
    let _ = content;
    Ok(())
}

fn exec(mut codepath: impl FnMut(&str) -> Result<(), ()>) -> Duration {
    let begin = Instant::now();
    let mut success = 0;
    let mut total = 0;

    walkdir::WalkDir::new("tests/rust/src")
        .into_iter()
        .filter_entry(repo::base_dir_filter)
        .for_each(|entry| {
            let entry = entry.unwrap();
            let path = entry.path();
            if path.is_dir() {
                return;
            }
            let content = fs::read_to_string(path).unwrap();
            let ok = codepath(&content).is_ok();
            success += ok as usize;
            total += 1;
            if !ok {
                eprintln!("FAIL {}", path.display());
            }
        });

    assert_eq!(success, total);
    begin.elapsed()
}

fn main() {
    repo::clone_rust();

    macro_rules! testcases {
        ($($(#[$cfg:meta])* $name:ident,)*) => {
            vec![
                $(
                    $(#[$cfg])*
                    (stringify!($name), $name as fn(&str) -> Result<(), ()>),
                )*
            ]
        };
    }

    #[cfg(not(syn_only))]
    {
        let mut lines = 0;
        let mut files = 0;
        exec(|content| {
            lines += content.lines().count();
            files += 1;
            Ok(())
        });
        eprintln!("\n{} lines in {} files", lines, files);
    }

    for (name, f) in testcases!(
        #[cfg(not(syn_only))]
        read_from_disk,
        #[cfg(not(syn_only))]
        tokenstream_parse,
        syn_parse,
        #[cfg(not(syn_only))]
        libsyntax_parse,
    ) {
        eprint!("{:20}", format!("{}:", name));
        let elapsed = exec(f);
        eprintln!(
            "elapsed={}.{:03}s",
            elapsed.as_secs(),
            elapsed.subsec_millis(),
        );
    }
    eprintln!();
}
<|endoftext|>


<commit_before>// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//! Logging for alacritty.
//!
//! The main executable is supposed to call `initialize()` exactly once during
//! startup. All logging messages are written to stdout, given that their
//! log-level is sufficient for the level configured in `cli::Options`.
use log;
use std::sync;
use std::io;
use cli;

pub struct Logger<T:Send+io::Write> {
    level: log::LogLevelFilter,
    output: sync::Mutex<T>
}

impl<T> Logger<T> where T:Send+io::Write {
    pub fn new(output: T, level: log::LogLevelFilter) -> Logger<io::LineWriter<T>> {
        Logger { level: level,
                 output: sync::Mutex::new(io::LineWriter::new(output)) }
    }
}

impl<T> log::Log for Logger<T> where T:Send+io::Write {
    fn enabled(&self, metadata: &log::LogMetadata) -> bool {
        metadata.level() <= self.level
    }

    fn log(&self, record: &log::LogRecord) {
        if !self.enabled(record.metadata()) {
            return
        }
        let writer = &mut self.output.lock().unwrap();
        if self.level < log::LogLevelFilter::Debug {
            writer.write(format!("{}\n", record.args()).as_ref()).unwrap();
        } else {
            writer.write(format!("{}: {}\n",
                                 record.target(),
                                 record.args())
                         .as_ref()).unwrap();
        }
    }
}

pub fn initialize(options: &cli::Options) {
    log::set_logger(|max_log_level| {
        max_log_level.set(options.log_level);
        Box::new(Logger::new(io::stdout(), options.log_level))
    }).unwrap_or_else(|e| die!("{}", e));
}
<commit_msg>Changes to Logger logic<commit_after>// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//! Logging for alacritty.
//!
//! The main executable is supposed to call `initialize()` exactly once during
//! startup. All logging messages are written to stdout, given that their
//! log-level is sufficient for the level configured in `cli::Options`.
use log;
use std::sync;
use std::io;
use cli;

pub struct Logger<T:Send+io::Write> {
    level: log::LogLevelFilter,
    output: sync::Mutex<T>
}

impl<T> Logger<T> where T:Send+io::Write {
    pub fn new(output: T, level: log::LogLevelFilter) -> Logger<io::LineWriter<T>> {
        Logger { level: level,
                 output: sync::Mutex::new(io::LineWriter::new(output)) }
    }
}

impl<T> log::Log for Logger<T> where T:Send+io::Write {
    fn enabled(&self, metadata: &log::LogMetadata) -> bool {
        metadata.level() <= self.level
    }

    fn log(&self, record: &log::LogRecord) {
        if self.enabled(record.metadata()) {
            if record.target().starts_with("alacritty") {
                if let Ok(ref mut writer) = self.output.lock() {
                    let _ = writer.write(format!("{}\n", record.args()).as_ref());
                }
            }
        }
    }
}

pub fn initialize(options: &cli::Options) {
    log::set_logger(|max_log_level| {
        max_log_level.set(options.log_level);
        Box::new(Logger::new(io::stdout(), options.log_level))
    }).unwrap_or_else(|e| die!("{}", e));
}
<|endoftext|>


<commit_before>// Copyright (C) 2019 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

use serde::Deserialize;

/// Handles when an object should be installed on target.
#[derive(PartialEq, Debug, Deserialize)]
#[serde(untagged)]
pub enum InstallIfDifferent {
    #[serde(deserialize_with = "deserialize_checksum")]
    /// Use checksum to check.
    CheckSum,
    /// Use a predefined (known) pattern to check.
    KnownPattern {
        version: String,
        pattern: KnownPatternKind,
    },
    /// Use a custom pattern to check.
    CustomPattern { version: String, pattern: Pattern },
}

/// Known patterns to be used with
/// [InstallIfDifferent](InstallIfDifferent::KnownPattern)
#[derive(PartialEq, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum KnownPatternKind {
    LinuxKernel,
    UBoot,
}

/// Custom pattern to use with
/// [InstallIfDifferent](InstallIfDifferent::CustomPattern)
#[derive(PartialEq, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Pattern {
    pub regexp: String,
    pub seek: u64,
    pub buffer_size: u64,
}

fn deserialize_checksum<'de, D, E>(deserializer: D) -> Result<(), E>
where
    D: serde::Deserializer<'de>,
    E: serde::de::Error,
    E: From<D::Error>,
{
    match String::deserialize(deserializer)?.to_lowercase().as_str() {
        "sha256sum" => Ok(()),
        s => Err(E::custom(format!("Not a vliad CheckSum format: {}", s))),
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use pretty_assertions::assert_eq;
    use serde_json::json;

    #[test]
    fn deserialize() {
        assert_eq!(
            InstallIfDifferent::CheckSum,
            serde_json::from_value::<InstallIfDifferent>(json!("sha256sum")).unwrap()
        );
        assert_eq!(
            InstallIfDifferent::CustomPattern {
                version: "2.0".to_string(),
                pattern: Pattern {
                    regexp: "[0-9.]+".to_string(),
                    seek: 1024,
                    buffer_size: 2048,
                }
            },
            serde_json::from_value::<InstallIfDifferent>(json!({
                "version": "2.0",
                "pattern": {
                    "regexp": "[0-9.]+",
                    "seek": 1024,
                    "buffer-size": 2048
                }
            }))
            .unwrap()
        );
        assert_eq!(
            InstallIfDifferent::KnownPattern {
                version: "4.7.4-1-ARCH".to_string(),
                pattern: KnownPatternKind::LinuxKernel,
            },
            serde_json::from_value::<InstallIfDifferent>(json!({
                "version": "4.7.4-1-ARCH",
                "pattern": "linux-kernel"
            }))
            .unwrap()
        )
    }
}
<commit_msg>updatehub-package-schema: Clippy notes<commit_after>// Copyright (C) 2019 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

use serde::Deserialize;

/// Handles when an object should be installed on target.
#[derive(PartialEq, Debug, Deserialize)]
#[serde(untagged)]
pub enum InstallIfDifferent {
    #[serde(deserialize_with = "deserialize_checksum")]
    /// Use checksum to check.
    CheckSum,
    /// Use a predefined (known) pattern to check.
    KnownPattern {
        version: String,
        pattern: KnownPatternKind,
    },
    /// Use a custom pattern to check.
    CustomPattern { version: String, pattern: Pattern },
}

/// Known patterns to be used with
/// [InstallIfDifferent](InstallIfDifferent::KnownPattern)
#[derive(PartialEq, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum KnownPatternKind {
    LinuxKernel,
    UBoot,
}

/// Custom pattern to use with
/// [InstallIfDifferent](InstallIfDifferent::CustomPattern)
#[derive(PartialEq, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Pattern {
    pub regexp: String,
    pub seek: u64,
    pub buffer_size: u64,
}

fn deserialize_checksum<'de, D, E>(deserializer: D) -> Result<(), E>
where
    D: serde::Deserializer<'de>,
    E: serde::de::Error + From<D::Error>,
{
    match String::deserialize(deserializer)?.to_lowercase().as_str() {
        "sha256sum" => Ok(()),
        s => Err(E::custom(format!("Not a vliad CheckSum format: {}", s))),
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use pretty_assertions::assert_eq;
    use serde_json::json;

    #[test]
    fn deserialize() {
        assert_eq!(
            InstallIfDifferent::CheckSum,
            serde_json::from_value::<InstallIfDifferent>(json!("sha256sum")).unwrap()
        );
        assert_eq!(
            InstallIfDifferent::CustomPattern {
                version: "2.0".to_string(),
                pattern: Pattern {
                    regexp: "[0-9.]+".to_string(),
                    seek: 1024,
                    buffer_size: 2048,
                }
            },
            serde_json::from_value::<InstallIfDifferent>(json!({
                "version": "2.0",
                "pattern": {
                    "regexp": "[0-9.]+",
                    "seek": 1024,
                    "buffer-size": 2048
                }
            }))
            .unwrap()
        );
        assert_eq!(
            InstallIfDifferent::KnownPattern {
                version: "4.7.4-1-ARCH".to_string(),
                pattern: KnownPatternKind::LinuxKernel,
            },
            serde_json::from_value::<InstallIfDifferent>(json!({
                "version": "4.7.4-1-ARCH",
                "pattern": "linux-kernel"
            }))
            .unwrap()
        )
    }
}
<|endoftext|>


<commit_before>use time::precise_time_ns;

use std::sync::mpsc::{channel, Sender};
use std::thread::Thread;

use search;
use timer;

use super::types::{Cmd, Response};
use super::state::{State, Mode};

mod go_param;

pub fn process(state: &mut State, cmd: Cmd, output: &Sender<Response>) {
    match cmd {
        Cmd::Debug(val) => {
            state.is_debug = val;
            state.search_tx.as_ref().map(|tx| { let _ = tx.send(search::Cmd::Stop); } );
        },
        Cmd::IsReady => {
            // TODO implement IsReady
            unimplemented!();
        },
        Cmd::Register(ref param) => {
            // TODO register
            unimplemented!();
        },
        cmd => {
            match state.mode {
                Mode::Init => {
                    if cmd == Cmd::Uci {
                        // TODO print id/option/uciok
                        state.mode = Mode::Wait;
                        unimplemented!();
                    }
                },
                Mode::Wait => {
                    match cmd {
                        Cmd::SetOption(ref name, ref val_opt) => {
                            // TODO maybe initialize
                            // TODO set options
                            unimplemented!();
                        },
                        Cmd::UciNewGame => {
                            // TODO note that ucinewgame supported
                            // TODO reset game status
                            state.mode = Mode::NewGame;
                            unimplemented!();
                        },
                        cmd @Cmd::SetupPosition(..) => {
                            if state.ucinewgame_support {
                                // TODO set up position for same game
                                state.mode = Mode::Ready;
                                unimplemented!();
                            } else {
                                process(state, Cmd::UciNewGame, output);
                                state.ucinewgame_support = false;
                                process(state, cmd, output);
                            }
                        },
                        _ => {},
                    }
                },
                Mode::NewGame => {
                    if let Cmd::SetupPosition(ref pos, ref from_to_vec) = cmd {
                        // TODO setup position for new game
                        state.mode = Mode::Ready;
                        unimplemented!();
                    }
                },
                Mode::Ready => {
                    assert!(state.search_state.is_some());
                    if let Cmd::Go(param) = cmd {
                        go_param::setup(state, param);
                        let (search_tx, search_rx) = channel::<search::Cmd>();
                        let search_state = state.search_state.as_ref().unwrap().clone();
                        let output = output.clone();
                        let temp = Thread::scoped(move ||
                                                  search::start(search_state, search_rx, output));

                        state.search_tx = Some(search_tx.clone());
                        state.search_guard = Some(temp);

                        let time_data = state.time_data.clone().unwrap();
                        let c = state.search_state.as_ref().unwrap().pos.side_to_move();

                        Thread::spawn(move || timer::start(time_data, c, search_tx));

                        state.mode = Mode::Search;
                    }
                },
                Mode::Search => {
                    assert!(state.search_state.is_some());
                    assert!(state.search_guard.is_some());
                    assert!(state.search_tx.is_some());
                    match cmd {
                        Cmd::PonderHit => {
                            state.search_tx.as_ref().map(|ref tx| tx.send(search::Cmd::PonderHit));
                            state.search_state.as_mut().map(|ref mut x| x.param.ponder = false);
                            state.start_move_time = Some(precise_time_ns());
                        },
                        Cmd::Stop => {
                            state.search_tx.as_mut().map(|ref tx| tx.send(search::Cmd::Stop));
                            state.search_guard.take().map(|x| x.join());
                            state.search_tx = None;
                            state.mode = Mode::Wait;
                            state.start_search_time = None;
                            state.start_move_time = None;
                            state.time_data = None;
                            // TODO what to do about search_state
                            // TODO what to do about the timer thread
                            // TODO what if time is out
                            unimplemented!();
                        },
                        _ => {},
                    }
                },
            }
        },
    }
}
<commit_msg>Clean up process() function.<commit_after>use time::precise_time_ns;

use std::sync::mpsc::{channel, Sender};
use std::thread::Thread;

use search;
use timer;

use super::types::{Cmd, Response};
use super::state::{State, Mode};

mod go_param;

pub fn process(state: &mut State, cmd: Cmd, output: &Sender<Response>) {
    match cmd {
        Cmd::Debug(val) => {
            state.is_debug = val;
            state.search_tx.as_ref().map(|tx| { let _ = tx.send(search::Cmd::Stop); } );
        },
        Cmd::IsReady => {
            // TODO implement IsReady
            unimplemented!();
        },
        Cmd::Register(ref param) => {
            // TODO register
            unimplemented!();
        },
        cmd => {
            match state.mode {
                Mode::Init => {
                    if cmd == Cmd::Uci {
                        // TODO print id/option/uciok
                        state.mode = Mode::Wait;
                        unimplemented!();
                    }
                },
                Mode::Wait => {
                    match cmd {
                        Cmd::SetOption(ref name, ref val_opt) => {
                            // TODO maybe initialize
                            // TODO set options
                            unimplemented!();
                        },
                        Cmd::UciNewGame => {
                            // TODO note that ucinewgame supported
                            // TODO reset game status
                            state.mode = Mode::NewGame;
                            unimplemented!();
                        },
                        cmd @Cmd::SetupPosition(..) => {
                            if state.ucinewgame_support {
                                // TODO set up position for same game
                                state.mode = Mode::Ready;
                                unimplemented!();
                            } else {
                                process(state, Cmd::UciNewGame, output);
                                state.ucinewgame_support = false;
                                process(state, cmd, output);
                            }
                        },
                        _ => {},
                    }
                },
                Mode::NewGame => {
                    if let Cmd::SetupPosition(ref pos, ref from_to_vec) = cmd {
                        // TODO setup position for new game
                        state.mode = Mode::Ready;
                        unimplemented!();
                    }
                },
                Mode::Ready => {
                    assert!(state.search_state.is_some());
                    if let Cmd::Go(param) = cmd {
                        go_param::setup(state, param);
                        let (search_tx, search_rx) = channel::<search::Cmd>();
                        let search_state = state.search_state.as_ref().unwrap().clone();
                        let output = output.clone();
                        let temp = Thread::scoped(move ||
                                                  search::start(search_state, search_rx, output));

                        state.search_tx = Some(search_tx.clone());
                        state.search_guard = Some(temp);

                        let time_data = state.time_data.clone().unwrap();
                        let c = state.search_state.as_ref().unwrap().pos.side_to_move();

                        Thread::spawn(move || timer::start(time_data, c, search_tx));

                        state.mode = Mode::Search;
                    }
                },
                Mode::Search => {
                    assert!(state.search_state.is_some());
                    assert!(state.search_guard.is_some());
                    assert!(state.search_tx.is_some());
                    match cmd {
                        Cmd::PonderHit => {
                            if !state.search_state.as_ref().unwrap().param.ponder {
                                return;
                            }
                            let _ = state.search_tx.as_ref().unwrap().send(search::Cmd::PonderHit);
                            state.search_state.as_mut().unwrap().param.ponder = false;
                            state.start_move_time = Some(precise_time_ns());
                        },
                        Cmd::Stop => {
                            let _ = state.search_tx.as_ref().unwrap().send(search::Cmd::Stop);
                            let _ = state.search_guard.take().unwrap().join();
                            state.search_tx = None;
                            state.mode = Mode::Wait;
                            state.start_search_time = None;
                            state.start_move_time = None;
                            state.time_data = None;
                            // TODO what to do about search_state
                            // TODO what to do about the timer thread
                            // TODO what if time is out
                            unimplemented!();
                        },
                        _ => {},
                    }
                },
            }
        },
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use lsystem::{LRules};

pub struct LStringRule {
    productions: HashMap<char, Vec<char>>,
}

impl LStringRule {
    pub fn new() -> LStringRule {
        LStringRule {
            productions: HashMap::new(),
        }
    }

    pub fn set(&mut self, k: char, v: Vec<char>) {
        self.productions.insert(k, v);
    }

    pub fn set_str(&mut self, k: char, v: &str) {
        let mut rule = Vec::new();
        for c in v.chars() {
            rule.push(c);
        }
        self.productions.insert(k, rule);
    }
}

impl LRules<char> for LStringRule {
    fn map(&self, k: &char) -> Option<Vec<char>> {
        match self.productions.get(k) {
            Some(v) => Some(v.clone()),
            None => None,
        }
    }
}

pub fn show(v: &Vec<char>) -> String {
    let mut out = String::with_capacity(v.len());
    for c in v.iter() {
        out.push(*c);
    }
    out
}

#[cfg(test)]
pub mod tests {
    use super::*;
    use lsystem::{LSystem};

    #[test]
    fn test_algae_str() {
        let mut rules = LStringRule::new();
        rules.set_str('A', "AB");
        rules.set_str('B', "A");
        let axiom = "A".chars().collect();
        let mut system = LSystem::new(rules, axiom);
        let out = system.next().unwrap();
        let expected: Vec<char> = "AB".chars().collect();
        assert_eq!(expected, out);
        let out = system.next().unwrap();
        let expected: Vec<char> = "ABA".chars().collect();
        assert_eq!(expected, out);
        let out = system.next().unwrap();
        let expected: Vec<char> = "ABAAB".chars().collect();
        assert_eq!(expected, out);
        let out = system.next().unwrap();
        let expected: Vec<char> = "ABAABABA".chars().collect();
        assert_eq!(expected, out);
        let out = system.next().unwrap();
        let expected: Vec<char> = "ABAABABAABAAB".chars().collect();
        assert_eq!(expected, out);
        let out = system.next().unwrap();
        let expected: Vec<char> = "ABAABABAABAABABAABABA".chars().collect();
        assert_eq!(expected, out);
        let out = system.next().unwrap();
        let expected: Vec<char> = "ABAABABAABAABABAABABAABAABABAABAAB".chars().collect();
        assert_eq!(expected, out);
    }
}
<commit_msg>write a second test for string lsystems<commit_after>use std::collections::HashMap;
use lsystem::{LRules};

pub struct LStringRule {
    productions: HashMap<char, Vec<char>>,
}

impl LStringRule {
    pub fn new() -> LStringRule {
        LStringRule {
            productions: HashMap::new(),
        }
    }

    pub fn set(&mut self, k: char, v: Vec<char>) {
        self.productions.insert(k, v);
    }

    pub fn set_str(&mut self, k: char, v: &str) {
        let mut rule = Vec::new();
        for c in v.chars() {
            rule.push(c);
        }
        self.productions.insert(k, rule);
    }
}

impl LRules<char> for LStringRule {
    fn map(&self, k: &char) -> Option<Vec<char>> {
        match self.productions.get(k) {
            Some(v) => Some(v.clone()),
            None => None,
        }
    }
}

pub fn show(v: &Vec<char>) -> String {
    let mut out = String::with_capacity(v.len());
    for c in v.iter() {
        out.push(*c);
    }
    out
}

#[cfg(test)]
pub mod tests {
    use super::*;
    use lsystem::{LSystem};

    #[test]
    fn test_algae_str() {
        let mut rules = LStringRule::new();
        rules.set_str('A', "AB");
        rules.set_str('B', "A");
        let axiom = "A".chars().collect();
        let mut system = LSystem::new(rules, axiom);

        let out = system.next().unwrap();
        let expected: Vec<char> = "AB".chars().collect();
        assert_eq!(expected, out);

        let out = system.next().unwrap();
        let expected: Vec<char> = "ABA".chars().collect();
        assert_eq!(expected, out);

        let out = system.next().unwrap();
        let expected: Vec<char> = "ABAAB".chars().collect();
        assert_eq!(expected, out);

        let out = system.next().unwrap();
        let expected: Vec<char> = "ABAABABA".chars().collect();
        assert_eq!(expected, out);

        let out = system.next().unwrap();
        let expected: Vec<char> = "ABAABABAABAAB".chars().collect();
        assert_eq!(expected, out);

        let out = system.next().unwrap();
        let expected: Vec<char> = "ABAABABAABAABABAABABA".chars().collect();
        assert_eq!(expected, out);

        let out = system.next().unwrap();
        let expected: Vec<char> = "ABAABABAABAABABAABABAABAABABAABAAB".chars().collect();
        assert_eq!(expected, out);
    }

    #[test]
    fn test_pythagoras_tree() {
        let mut rules = LStringRule::new();
        rules.set_str('1', "11");
        rules.set_str('0', "1[0]0");
        let axiom = "0".chars().collect();
        let mut system = LSystem::new(rules, axiom);

        let out = system.next().unwrap();
        let expected: Vec<char> = "1[0]0".chars().collect();
        assert_eq!(expected, out);

        let out = system.next().unwrap();
        let expected: Vec<char> = "11[1[0]0]1[0]0".chars().collect();
        assert_eq!(expected, out);

        let out = system.next().unwrap();
        let expected: Vec<char> = "1111[11[1[0]0]1[0]0]11[1[0]0]1[0]0".chars().collect();
        assert_eq!(expected, out);
    }
}
<|endoftext|>


<commit_before>use std::path::PathBuf;

use isahc::{HttpClient, ReadResponseExt};
use semver::Version;
use serde::{de::IgnoredAny, Deserialize};
use toml_edit::{value, Document};
use xshell::{cmd, pushd, read_file, write_file};

use crate::{util::ask_yes_no, Metadata, Result};

const CRATESIO_API: &str = "https://crates.io/api/v1/crates";

/// A cargo package.
#[derive(Clone, Debug, Deserialize)]
pub struct Package {
    /// The package name
    pub name: String,

    /// The package version.
    pub version: Version,

    /// The package's manifest path.
    pub manifest_path: PathBuf,

    /// A map of the package dependencies.
    #[serde(default)]
    pub dependencies: Vec<Dependency>,
}

impl Package {
    /// Update the version of this crate.
    pub fn update_version(&mut self, version: &Version, dry_run: bool) -> Result<()> {
        println!("Updating {} to version {}…", self.name, version);

        if !dry_run {
            let mut document = read_file(&self.manifest_path)?.parse::<Document>()?;

            document["package"]["version"] = value(version.to_string());

            write_file(&self.manifest_path, document.to_string())?;
        }

        self.version = version.clone();

        Ok(())
    }

    /// Update the version of this crate in dependant crates' manifests, with the given version
    /// prefix.
    pub(crate) fn update_dependants(&self, metadata: &Metadata, dry_run: bool) -> Result<()> {
        for package in metadata.packages.iter().filter(|p| {
            p.manifest_path.starts_with(&metadata.workspace_root)
                && p.dependencies.iter().any(|d| d.name == self.name)
        }) {
            println!("Updating dependency in {} crate…", package.name);

            if !dry_run {
                let mut document = read_file(&package.manifest_path)?.parse::<Document>()?;

                let version = if !self.version.pre.is_empty()
                    || self.name.strip_suffix("-macros") == Some(&package.name)
                {
                    format!("={}", self.version)
                } else {
                    self.version.to_string()
                };

                for dependency in package.dependencies.iter().filter(|d| d.name == self.name) {
                    let kind = match dependency.kind {
                        Some(DependencyKind::Dev) => "dev-dependencies",
                        Some(DependencyKind::Build) => "build-dependencies",
                        None => "dependencies",
                    };

                    document[kind][&self.name]["version"] = value(version.as_str());
                }

                write_file(&package.manifest_path, document.to_string())?;
            }
        }

        Ok(())
    }

    /// Get the changes for the version.
    ///
    /// If `update` is `true`, update the changelog for the release of the given version.
    pub fn changes(&self, update: bool) -> Result<String> {
        let mut changelog_path = self.manifest_path.clone();
        changelog_path.set_file_name("CHANGELOG.md");

        let changelog = read_file(&changelog_path)?;
        let version = Version {
            pre: semver::Prerelease::EMPTY,
            build: semver::BuildMetadata::EMPTY,
            ..self.version.clone()
        };

        if !changelog.starts_with(&format!("# {}\n", version))
            && !changelog.starts_with(&format!("# {} (unreleased)\n", version))
            && !changelog.starts_with("# [unreleased]\n")
        {
            return Err("Could not find version title in changelog".into());
        };

        let changes_start = match changelog.find('\n') {
            Some(p) => p + 1,
            None => {
                return Err("Could not find end of version title in changelog".into());
            }
        };

        let changes_end = match changelog[changes_start..].find("\n# ") {
            Some(p) => changes_start + p,
            None => changelog.len(),
        };

        let changes = match changelog[changes_start..changes_end].trim() {
            s if s.is_empty() => "No changes for this version",
            s => s,
        };

        if update {
            let changelog = format!(
                "# [unreleased]\n\n# {}\n\n{}\n{}",
                self.version,
                changes,
                &changelog[changes_end..]
            );

            write_file(&changelog_path, changelog)?;
        }

        Ok(changes.to_owned())
    }

    /// Check if the current version of the crate is published on crates.io.
    pub fn is_published(&self, client: &HttpClient) -> Result<bool> {
        let response: CratesIoCrate =
            client.get(format!("{}/{}/{}", CRATESIO_API, self.name, self.version))?.json()?;

        Ok(response.version.is_some())
    }

    /// Publish this package on crates.io.
    pub fn publish(&self, client: &HttpClient, dry_run: bool) -> Result<bool> {
        println!("Publishing {} {} on crates.io…", self.name, self.version);
        let _dir = pushd(&self.manifest_path.parent().unwrap())?;

        if self.is_published(client)? {
            if ask_yes_no("This version is already published. Skip this step and continue?")? {
                Ok(false)
            } else {
                Err("Release interrupted by user.".into())
            }
        } else {
            if !dry_run {
                cmd!("cargo publish").run()?;
            }
            Ok(true)
        }
    }
}

/// A cargo package dependency.
#[derive(Clone, Debug, Deserialize)]
pub struct Dependency {
    /// The package name.
    pub name: String,

    /// The kind of the dependency.
    pub kind: Option<DependencyKind>,
}

/// The kind of a cargo package dependency.
#[derive(Clone, Debug, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum DependencyKind {
    /// A dev dependency.
    Dev,

    /// A build dependency.
    Build,
}

/// A crate from the `GET /crates/{crate}` endpoint of crates.io.
#[derive(Deserialize)]
struct CratesIoCrate {
    version: Option<IgnoredAny>,
}
<commit_msg>xtask: Don't update changelog when version is set<commit_after>use std::path::PathBuf;

use isahc::{HttpClient, ReadResponseExt};
use semver::Version;
use serde::{de::IgnoredAny, Deserialize};
use toml_edit::{value, Document};
use xshell::{cmd, pushd, read_file, write_file};

use crate::{util::ask_yes_no, Metadata, Result};

const CRATESIO_API: &str = "https://crates.io/api/v1/crates";

/// A cargo package.
#[derive(Clone, Debug, Deserialize)]
pub struct Package {
    /// The package name
    pub name: String,

    /// The package version.
    pub version: Version,

    /// The package's manifest path.
    pub manifest_path: PathBuf,

    /// A map of the package dependencies.
    #[serde(default)]
    pub dependencies: Vec<Dependency>,
}

impl Package {
    /// Update the version of this crate.
    pub fn update_version(&mut self, version: &Version, dry_run: bool) -> Result<()> {
        println!("Updating {} to version {}…", self.name, version);

        if !dry_run {
            let mut document = read_file(&self.manifest_path)?.parse::<Document>()?;

            document["package"]["version"] = value(version.to_string());

            write_file(&self.manifest_path, document.to_string())?;
        }

        self.version = version.clone();

        Ok(())
    }

    /// Update the version of this crate in dependant crates' manifests, with the given version
    /// prefix.
    pub(crate) fn update_dependants(&self, metadata: &Metadata, dry_run: bool) -> Result<()> {
        for package in metadata.packages.iter().filter(|p| {
            p.manifest_path.starts_with(&metadata.workspace_root)
                && p.dependencies.iter().any(|d| d.name == self.name)
        }) {
            println!("Updating dependency in {} crate…", package.name);

            if !dry_run {
                let mut document = read_file(&package.manifest_path)?.parse::<Document>()?;

                let version = if !self.version.pre.is_empty()
                    || self.name.strip_suffix("-macros") == Some(&package.name)
                {
                    format!("={}", self.version)
                } else {
                    self.version.to_string()
                };

                for dependency in package.dependencies.iter().filter(|d| d.name == self.name) {
                    let kind = match dependency.kind {
                        Some(DependencyKind::Dev) => "dev-dependencies",
                        Some(DependencyKind::Build) => "build-dependencies",
                        None => "dependencies",
                    };

                    document[kind][&self.name]["version"] = value(version.as_str());
                }

                write_file(&package.manifest_path, document.to_string())?;
            }
        }

        Ok(())
    }

    /// Get the changes for the version.
    ///
    /// If `update` is `true`, update the changelog for the release of the given version.
    pub fn changes(&self, update: bool) -> Result<String> {
        let mut changelog_path = self.manifest_path.clone();
        changelog_path.set_file_name("CHANGELOG.md");

        let changelog = read_file(&changelog_path)?;
        let version = Version {
            pre: semver::Prerelease::EMPTY,
            build: semver::BuildMetadata::EMPTY,
            ..self.version.clone()
        };

        let update = if !changelog.starts_with(&format!("# {}\n", version)) {
            false
        } else if changelog.starts_with(&format!("# {} (unreleased)\n", version))
            || !changelog.starts_with("# [unreleased]\n")
        {
            update
        } else {
            return Err("Could not find version title in changelog".into());
        };

        let changes_start = match changelog.find('\n') {
            Some(p) => p + 1,
            None => {
                return Err("Could not find end of version title in changelog".into());
            }
        };

        let changes_end = match changelog[changes_start..].find("\n# ") {
            Some(p) => changes_start + p,
            None => changelog.len(),
        };

        let changes = match changelog[changes_start..changes_end].trim() {
            s if s.is_empty() => "No changes for this version",
            s => s,
        };

        if update {
            let changelog = format!(
                "# [unreleased]\n\n# {}\n\n{}\n{}",
                self.version,
                changes,
                &changelog[changes_end..]
            );

            write_file(&changelog_path, changelog)?;
        }

        Ok(changes.to_owned())
    }

    /// Check if the current version of the crate is published on crates.io.
    pub fn is_published(&self, client: &HttpClient) -> Result<bool> {
        let response: CratesIoCrate =
            client.get(format!("{}/{}/{}", CRATESIO_API, self.name, self.version))?.json()?;

        Ok(response.version.is_some())
    }

    /// Publish this package on crates.io.
    pub fn publish(&self, client: &HttpClient, dry_run: bool) -> Result<bool> {
        println!("Publishing {} {} on crates.io…", self.name, self.version);
        let _dir = pushd(&self.manifest_path.parent().unwrap())?;

        if self.is_published(client)? {
            if ask_yes_no("This version is already published. Skip this step and continue?")? {
                Ok(false)
            } else {
                Err("Release interrupted by user.".into())
            }
        } else {
            if !dry_run {
                cmd!("cargo publish").run()?;
            }
            Ok(true)
        }
    }
}

/// A cargo package dependency.
#[derive(Clone, Debug, Deserialize)]
pub struct Dependency {
    /// The package name.
    pub name: String,

    /// The kind of the dependency.
    pub kind: Option<DependencyKind>,
}

/// The kind of a cargo package dependency.
#[derive(Clone, Debug, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum DependencyKind {
    /// A dev dependency.
    Dev,

    /// A build dependency.
    Build,
}

/// A crate from the `GET /crates/{crate}` endpoint of crates.io.
#[derive(Deserialize)]
struct CratesIoCrate {
    version: Option<IgnoredAny>,
}
<|endoftext|>