<commit_before>use cell_gc::{GcHeapSession, GcLeaf};
use cell_gc::collections::VecRef;
use compile;
use std::borrow::Borrow;
use std::fmt;
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
use std::collections::HashSet;
use vm::{EnvironmentRef, Trampoline};

#[derive(Debug, IntoHeap)]
pub struct Pair<'h> {
    pub car: Value<'h>,
    pub cdr: Value<'h>,
}

#[derive(Clone, Debug, PartialEq, IntoHeap)]
pub enum Value<'h> {
    Nil,
    Bool(bool),
    Int(i32),
    Char(char),
    Symbol(GcLeaf<InternedString>),
    ImmString(GcLeaf<InternedString>),
    Lambda(PairRef<'h>),
    Code(compile::CodeRef<'h>),
    Builtin(GcLeaf<BuiltinFnPtr>),
    Cons(PairRef<'h>),
    Vector(VecRef<'h, Value<'h>>),
    Environment(EnvironmentRef<'h>),
}

pub use self::Value::*;

pub type BuiltinFn =
    for<'b> fn(&mut GcHeapSession<'b>, Vec<Value<'b>>)
        -> Result<Trampoline<'b>, String>;

pub struct BuiltinFnPtr(pub BuiltinFn);

// This can't be #[derive]d because function pointers aren't Clone.
// But they are Copy. A very weird thing about Rust.
impl Clone for BuiltinFnPtr {
    fn clone(&self) -> BuiltinFnPtr {
        BuiltinFnPtr(self.0)
    }
}

impl PartialEq for BuiltinFnPtr {
    fn eq(&self, other: &BuiltinFnPtr) -> bool {
        self.0 as usize == other.0 as usize
    }
}

impl fmt::Debug for BuiltinFnPtr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "BuiltinFn({:p})", self.0 as usize as *mut ())?;
        Ok(())
    }
}

impl<'h> fmt::Display for Value<'h> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Note that this will need to add a set of already-printed pairs if we add
        // `set-car!` and/or `set-cdr!` and introduce the possibility of cycles.
        match *self {
            Nil => write!(f, "nil"),
            Bool(true) => write!(f, "#t"),
            Bool(false) => write!(f, "#f"),
            Char(c) => write!(f, "#\\{}", c),
            Int(n) => write!(f, "{}", n),
            Symbol(ref s) => write!(f, "{}", s.as_str()),
            ImmString(ref s) => write!(f, "{:?}", s.as_str()),
            Lambda(_) => write!(f, "#lambda"),
            Code(_) => write!(f, "#code"),
            Builtin(_) => write!(f, "#builtin"),
            Cons(ref p) => {
                write!(f, "(")?;
                write_pair(f, p.clone())?;
                write!(f, ")")
            }
            Vector(ref v) => {
                write!(f, "#(")?;
                for i in 0..v.len() {
                    if i != 0 {
                        write!(f, " ")?;
                    }
                    write!(f, "{}", v.get(i))?;
                }
                write!(f, ")")
            }
            Environment(_) => write!(f, "#environment"),
        }
    }
}

fn write_pair<'h>(f: &mut fmt::Formatter, pair: PairRef<'h>) -> fmt::Result {
    write!(f, "{}", pair.car())?;
    match pair.cdr() {
        Nil => Ok(()),
        Cons(p) => {
            write!(f, " ")?;
            write_pair(f, p)
        }
        otherwise => {
            write!(f, " . ")?;
            write!(f, "{}", otherwise)
        }
    }
}

impl<'h> Value<'h> {
    pub fn is_nil(&self) -> bool {
        match *self {
            Nil => true,
            _ => false,
        }
    }

    pub fn is_boolean(&self) -> bool {
        match *self {
            Bool(_) => true,
            _ => false,
        }
    }

    /// True unless this value is `#f`. Conditional expressions (`if`, `cond`,
    /// etc.) should use this to check whether a value is a "true value".
    pub fn to_bool(&self) -> bool {
        match *self {
            Bool(false) => false,
            _ => true,
        }
    }

    pub fn as_int(self, error_msg: &str) -> Result<i32, String> {
        match self {
            Int(i) => Ok(i),
            _ => Err(format!("{}: number required", error_msg)),
        }
    }

    pub fn as_index(self, error_msg: &str) -> Result<usize, String> {
        match self {
            Int(i) => {
                if i >= 0 {
                    Ok(i as usize)
                } else {
                    Err(format!("{}: negative vector index", error_msg))
                }
            }
            _ => Err(format!("{}: vector index required", error_msg)),
        }
    }

    pub fn is_pair(&self) -> bool {
        match *self {
            Cons(_) => true,
            _ => false,
        }
    }

    pub fn as_pair(self, msg: &'static str) -> Result<(Value<'h>, Value<'h>), String> {
        match self {
            Cons(r) => Ok((r.car(), r.cdr())),
            _ => Err(msg.to_string()),
        }
    }

    pub fn is_vector(&self) -> bool {
        match *self {
            Vector(_) => true,
            _ => false,
        }
    }

    pub fn as_vector(self, error_msg: &str) -> Result<VecRef<'h, Value<'h>>, String> {
        match self {
            Vector(v) => Ok(v),
            _ => Err(format!("{}: vector expected", error_msg)),
        }
    }

    pub fn is_symbol(&self) -> bool {
        match *self {
            Symbol(_) => true,
            _ => false
        }
    }

    pub fn as_symbol(self, error_msg: &str) -> Result<InternedString, String> {
        match self {
            Symbol(s) => Ok(s.unwrap()),
            _ => Err(error_msg.to_string()),
        }
    }

    pub fn is_string(&self) -> bool {
        match *self {
            ImmString(_) => true,
            _ => false
        }
    }

    pub fn as_string(self, error_msg: &str) -> Result<InternedString, String> {
        match self {
            ImmString(s) => Ok(s.unwrap()),
            _ => Err(error_msg.to_string()),
        }
    }

    pub fn is_procedure(&self) -> bool {
        match *self {
            Lambda(_) => true,
            Builtin(_) => true,
            _ => false,
        }
    }

    pub fn as_environment(self, error_msg: &str) -> Result<EnvironmentRef<'h>, String> {
        match self {
            Environment(env) => Ok(env),
            _ => Err(error_msg.to_string())
        }
    }
}

impl<'h> Iterator for Value<'h> {
    type Item = Result<Value<'h>, String>;

    fn next(&mut self) -> Option<Self::Item> {
        let (car, cdr) = match *self {
            Nil => return None,
            Cons(ref pair) => (pair.car(), pair.cdr()),
            _ => return Some(Err("improper list".into())),
        };
        *self = cdr;
        Some(Ok(car))
    }
}

#[derive(Clone, Debug)]
pub struct InternedString(Arc<String>);

// Note: If we ever impl Hash for InternedString, it will be better to use a
// custom pointer-based implementation than to use derive(Hash), which would
// hash the contents of the string.
impl PartialEq for InternedString {
    fn eq(&self, other: &InternedString) -> bool {
        Arc::ptr_eq(&self.0, &other.0)
    }
}

impl Eq for InternedString {}

lazy_static! {
    static ref STRINGS: Mutex<HashSet<InternedStringByValue>> = Mutex::new(HashSet::new());
}

static GENSYM_COUNT: AtomicUsize = ATOMIC_USIZE_INIT;

#[derive(Eq, Hash, PartialEq)]
struct InternedStringByValue(Arc<String>);

impl Borrow<str> for InternedStringByValue {
    fn borrow(&self) -> &str {
        &self.0
    }
}

impl InternedString {
    /// Return an InternedString that is not interned.
    pub fn gensym() -> InternedString {
        let n = GENSYM_COUNT.fetch_add(1, Ordering::SeqCst);
        InternedString(Arc::new(format!("#<gensym{}>", n)))
    }

    pub fn get(s: &str) -> InternedString {
        let mut guard = STRINGS.lock().unwrap();
        if let Some(x) = guard.get(s) {
            return InternedString(x.0.clone());
        }
        let s = Arc::new(s.to_string());
        guard.insert(InternedStringByValue(s.clone()));
        InternedString(s)
    }

    pub fn really_intern(self) -> InternedString {
        if !self.0.starts_with("#<gensym") {
            return self;
        }
        let mut guard = STRINGS.lock().unwrap();
        {
            let s: &str = &self.0;
            match guard.get(s) {
                Some(interned) => return InternedString(interned.0.clone()),
                None => {}
            }
        }

        // Don't cause other references to this string to become interned!
        let new_arc = {
            let s: &str = &self.0;
            Arc::new(s.to_string())
        };
        guard.insert(InternedStringByValue(new_arc));
        self
    }

    pub fn as_str(&self) -> &str {
        &self.0
    }

    pub fn is_interned(&self) -> bool {
        let guard = STRINGS.lock().unwrap();
        let s: &str = &self.0;
        match guard.get(s) {
            None => false,
            Some(interned) => Arc::ptr_eq(&interned.0, &self.0)
        }
    }

    pub fn is_gensym(&self) -> bool { !self.is_interned() }
}
<commit_msg>lisp: Macro for Value predicates based on pattern-matching.<commit_after>use cell_gc::{GcHeapSession, GcLeaf};
use cell_gc::collections::VecRef;
use compile;
use std::borrow::Borrow;
use std::fmt;
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
use std::collections::HashSet;
use vm::{EnvironmentRef, Trampoline};

#[derive(Debug, IntoHeap)]
pub struct Pair<'h> {
    pub car: Value<'h>,
    pub cdr: Value<'h>,
}

#[derive(Clone, Debug, PartialEq, IntoHeap)]
pub enum Value<'h> {
    Nil,
    Bool(bool),
    Int(i32),
    Char(char),
    Symbol(GcLeaf<InternedString>),
    ImmString(GcLeaf<InternedString>),
    Lambda(PairRef<'h>),
    Code(compile::CodeRef<'h>),
    Builtin(GcLeaf<BuiltinFnPtr>),
    Cons(PairRef<'h>),
    Vector(VecRef<'h, Value<'h>>),
    Environment(EnvironmentRef<'h>),
}

pub use self::Value::*;

pub type BuiltinFn =
    for<'b> fn(&mut GcHeapSession<'b>, Vec<Value<'b>>)
        -> Result<Trampoline<'b>, String>;

pub struct BuiltinFnPtr(pub BuiltinFn);

// This can't be #[derive]d because function pointers aren't Clone.
// But they are Copy. A very weird thing about Rust.
impl Clone for BuiltinFnPtr {
    fn clone(&self) -> BuiltinFnPtr {
        BuiltinFnPtr(self.0)
    }
}

impl PartialEq for BuiltinFnPtr {
    fn eq(&self, other: &BuiltinFnPtr) -> bool {
        self.0 as usize == other.0 as usize
    }
}

impl fmt::Debug for BuiltinFnPtr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "BuiltinFn({:p})", self.0 as usize as *mut ())?;
        Ok(())
    }
}

impl<'h> fmt::Display for Value<'h> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Note that this will need to add a set of already-printed pairs if we add
        // `set-car!` and/or `set-cdr!` and introduce the possibility of cycles.
        match *self {
            Nil => write!(f, "nil"),
            Bool(true) => write!(f, "#t"),
            Bool(false) => write!(f, "#f"),
            Char(c) => write!(f, "#\\{}", c),
            Int(n) => write!(f, "{}", n),
            Symbol(ref s) => write!(f, "{}", s.as_str()),
            ImmString(ref s) => write!(f, "{:?}", s.as_str()),
            Lambda(_) => write!(f, "#lambda"),
            Code(_) => write!(f, "#code"),
            Builtin(_) => write!(f, "#builtin"),
            Cons(ref p) => {
                write!(f, "(")?;
                write_pair(f, p.clone())?;
                write!(f, ")")
            }
            Vector(ref v) => {
                write!(f, "#(")?;
                for i in 0..v.len() {
                    if i != 0 {
                        write!(f, " ")?;
                    }
                    write!(f, "{}", v.get(i))?;
                }
                write!(f, ")")
            }
            Environment(_) => write!(f, "#environment"),
        }
    }
}

fn write_pair<'h>(f: &mut fmt::Formatter, pair: PairRef<'h>) -> fmt::Result {
    write!(f, "{}", pair.car())?;
    match pair.cdr() {
        Nil => Ok(()),
        Cons(p) => {
            write!(f, " ")?;
            write_pair(f, p)
        }
        otherwise => {
            write!(f, " . ")?;
            write!(f, "{}", otherwise)
        }
    }
}

macro_rules! pattern_predicate {
    { $method:ident, $pat:pat } => {
        pub fn $method(&self) -> bool {
            match *self {
                $pat => true,
                _ => false
            }
        }
    }
}

impl<'h> Value<'h> {
    pattern_predicate!(is_nil, Nil);
    pattern_predicate!(is_boolean, Bool(_));

    /// True unless this value is `#f`. Conditional expressions (`if`, `cond`,
    /// etc.) should use this to check whether a value is a "true value".
    pub fn to_bool(&self) -> bool {
        match *self {
            Bool(false) => false,
            _ => true,
        }
    }

    pub fn as_int(self, error_msg: &str) -> Result<i32, String> {
        match self {
            Int(i) => Ok(i),
            _ => Err(format!("{}: number required", error_msg)),
        }
    }

    pub fn as_index(self, error_msg: &str) -> Result<usize, String> {
        match self {
            Int(i) => {
                if i >= 0 {
                    Ok(i as usize)
                } else {
                    Err(format!("{}: negative vector index", error_msg))
                }
            }
            _ => Err(format!("{}: vector index required", error_msg)),
        }
    }

    pattern_predicate!(is_pair, Cons(_));

    pub fn as_pair(self, msg: &'static str) -> Result<(Value<'h>, Value<'h>), String> {
        match self {
            Cons(r) => Ok((r.car(), r.cdr())),
            _ => Err(msg.to_string()),
        }
    }

    pattern_predicate!(is_vector, Vector(_));

    pub fn as_vector(self, error_msg: &str) -> Result<VecRef<'h, Value<'h>>, String> {
        match self {
            Vector(v) => Ok(v),
            _ => Err(format!("{}: vector expected", error_msg)),
        }
    }

    pattern_predicate!(is_symbol, Symbol(_));

    pub fn as_symbol(self, error_msg: &str) -> Result<InternedString, String> {
        match self {
            Symbol(s) => Ok(s.unwrap()),
            _ => Err(error_msg.to_string()),
        }
    }

    pattern_predicate!(is_string, ImmString(_));

    pub fn as_string(self, error_msg: &str) -> Result<InternedString, String> {
        match self {
            ImmString(s) => Ok(s.unwrap()),
            _ => Err(error_msg.to_string()),
        }
    }

    pub fn is_procedure(&self) -> bool {
        match *self {
            Lambda(_) => true,
            Builtin(_) => true,
            _ => false,
        }
    }

    pub fn as_environment(self, error_msg: &str) -> Result<EnvironmentRef<'h>, String> {
        match self {
            Environment(env) => Ok(env),
            _ => Err(error_msg.to_string())
        }
    }
}

impl<'h> Iterator for Value<'h> {
    type Item = Result<Value<'h>, String>;

    fn next(&mut self) -> Option<Self::Item> {
        let (car, cdr) = match *self {
            Nil => return None,
            Cons(ref pair) => (pair.car(), pair.cdr()),
            _ => return Some(Err("improper list".into())),
        };
        *self = cdr;
        Some(Ok(car))
    }
}

#[derive(Clone, Debug)]
pub struct InternedString(Arc<String>);

// Note: If we ever impl Hash for InternedString, it will be better to use a
// custom pointer-based implementation than to use derive(Hash), which would
// hash the contents of the string.
impl PartialEq for InternedString {
    fn eq(&self, other: &InternedString) -> bool {
        Arc::ptr_eq(&self.0, &other.0)
    }
}

impl Eq for InternedString {}

lazy_static! {
    static ref STRINGS: Mutex<HashSet<InternedStringByValue>> = Mutex::new(HashSet::new());
}

static GENSYM_COUNT: AtomicUsize = ATOMIC_USIZE_INIT;

#[derive(Eq, Hash, PartialEq)]
struct InternedStringByValue(Arc<String>);

impl Borrow<str> for InternedStringByValue {
    fn borrow(&self) -> &str {
        &self.0
    }
}

impl InternedString {
    /// Return an InternedString that is not interned.
    pub fn gensym() -> InternedString {
        let n = GENSYM_COUNT.fetch_add(1, Ordering::SeqCst);
        InternedString(Arc::new(format!("#<gensym{}>", n)))
    }

    pub fn get(s: &str) -> InternedString {
        let mut guard = STRINGS.lock().unwrap();
        if let Some(x) = guard.get(s) {
            return InternedString(x.0.clone());
        }
        let s = Arc::new(s.to_string());
        guard.insert(InternedStringByValue(s.clone()));
        InternedString(s)
    }

    pub fn really_intern(self) -> InternedString {
        if !self.0.starts_with("#<gensym") {
            return self;
        }
        let mut guard = STRINGS.lock().unwrap();
        {
            let s: &str = &self.0;
            match guard.get(s) {
                Some(interned) => return InternedString(interned.0.clone()),
                None => {}
            }
        }

        // Don't cause other references to this string to become interned!
        let new_arc = {
            let s: &str = &self.0;
            Arc::new(s.to_string())
        };
        guard.insert(InternedStringByValue(new_arc));
        self
    }

    pub fn as_str(&self) -> &str {
        &self.0
    }

    pub fn is_interned(&self) -> bool {
        let guard = STRINGS.lock().unwrap();
        let s: &str = &self.0;
        match guard.get(s) {
            None => false,
            Some(interned) => Arc::ptr_eq(&interned.0, &self.0)
        }
    }

    pub fn is_gensym(&self) -> bool { !self.is_interned() }
}
<|endoftext|>


<commit_before>use rocket::{self, LoggingLevel};
use rocket_contrib::JSON;
use rocket::response::{Response, Responder};
use rocket::http::Status;
use rocket::config::{Environment, Config};
use adapters::json;
use entities::*;
use business::db::Db;
use business::error::{Error, RepoError};
use infrastructure::error::AppError;
use serde_json::ser::to_string;
use business::sort::SortByDistanceTo;
use business::{usecase, filter, geo};
use business::filter::InBBox;
use business::duplicates::{self, DuplicateType};
use std::{env,result,io};

static MAX_INVISIBLE_RESULTS: usize = 5;
static DB_URL_KEY: &'static str = "OFDB_DATABASE_URL";

#[cfg(not(test))]
mod neo4j;
#[cfg(test)]
mod mockdb;
#[cfg(test)]
mod tests;

#[cfg(not(test))]
fn db() -> io::Result<neo4j::DB> { neo4j::db() }
#[cfg(test)]
fn db() -> io::Result<mockdb::DB> { mockdb::db() }

type Result<T> = result::Result<JSON<T>, AppError>;

fn extract_ids(s: &str) -> Vec<String> {
    s.split(',')
        .map(|x| x.to_owned())
        .filter(|id| id != "")
        .collect::<Vec<String>>()
}

#[test]
fn extract_ids_test() {
    assert_eq!(extract_ids("abc"), vec!["abc"]);
    assert_eq!(extract_ids("a,b,c"), vec!["a", "b", "c"]);
    assert_eq!(extract_ids("").len(), 0);
    assert_eq!(extract_ids("abc,,d"), vec!["abc", "d"]);
}

#[get("/entries")]
fn get_entries() -> Result<Vec<json::Entry>> {
    let e = db()?
        .conn()
        .all_entries()?
        .into_iter()
        //TODO
        .map(|e|json::Entry::from_entry_with_tags(e,vec![]))
        .collect::<Vec<json::Entry>>();
    Ok(JSON(e))
}

#[get("/entries/<ids>")]
fn get_entry(ids: &str) -> Result<Vec<json::Entry>> {
    let ids = extract_ids(ids);
    let entries = usecase::get_entries(db()?.conn(), &ids)?;
    let tags = usecase::get_tags_by_entry_ids(db()?.conn(), &ids)?;
    Ok(JSON(entries
        .into_iter()
        .map(|e|{
            let t = tags.get(&e.id).cloned().unwrap_or_else(|| vec![]);
            json::Entry::from_entry_with_tags(e,t)
        })
        .collect::<Vec<json::Entry>>()))
}

#[get("/duplicates")]
fn get_duplicates() -> Result<Vec<(String, String, DuplicateType)>> {
    let entries = db()?.conn().all_entries()?;
    let ids = duplicates::find_duplicates(&entries);
    Ok(JSON(ids))
}

#[post("/entries", format = "application/json", data = "<e>")]
fn post_entry(e: JSON<usecase::NewEntry>) -> result::Result<String,AppError> {
    let id = usecase::create_new_entry(db()?.conn(), e.into_inner())?;
    Ok(id)
}

#[put("/entries/<id>", format = "application/json", data = "<e>")]
fn put_entry(id: &str, e: JSON<usecase::UpdateEntry>) -> Result<String> {
    usecase::update_entry(db()?.conn(), e.into_inner())?;
    Ok(JSON(id.to_string()))
}

#[get("/categories")]
fn get_categories() -> Result<Vec<json::Category>> {
    let categories = db()?.conn().all_categories()?;
    Ok(JSON(categories
        .into_iter()
        .map(json::Category::from)
        .collect::<Vec<json::Category>>()))
}

#[get("/categories/<id>")]
fn get_category(id: &str) -> Result<String> {
    let ids = extract_ids(id);
    let categories = db()?.conn().all_categories()?;
    let res = match ids.len() {
        0 => {
            to_string(&categories.into_iter()
                .map(json::Category::from)
                .collect::<Vec<json::Category>>())
        }
        1 => {
            let id = ids[0].clone();
            let e = categories.into_iter()
                .find(|x| x.id == id)
                .ok_or(RepoError::NotFound)?;
            to_string(&json::Category::from(e))
        }
        _ => {
            to_string(&categories.into_iter()
                .filter(|e| ids.iter().any(|id| e.id == id.clone()))
                .map(json::Category::from)
                .collect::<Vec<json::Category>>())
        }
    }?;
    Ok(JSON(res))
}

#[derive(FromForm)]
struct SearchQuery {
    bbox: String,
    categories: String,
    text: Option<String>,
    tags: Option<String>,
}

#[get("/search?<search>")]
fn get_search(search: SearchQuery) -> Result<json::SearchResult> {
    let entries: Vec<Entry> = db()?.conn().all_entries()?;
    let cat_ids = extract_ids(&search.categories);
    let bbox = geo::extract_bbox(&search.bbox).map_err(Error::Parameter)
        .map_err(AppError::Business)?;
    let bbox_center = geo::center(&bbox[0], &bbox[1]);

    let mut entries: Vec<_> = entries.iter()
        .filter(&*filter::entries_by_category_ids(&cat_ids))
        .collect();

    if let Some(tags_str) = search.tags {
        let tags = extract_ids(&tags_str);
        if tags.len() > 0 {
            let triple = db()?.conn().all_triples()?;
            entries = entries.into_iter()
                .filter(&*filter::entries_by_tags(
                    &tags,
                    &triple,
                    filter::Combination::Or
                ))
                .collect();
        }
    }

    let entries = match search.text {
        Some(txt) => {
            entries.into_iter().filter(&*filter::entries_by_search_text(&txt)).collect()
        }
        None => entries,
    };

    let mut entries : Vec<Entry> = entries.into_iter().cloned().collect();
    entries.sort_by_distance_to(&bbox_center);

    let visible_results: Vec<_> =
        entries
            .iter()
            .filter(|x| x.in_bbox(&bbox))
            .map(|x| &x.id)
            .cloned()
            .collect();

    let invisible_results =
        entries
            .iter()
            .filter(|e| !visible_results.iter().any(|v| *v == e.id))
            .take(MAX_INVISIBLE_RESULTS)
            .map(|x| &x.id)
            .cloned()
            .collect::<Vec<_>>();

    Ok(JSON(json::SearchResult {
        visible: visible_results,
        invisible: invisible_results,
    }))
}

#[get("/count/entries")]
fn get_count_entries() -> Result<String> {
    let entries = db()?.conn().all_entries()?;
    Ok(JSON(entries.len().to_string()))
}

#[get("/server/version")]
fn get_version() -> &'static str {
    env!("CARGO_PKG_VERSION")
}

pub fn run(db_url: &str, port: u16, enable_cors: bool) {

    env::set_var(DB_URL_KEY, db_url);

    if enable_cors {
        panic!("This feature is currently not available until\
        \nhttps://github.com/SergioBenitez/Rocket/pull/141\nis merged :(");
    }

    let cfg = Config::build(Environment::Production)
        .log_level(LoggingLevel::Normal)
        .address("127.0.0.1")
        .port(port)
        .finalize()
        .unwrap();

    rocket::custom(cfg,true)
        .mount("/",
               routes![get_entries,
                       get_entry,
                       post_entry,
                       put_entry,
                       get_categories,
                       get_category,
                       get_search,
                       get_duplicates,
                       get_count_entries,
                       get_version])
        .launch();
}

impl<'r> Responder<'r> for AppError {
    fn respond(self) -> result::Result<Response<'r>, Status> {
        Err(match self {
            AppError::Business(ref err) => {
                match *err {
                    Error::Parameter(_) => Status::BadRequest,
                    Error::Repo(ref err) => {
                        match *err {
                            RepoError::NotFound => Status::NotFound,
                            _ => Status::InternalServerError,
                        }
                    }
                }
            }
            AppError::Adapter(_) => Status::BadRequest,

            _ => Status::InternalServerError,
        })
    }
}
<commit_msg>new(web): add /count/tags<commit_after>use rocket::{self, LoggingLevel};
use rocket_contrib::JSON;
use rocket::response::{Response, Responder};
use rocket::http::Status;
use rocket::config::{Environment, Config};
use adapters::json;
use entities::*;
use business::db::Db;
use business::error::{Error, RepoError};
use infrastructure::error::AppError;
use serde_json::ser::to_string;
use business::sort::SortByDistanceTo;
use business::{usecase, filter, geo};
use business::filter::InBBox;
use business::duplicates::{self, DuplicateType};
use std::{env,result,io};

static MAX_INVISIBLE_RESULTS: usize = 5;
static DB_URL_KEY: &'static str = "OFDB_DATABASE_URL";

#[cfg(not(test))]
mod neo4j;
#[cfg(test)]
mod mockdb;
#[cfg(test)]
mod tests;

#[cfg(not(test))]
fn db() -> io::Result<neo4j::DB> { neo4j::db() }
#[cfg(test)]
fn db() -> io::Result<mockdb::DB> { mockdb::db() }

type Result<T> = result::Result<JSON<T>, AppError>;

fn extract_ids(s: &str) -> Vec<String> {
    s.split(',')
        .map(|x| x.to_owned())
        .filter(|id| id != "")
        .collect::<Vec<String>>()
}

#[test]
fn extract_ids_test() {
    assert_eq!(extract_ids("abc"), vec!["abc"]);
    assert_eq!(extract_ids("a,b,c"), vec!["a", "b", "c"]);
    assert_eq!(extract_ids("").len(), 0);
    assert_eq!(extract_ids("abc,,d"), vec!["abc", "d"]);
}

#[get("/entries")]
fn get_entries() -> Result<Vec<json::Entry>> {
    let e = db()?
        .conn()
        .all_entries()?
        .into_iter()
        //TODO
        .map(|e|json::Entry::from_entry_with_tags(e,vec![]))
        .collect::<Vec<json::Entry>>();
    Ok(JSON(e))
}

#[get("/entries/<ids>")]
fn get_entry(ids: &str) -> Result<Vec<json::Entry>> {
    let ids = extract_ids(ids);
    let entries = usecase::get_entries(db()?.conn(), &ids)?;
    let tags = usecase::get_tags_by_entry_ids(db()?.conn(), &ids)?;
    Ok(JSON(entries
        .into_iter()
        .map(|e|{
            let t = tags.get(&e.id).cloned().unwrap_or_else(|| vec![]);
            json::Entry::from_entry_with_tags(e,t)
        })
        .collect::<Vec<json::Entry>>()))
}

#[get("/duplicates")]
fn get_duplicates() -> Result<Vec<(String, String, DuplicateType)>> {
    let entries = db()?.conn().all_entries()?;
    let ids = duplicates::find_duplicates(&entries);
    Ok(JSON(ids))
}

#[post("/entries", format = "application/json", data = "<e>")]
fn post_entry(e: JSON<usecase::NewEntry>) -> result::Result<String,AppError> {
    let id = usecase::create_new_entry(db()?.conn(), e.into_inner())?;
    Ok(id)
}

#[put("/entries/<id>", format = "application/json", data = "<e>")]
fn put_entry(id: &str, e: JSON<usecase::UpdateEntry>) -> Result<String> {
    usecase::update_entry(db()?.conn(), e.into_inner())?;
    Ok(JSON(id.to_string()))
}

#[get("/categories")]
fn get_categories() -> Result<Vec<json::Category>> {
    let categories = db()?.conn().all_categories()?;
    Ok(JSON(categories
        .into_iter()
        .map(json::Category::from)
        .collect::<Vec<json::Category>>()))
}

#[get("/categories/<id>")]
fn get_category(id: &str) -> Result<String> {
    let ids = extract_ids(id);
    let categories = db()?.conn().all_categories()?;
    let res = match ids.len() {
        0 => {
            to_string(&categories.into_iter()
                .map(json::Category::from)
                .collect::<Vec<json::Category>>())
        }
        1 => {
            let id = ids[0].clone();
            let e = categories.into_iter()
                .find(|x| x.id == id)
                .ok_or(RepoError::NotFound)?;
            to_string(&json::Category::from(e))
        }
        _ => {
            to_string(&categories.into_iter()
                .filter(|e| ids.iter().any(|id| e.id == id.clone()))
                .map(json::Category::from)
                .collect::<Vec<json::Category>>())
        }
    }?;
    Ok(JSON(res))
}

#[derive(FromForm)]
struct SearchQuery {
    bbox: String,
    categories: String,
    text: Option<String>,
    tags: Option<String>,
}

#[get("/search?<search>")]
fn get_search(search: SearchQuery) -> Result<json::SearchResult> {
    let entries: Vec<Entry> = db()?.conn().all_entries()?;
    let cat_ids = extract_ids(&search.categories);
    let bbox = geo::extract_bbox(&search.bbox).map_err(Error::Parameter)
        .map_err(AppError::Business)?;
    let bbox_center = geo::center(&bbox[0], &bbox[1]);

    let mut entries: Vec<_> = entries.iter()
        .filter(&*filter::entries_by_category_ids(&cat_ids))
        .collect();

    if let Some(tags_str) = search.tags {
        let tags = extract_ids(&tags_str);
        if tags.len() > 0 {
            let triple = db()?.conn().all_triples()?;
            entries = entries.into_iter()
                .filter(&*filter::entries_by_tags(
                    &tags,
                    &triple,
                    filter::Combination::Or
                ))
                .collect();
        }
    }

    let entries = match search.text {
        Some(txt) => {
            entries.into_iter().filter(&*filter::entries_by_search_text(&txt)).collect()
        }
        None => entries,
    };

    let mut entries : Vec<Entry> = entries.into_iter().cloned().collect();
    entries.sort_by_distance_to(&bbox_center);

    let visible_results: Vec<_> =
        entries
            .iter()
            .filter(|x| x.in_bbox(&bbox))
            .map(|x| &x.id)
            .cloned()
            .collect();

    let invisible_results =
        entries
            .iter()
            .filter(|e| !visible_results.iter().any(|v| *v == e.id))
            .take(MAX_INVISIBLE_RESULTS)
            .map(|x| &x.id)
            .cloned()
            .collect::<Vec<_>>();

    Ok(JSON(json::SearchResult {
        visible: visible_results,
        invisible: invisible_results,
    }))
}

#[get("/count/entries")]
fn get_count_entries() -> Result<usize> {
    let entries = db()?.conn().all_entries()?;
    Ok(JSON(entries.len()))
}

#[get("/count/tags")]
fn get_count_tags() -> Result<usize> {
    let mut tags : Vec<String>= db()?.conn()
        .all_triples()?
        .into_iter()
        .filter_map(|t| match t.object {
            ObjectId::Tag(id) => Some(id),
            _=> None
        })
        .collect();
    tags.dedup();
    Ok(JSON(tags.len()))
}

#[get("/server/version")]
fn get_version() -> &'static str {
    env!("CARGO_PKG_VERSION")
}

pub fn run(db_url: &str, port: u16, enable_cors: bool) {

    env::set_var(DB_URL_KEY, db_url);

    if enable_cors {
        panic!("This feature is currently not available until\
        \nhttps://github.com/SergioBenitez/Rocket/pull/141\nis merged :(");
    }

    let cfg = Config::build(Environment::Production)
        .log_level(LoggingLevel::Normal)
        .address("127.0.0.1")
        .port(port)
        .finalize()
        .unwrap();

    rocket::custom(cfg,true)
        .mount("/",
               routes![get_entries,
                       get_entry,
                       post_entry,
                       put_entry,
                       get_categories,
                       get_category,
                       get_search,
                       get_duplicates,
                       get_count_entries,
                       get_count_tags,
                       get_version])
        .launch();
}

impl<'r> Responder<'r> for AppError {
    fn respond(self) -> result::Result<Response<'r>, Status> {
        Err(match self {
            AppError::Business(ref err) => {
                match *err {
                    Error::Parameter(_) => Status::BadRequest,
                    Error::Repo(ref err) => {
                        match *err {
                            RepoError::NotFound => Status::NotFound,
                            _ => Status::InternalServerError,
                        }
                    }
                }
            }
            AppError::Adapter(_) => Status::BadRequest,

            _ => Status::InternalServerError,
        })
    }
}
<|endoftext|>


<commit_before>use std::fmt::Display;
use serde::Serialize;
use request::Request;
use method::{Get, List, Create, Delete, Update};
use STATIC_URL_ERROR;
use url::Url;
use super::domain::Domain;
use super::{ApiLinks, ApiMeta};
use super::{HasValue, HasPagination, HasResponse};
use request::{DomainRequest, DomainRecordRequest};

const DOMAIN_RECORDS_SEGMENT: &'static str = "records";

/// Domain record resources are used to set or retrieve information about the
/// individual DNS records configured for a domain. This allows you to build
/// and manage DNS zone files by adding and modifying individual records for a
/// domain.
///
/// Requests with this output this type are accessed via [`Domain::get(..).records()`](../request/struct.Request.html#method.records).
/// Make sure to check the builders for [`Create`](../request/struct.Request.html#method.priority)
/// and [`Update`](../request/struct.Request.html#method.kind).
///
/// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#domain-records)
#[derive(Deserialize, Serialize, Debug, Clone, Getters, Setters)]
pub struct DomainRecord {
    /// A unique identifier for each domain record.
    #[get = "pub"]
    id: usize,
    /// The type of the DNS record (ex: A, CNAME, TXT, ...).
    ///
    /// *Note:* Since `type` is a keyword in Rust `kind` is used instead.
    #[get = "pub"]
    #[serde(rename = "type")]
    kind: String, // 'type' is reserved in Rust.
    /// The name to use for the DNS record.
    #[get = "pub"]
    name: String,
    /// The value to use for the DNS record.
    #[get = "pub"]
    data: String,
    /// The priority for SRV and MX records.
    #[get = "pub"]
    priority: Option<usize>,
    /// The port for SRV records.
    #[get = "pub"]
    port: Option<usize>,
    /// This value is the time to live for the record, in seconds. This defines
    /// the time frame that clients can cache queried information before a refresh
    /// should be requested.
    #[get = "pub"]
    ttl: usize,
    /// The weight for SRV records.
    #[get = "pub"]
    weight: Option<usize>,
}

impl DomainRequest<Get, Domain> {
    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#list-all-domain-records)
    pub fn records(mut self) -> DomainRecordRequest<List, Vec<DomainRecord>> {
        self.url_mut()
            .path_segments_mut()
            .expect(STATIC_URL_ERROR)
            .push(DOMAIN_RECORDS_SEGMENT);

        self.transmute()
    }
}

impl DomainRecordRequest<List, Vec<DomainRecord>> {
    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-new-domain-record)
    pub fn create<S>(mut self,
                     kind: S,
                     name: S,
                     data: S)
                     -> DomainRecordRequest<Create, DomainRecord>
        where S: AsRef<str> + Display + Serialize
    {
        self.url_mut().path_segments_mut().expect(STATIC_URL_ERROR);

        self.set_body(json!({
            "type": kind,
            "name": name,
            "data": data,
        }));

        self.transmute()
    }

    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#retrieve-an-existing-domain-record)
    pub fn get(mut self, id: usize) -> DomainRecordRequest<Get, DomainRecord> {
        self.url_mut()
            .path_segments_mut()
            .expect(STATIC_URL_ERROR)
            .push(&id.to_string());

        self.transmute()
    }

    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#update-a-domain-record)
    pub fn update(mut self, id: usize) -> DomainRecordRequest<Update, DomainRecord> {
        self.url_mut()
            .path_segments_mut()
            .expect(STATIC_URL_ERROR)
            .push(&id.to_string());

        self.transmute()
    }

    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#delete-a-domain-record)
    pub fn delete(mut self, id: usize) -> DomainRecordRequest<Delete, ()> {
        self.url_mut()
            .path_segments_mut()
            .expect(STATIC_URL_ERROR)
            .push(&id.to_string());

        self.transmute()
    }
}

impl DomainRecordRequest<Create, DomainRecord> {
    /// The priority for SRV and MX records.
    ///
    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#domain-records)
    pub fn priority(mut self, val: Option<usize>) -> Self {
        self.body_mut()["priority"] = json!(val);
        self
    }
    /// The port for SRV records.
    ///
    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#domain-records)
    pub fn port(mut self, val: Option<usize>) -> Self {
        self.body_mut()["port"] = json!(val);
        self
    }
    /// This value is the time to live for the record, in seconds. This defines
    /// the time frame that clients can cache queried information before a
    /// refresh should be requested.
    ///
    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#domain-records)
    pub fn ttl(mut self, val: usize) -> Self {
        self.body_mut()["ttl"] = json!(val);
        self
    }
    /// The weight for SRV records.
    ///
    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#domain-records)
    pub fn weight(mut self, val: Option<usize>) -> Self {
        self.body_mut()["weight"] = json!(val);
        self
    }
}

impl DomainRecordRequest<Update, DomainRecord> {
    /// The record type (A, MX, CNAME, etc).
    ///
    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#domain-records)
    pub fn kind<S>(mut self, val: S) -> Self
        where S: AsRef<str> + Display + Serialize
    {
        self.body_mut()["type"] = json!(val);
        self
    }
    /// The host name, alias, or service being defined by the record.
    ///
    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#domain-records)
    pub fn name<S>(mut self, val: S) -> Self
        where S: AsRef<str> + Display + Serialize
    {
        self.body_mut()["name"] = json!(val);
        self
    }
    /// Variable data depending on record type. See the Domain Records section
    /// for more detail on each record type.
    ///
    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#domain-records)
    pub fn data<S>(mut self, val: S) -> Self
        where S: AsRef<str> + Display + Serialize
    {
        self.body_mut()["data"] = json!(val);
        self
    }
    /// The priority for SRV and MX records.
    ///
    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#domain-records)
    pub fn priority(mut self, val: Option<usize>) -> Self {
        self.body_mut()["priority"] = json!(val);
        self
    }
    /// The port for SRV records.
    ///
    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#domain-records)
    pub fn port(mut self, val: Option<usize>) -> Self {
        self.body_mut()["port"] = json!(val);
        self
    }
    /// This value is the time to live for the record, in seconds. This defines
    /// the time frame that clients can cache queried information before a
    /// refresh should be requested.
    ///
    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#domain-records)
    pub fn ttl(mut self, val: usize) -> Self {
        self.body_mut()["ttl"] = json!(val);
        self
    }
    /// The weight for SRV records.
    ///
    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#domain-records)
    pub fn weight(mut self, val: Option<usize>) -> Self {
        self.body_mut()["weight"] = json!(val);
        self
    }
}

/// Response type returned from Digital Ocean.
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct DomainRecordResponse {
    domain_record: DomainRecord,
}

impl HasValue for DomainRecordResponse {
    type Value = DomainRecord;
    fn value(self) -> DomainRecord {
        self.domain_record
    }
}

impl HasResponse for DomainRecord {
    type Response = DomainRecordResponse;
}

/// Response type returned from Digital Ocean.
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct DomainRecordListResponse {
    domain_records: Vec<DomainRecord>,
    links: ApiLinks,
    meta: ApiMeta,
}

impl HasResponse for Vec<DomainRecord> {
    type Response = DomainRecordListResponse;
}

impl HasPagination for DomainRecordListResponse {
    fn next_page(&self) -> Option<Url> {
        self.links.next()
    }
}

impl HasValue for DomainRecordListResponse {
    type Value = Vec<DomainRecord>;
    fn value(self) -> Vec<DomainRecord> {
        self.domain_records
    }
}
<commit_msg>Fix some links<commit_after>use std::fmt::Display;
use serde::Serialize;
use request::Request;
use method::{Get, List, Create, Delete, Update};
use STATIC_URL_ERROR;
use url::Url;
use super::domain::Domain;
use super::{ApiLinks, ApiMeta};
use super::{HasValue, HasPagination, HasResponse};
use request::{DomainRequest, DomainRecordRequest};

const DOMAIN_RECORDS_SEGMENT: &'static str = "records";

/// Domain record resources are used to set or retrieve information about the
/// individual DNS records configured for a domain. This allows you to build
/// and manage DNS zone files by adding and modifying individual records for a
/// domain.
///
/// Requests with this output this type are accessed via [`Domain::get(..).records()`](../request/type.DomainRequest.html#method.records).
/// Make sure to check the functions in [`DomainRecordRequest`](../request/type.DomainRecordRequest.html)
///
/// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#domain-records)
#[derive(Deserialize, Serialize, Debug, Clone, Getters, Setters)]
pub struct DomainRecord {
    /// A unique identifier for each domain record.
    #[get = "pub"]
    id: usize,
    /// The type of the DNS record (ex: A, CNAME, TXT, ...).
    ///
    /// *Note:* Since `type` is a keyword in Rust `kind` is used instead.
    #[get = "pub"]
    #[serde(rename = "type")]
    kind: String, // 'type' is reserved in Rust.
    /// The name to use for the DNS record.
    #[get = "pub"]
    name: String,
    /// The value to use for the DNS record.
    #[get = "pub"]
    data: String,
    /// The priority for SRV and MX records.
    #[get = "pub"]
    priority: Option<usize>,
    /// The port for SRV records.
    #[get = "pub"]
    port: Option<usize>,
    /// This value is the time to live for the record, in seconds. This defines
    /// the time frame that clients can cache queried information before a refresh
    /// should be requested.
    #[get = "pub"]
    ttl: usize,
    /// The weight for SRV records.
    #[get = "pub"]
    weight: Option<usize>,
}

impl DomainRequest<Get, Domain> {
    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#list-all-domain-records)
    pub fn records(mut self) -> DomainRecordRequest<List, Vec<DomainRecord>> {
        self.url_mut()
            .path_segments_mut()
            .expect(STATIC_URL_ERROR)
            .push(DOMAIN_RECORDS_SEGMENT);

        self.transmute()
    }
}

impl DomainRecordRequest<List, Vec<DomainRecord>> {
    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-new-domain-record)
    pub fn create<S>(mut self,
                     kind: S,
                     name: S,
                     data: S)
                     -> DomainRecordRequest<Create, DomainRecord>
        where S: AsRef<str> + Display + Serialize
    {
        self.url_mut().path_segments_mut().expect(STATIC_URL_ERROR);

        self.set_body(json!({
            "type": kind,
            "name": name,
            "data": data,
        }));

        self.transmute()
    }

    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#retrieve-an-existing-domain-record)
    pub fn get(mut self, id: usize) -> DomainRecordRequest<Get, DomainRecord> {
        self.url_mut()
            .path_segments_mut()
            .expect(STATIC_URL_ERROR)
            .push(&id.to_string());

        self.transmute()
    }

    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#update-a-domain-record)
    pub fn update(mut self, id: usize) -> DomainRecordRequest<Update, DomainRecord> {
        self.url_mut()
            .path_segments_mut()
            .expect(STATIC_URL_ERROR)
            .push(&id.to_string());

        self.transmute()
    }

    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#delete-a-domain-record)
    pub fn delete(mut self, id: usize) -> DomainRecordRequest<Delete, ()> {
        self.url_mut()
            .path_segments_mut()
            .expect(STATIC_URL_ERROR)
            .push(&id.to_string());

        self.transmute()
    }
}

impl DomainRecordRequest<Create, DomainRecord> {
    /// The priority for SRV and MX records.
    ///
    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#domain-records)
    pub fn priority(mut self, val: Option<usize>) -> Self {
        self.body_mut()["priority"] = json!(val);
        self
    }
    /// The port for SRV records.
    ///
    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#domain-records)
    pub fn port(mut self, val: Option<usize>) -> Self {
        self.body_mut()["port"] = json!(val);
        self
    }
    /// This value is the time to live for the record, in seconds. This defines
    /// the time frame that clients can cache queried information before a
    /// refresh should be requested.
    ///
    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#domain-records)
    pub fn ttl(mut self, val: usize) -> Self {
        self.body_mut()["ttl"] = json!(val);
        self
    }
    /// The weight for SRV records.
    ///
    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#domain-records)
    pub fn weight(mut self, val: Option<usize>) -> Self {
        self.body_mut()["weight"] = json!(val);
        self
    }
}

impl DomainRecordRequest<Update, DomainRecord> {
    /// The record type (A, MX, CNAME, etc).
    ///
    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#domain-records)
    pub fn kind<S>(mut self, val: S) -> Self
        where S: AsRef<str> + Display + Serialize
    {
        self.body_mut()["type"] = json!(val);
        self
    }
    /// The host name, alias, or service being defined by the record.
    ///
    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#domain-records)
    pub fn name<S>(mut self, val: S) -> Self
        where S: AsRef<str> + Display + Serialize
    {
        self.body_mut()["name"] = json!(val);
        self
    }
    /// Variable data depending on record type. See the Domain Records section
    /// for more detail on each record type.
    ///
    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#domain-records)
    pub fn data<S>(mut self, val: S) -> Self
        where S: AsRef<str> + Display + Serialize
    {
        self.body_mut()["data"] = json!(val);
        self
    }
    /// The priority for SRV and MX records.
    ///
    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#domain-records)
    pub fn priority(mut self, val: Option<usize>) -> Self {
        self.body_mut()["priority"] = json!(val);
        self
    }
    /// The port for SRV records.
    ///
    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#domain-records)
    pub fn port(mut self, val: Option<usize>) -> Self {
        self.body_mut()["port"] = json!(val);
        self
    }
    /// This value is the time to live for the record, in seconds. This defines
    /// the time frame that clients can cache queried information before a
    /// refresh should be requested.
    ///
    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#domain-records)
    pub fn ttl(mut self, val: usize) -> Self {
        self.body_mut()["ttl"] = json!(val);
        self
    }
    /// The weight for SRV records.
    ///
    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#domain-records)
    pub fn weight(mut self, val: Option<usize>) -> Self {
        self.body_mut()["weight"] = json!(val);
        self
    }
}

/// Response type returned from Digital Ocean.
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct DomainRecordResponse {
    domain_record: DomainRecord,
}

impl HasValue for DomainRecordResponse {
    type Value = DomainRecord;
    fn value(self) -> DomainRecord {
        self.domain_record
    }
}

impl HasResponse for DomainRecord {
    type Response = DomainRecordResponse;
}

/// Response type returned from Digital Ocean.
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct DomainRecordListResponse {
    domain_records: Vec<DomainRecord>,
    links: ApiLinks,
    meta: ApiMeta,
}

impl HasResponse for Vec<DomainRecord> {
    type Response = DomainRecordListResponse;
}

impl HasPagination for DomainRecordListResponse {
    fn next_page(&self) -> Option<Url> {
        self.links.next()
    }
}

impl HasValue for DomainRecordListResponse {
    type Value = Vec<DomainRecord>;
    fn value(self) -> Vec<DomainRecord> {
        self.domain_records
    }
}
<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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.

//! Parameterized string expansion

use core::prelude::*;
use core::{char, int, vec};
use core::iterator::IteratorUtil;

#[deriving(Eq)]
enum States {
    Nothing,
    Percent,
    SetVar,
    GetVar,
    PushParam,
    CharConstant,
    CharClose,
    IntConstant,
    IfCond,
    IfBody
}

/// Types of parameters a capability can use
pub enum Param {
    String(~str),
    Char(char),
    Number(int)
}

/// Container for static and dynamic variable arrays
pub struct Variables {
    /// Static variables A-Z
    sta: [Param, ..26],
    /// Dynamic variables a-z
    dyn: [Param, ..26]
}

impl Variables {
    /// Return a new zero-initialized Variables
    pub fn new() -> Variables {
        Variables{ sta: [Number(0), ..26], dyn: [Number(0), ..26] }
    }
}

/**
  Expand a parameterized capability

  # Arguments
  * `cap`    - string to expand
  * `params` - vector of params for %p1 etc
  * `vars`   - Variables struct for %Pa etc

  To be compatible with ncurses, `vars` should be the same between calls to `expand` for
  multiple capabilities for the same terminal.
  */
pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables)
    -> Result<~[u8], ~str> {
    let mut state = Nothing;
    let mut i = 0;

    // expanded cap will only rarely be larger than the cap itself
    let mut output = vec::with_capacity(cap.len());

    let mut cur;

    let mut stack: ~[Param] = ~[];

    let mut intstate = ~[];

    // Copy parameters into a local vector for mutability
    let mut mparams = [Number(0), ..9];
    for mparams.mut_iter().zip(params.iter()).advance |(dst, &src)| {
        *dst = src;
    }

    while i < cap.len() {
        cur = cap[i] as char;
        let mut old_state = state;
        match state {
            Nothing => {
                if cur == '%' {
                    state = Percent;
                } else {
                    output.push(cap[i]);
                }
            },
            Percent => {
                match cur {
                    '%' => { output.push(cap[i]); state = Nothing },
                    'c' => match stack.pop() {
                        Char(c) => output.push(c as u8),
                        _       => return Err(~"a non-char was used with %c")
                    },
                    's' => match stack.pop() {
                        String(s) => output.push_all(s.as_bytes()),
                        _         => return Err(~"a non-str was used with %s")
                    },
                    'd' => match stack.pop() {
                        Number(x) => {
                            let s = x.to_str();
                            output.push_all(s.as_bytes())
                        }
                        _         => return Err(~"a non-number was used with %d")
                    },
                    'p' => state = PushParam,
                    'P' => state = SetVar,
                    'g' => state = GetVar,
                    '\'' => state = CharConstant,
                    '{' => state = IntConstant,
                    'l' => match stack.pop() {
                        String(s) => stack.push(Number(s.len() as int)),
                        _         => return Err(~"a non-str was used with %l")
                    },
                    '+' => match (stack.pop(), stack.pop()) {
                        (Number(x), Number(y)) => stack.push(Number(x + y)),
                        (_, _) => return Err(~"non-numbers on stack with +")
                    },
                    '-' => match (stack.pop(), stack.pop()) {
                        (Number(x), Number(y)) => stack.push(Number(x - y)),
                        (_, _) => return Err(~"non-numbers on stack with -")
                    },
                    '*' => match (stack.pop(), stack.pop()) {
                        (Number(x), Number(y)) => stack.push(Number(x * y)),
                        (_, _) => return Err(~"non-numbers on stack with *")
                    },
                    '/' => match (stack.pop(), stack.pop()) {
                        (Number(x), Number(y)) => stack.push(Number(x / y)),
                        (_, _) => return Err(~"non-numbers on stack with /")
                    },
                    'm' => match (stack.pop(), stack.pop()) {
                        (Number(x), Number(y)) => stack.push(Number(x % y)),
                        (_, _) => return Err(~"non-numbers on stack with %")
                    },
                    '&' => match (stack.pop(), stack.pop()) {
                        (Number(x), Number(y)) => stack.push(Number(x & y)),
                        (_, _) => return Err(~"non-numbers on stack with &")
                    },
                    '|' => match (stack.pop(), stack.pop()) {
                        (Number(x), Number(y)) => stack.push(Number(x | y)),
                        (_, _) => return Err(~"non-numbers on stack with |")
                    },
                    'A' => match (stack.pop(), stack.pop()) {
                        (Number(0), Number(_)) => stack.push(Number(0)),
                        (Number(_), Number(0)) => stack.push(Number(0)),
                        (Number(_), Number(_)) => stack.push(Number(1)),
                        _ => return Err(~"non-numbers on stack with logical and")
                    },
                    'O' => match (stack.pop(), stack.pop()) {
                        (Number(0), Number(0)) => stack.push(Number(0)),
                        (Number(_), Number(_)) => stack.push(Number(1)),
                        _ => return Err(~"non-numbers on stack with logical or")
                    },
                    '!' => match stack.pop() {
                        Number(0) => stack.push(Number(1)),
                        Number(_) => stack.push(Number(0)),
                        _ => return Err(~"non-number on stack with logical not")
                    },
                    '~' => match stack.pop() {
                        Number(x) => stack.push(Number(!x)),
                        _         => return Err(~"non-number on stack with %~")
                    },
                    'i' => match (copy mparams[0], copy mparams[1]) {
                        (Number(ref mut x), Number(ref mut y)) => {
                            *x += 1;
                            *y += 1;
                        },
                        (_, _) => return Err(~"first two params not numbers with %i")
                    },
                    '?' => state = return Err(fmt!("if expressions unimplemented (%?)", cap)),
                    _ => return Err(fmt!("unrecognized format option %c", cur))
                }
            },
            PushParam => {
                // params are 1-indexed
                stack.push(copy mparams[char::to_digit(cur, 10).expect("bad param number") - 1]);
            },
            SetVar => {
                if cur >= 'A' && cur <= 'Z' {
                    let idx = (cur as u8) - ('A' as u8);
                    vars.sta[idx] = stack.pop();
                } else if cur >= 'a' && cur <= 'z' {
                    let idx = (cur as u8) - ('a' as u8);
                    vars.dyn[idx] = stack.pop();
                } else {
                    return Err(~"bad variable name in %P");
                }
            },
            GetVar => {
                if cur >= 'A' && cur <= 'Z' {
                    let idx = (cur as u8) - ('A' as u8);
                    stack.push(copy vars.sta[idx]);
                } else if cur >= 'a' && cur <= 'z' {
                    let idx = (cur as u8) - ('a' as u8);
                    stack.push(copy vars.dyn[idx]);
                } else {
                    return Err(~"bad variable name in %g");
                }
            },
            CharConstant => {
                stack.push(Char(cur));
                state = CharClose;
            },
            CharClose => {
                if cur != '\'' {
                    return Err(~"malformed character constant");
                }
            },
            IntConstant => {
                if cur == '}' {
                    stack.push(Number(int::parse_bytes(intstate, 10).expect("bad int constant")));
                    state = Nothing;
                }
                intstate.push(cur as u8);
                old_state = Nothing;
            }
            _ => return Err(~"unimplemented state")
        }
        if state == old_state {
            state = Nothing;
        }
        i += 1;
    }
    Ok(output)
}

#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn test_basic_setabf() {
        let s = bytes!("\\E[48;5;%p1%dm");
        assert_eq!(expand(s, [Number(1)], &mut Variables::new()).unwrap(), bytes!("\\E[48;5;1m").to_owned());
    }
}
<commit_msg>Fix a bunch of failure cases in terminfo<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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.

//! Parameterized string expansion

use core::prelude::*;
use core::{char, int, vec};
use core::iterator::IteratorUtil;

#[deriving(Eq)]
enum States {
    Nothing,
    Percent,
    SetVar,
    GetVar,
    PushParam,
    CharConstant,
    CharClose,
    IntConstant,
    IfCond,
    IfBody
}

/// Types of parameters a capability can use
pub enum Param {
    String(~str),
    Number(int)
}

/// Container for static and dynamic variable arrays
pub struct Variables {
    /// Static variables A-Z
    sta: [Param, ..26],
    /// Dynamic variables a-z
    dyn: [Param, ..26]
}

impl Variables {
    /// Return a new zero-initialized Variables
    pub fn new() -> Variables {
        Variables{ sta: [Number(0), ..26], dyn: [Number(0), ..26] }
    }
}

/**
  Expand a parameterized capability

  # Arguments
  * `cap`    - string to expand
  * `params` - vector of params for %p1 etc
  * `vars`   - Variables struct for %Pa etc

  To be compatible with ncurses, `vars` should be the same between calls to `expand` for
  multiple capabilities for the same terminal.
  */
pub fn expand(cap: &[u8], params: &[Param], vars: &mut Variables)
    -> Result<~[u8], ~str> {
    let mut state = Nothing;

    // expanded cap will only rarely be larger than the cap itself
    let mut output = vec::with_capacity(cap.len());

    let mut stack: ~[Param] = ~[];

    let mut intstate = ~[];

    // Copy parameters into a local vector for mutability
    let mut mparams = [Number(0), ..9];
    for mparams.mut_iter().zip(params.iter()).advance |(dst, &src)| {
        *dst = src;
    }

    for cap.iter().transform(|&x| x).advance |c| {
        let cur = c as char;
        let mut old_state = state;
        match state {
            Nothing => {
                if cur == '%' {
                    state = Percent;
                } else {
                    output.push(c);
                }
            },
            Percent => {
                match cur {
                    '%' => { output.push(c); state = Nothing },
                    'c' => if stack.len() > 0 {
                        match stack.pop() {
                            // if c is 0, use 0200 (128) for ncurses compatibility
                            Number(c) => output.push(if c == 0 { 128 } else { c } as u8),
                            _       => return Err(~"a non-char was used with %c")
                        }
                    } else { return Err(~"stack is empty") },
                    's' => if stack.len() > 0 {
                        match stack.pop() {
                            String(s) => output.push_all(s.as_bytes()),
                            _         => return Err(~"a non-str was used with %s")
                        }
                    } else { return Err(~"stack is empty") },
                    'd' => if stack.len() > 0 {
                        match stack.pop() {
                            Number(x) => {
                                let s = x.to_str();
                                output.push_all(s.as_bytes())
                            }
                            _         => return Err(~"a non-number was used with %d")
                        }
                    } else { return Err(~"stack is empty") },
                    'p' => state = PushParam,
                    'P' => state = SetVar,
                    'g' => state = GetVar,
                    '\'' => state = CharConstant,
                    '{' => state = IntConstant,
                    'l' => if stack.len() > 0 {
                        match stack.pop() {
                            String(s) => stack.push(Number(s.len() as int)),
                            _         => return Err(~"a non-str was used with %l")
                        }
                    } else { return Err(~"stack is empty") },
                    '+' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(x), Number(y)) => stack.push(Number(x + y)),
                            (_, _) => return Err(~"non-numbers on stack with +")
                        }
                    } else { return Err(~"stack is empty") },
                    '-' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(x), Number(y)) => stack.push(Number(x - y)),
                            (_, _) => return Err(~"non-numbers on stack with -")
                        }
                    } else { return Err(~"stack is empty") },
                    '*' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(x), Number(y)) => stack.push(Number(x * y)),
                            (_, _) => return Err(~"non-numbers on stack with *")
                        }
                    } else { return Err(~"stack is empty") },
                    '/' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(x), Number(y)) => stack.push(Number(x / y)),
                            (_, _) => return Err(~"non-numbers on stack with /")
                        }
                    } else { return Err(~"stack is empty") },
                    'm' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(x), Number(y)) => stack.push(Number(x % y)),
                            (_, _) => return Err(~"non-numbers on stack with %")
                        }
                    } else { return Err(~"stack is empty") },
                    '&' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(x), Number(y)) => stack.push(Number(x & y)),
                            (_, _) => return Err(~"non-numbers on stack with &")
                        }
                    } else { return Err(~"stack is empty") },
                    '|' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(x), Number(y)) => stack.push(Number(x | y)),
                            (_, _) => return Err(~"non-numbers on stack with |")
                        }
                    } else { return Err(~"stack is empty") },
                    'A' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(0), Number(_)) => stack.push(Number(0)),
                            (Number(_), Number(0)) => stack.push(Number(0)),
                            (Number(_), Number(_)) => stack.push(Number(1)),
                            _ => return Err(~"non-numbers on stack with logical and")
                        }
                    } else { return Err(~"stack is empty") },
                    'O' => if stack.len() > 1 {
                        match (stack.pop(), stack.pop()) {
                            (Number(0), Number(0)) => stack.push(Number(0)),
                            (Number(_), Number(_)) => stack.push(Number(1)),
                            _ => return Err(~"non-numbers on stack with logical or")
                        }
                    } else { return Err(~"stack is empty") },
                    '!' => if stack.len() > 0 {
                        match stack.pop() {
                            Number(0) => stack.push(Number(1)),
                            Number(_) => stack.push(Number(0)),
                            _ => return Err(~"non-number on stack with logical not")
                        }
                    } else { return Err(~"stack is empty") },
                    '~' => if stack.len() > 0 {
                        match stack.pop() {
                            Number(x) => stack.push(Number(!x)),
                            _         => return Err(~"non-number on stack with %~")
                        }
                    } else { return Err(~"stack is empty") },
                    'i' => match (copy mparams[0], copy mparams[1]) {
                        (Number(ref mut x), Number(ref mut y)) => {
                            *x += 1;
                            *y += 1;
                        },
                        (_, _) => return Err(~"first two params not numbers with %i")
                    },
                    '?' => state = return Err(fmt!("if expressions unimplemented (%?)", cap)),
                    _ => return Err(fmt!("unrecognized format option %c", cur))
                }
            },
            PushParam => {
                // params are 1-indexed
                stack.push(copy mparams[match char::to_digit(cur, 10) {
                    Some(d) => d - 1,
                    None => return Err(~"bad param number")
                }]);
            },
            SetVar => {
                if cur >= 'A' && cur <= 'Z' {
                    if stack.len() > 0 {
                        let idx = (cur as u8) - ('A' as u8);
                        vars.sta[idx] = stack.pop();
                    } else { return Err(~"stack is empty") }
                } else if cur >= 'a' && cur <= 'z' {
                    if stack.len() > 0 {
                        let idx = (cur as u8) - ('a' as u8);
                        vars.dyn[idx] = stack.pop();
                    } else { return Err(~"stack is empty") }
                } else {
                    return Err(~"bad variable name in %P");
                }
            },
            GetVar => {
                if cur >= 'A' && cur <= 'Z' {
                    let idx = (cur as u8) - ('A' as u8);
                    stack.push(copy vars.sta[idx]);
                } else if cur >= 'a' && cur <= 'z' {
                    let idx = (cur as u8) - ('a' as u8);
                    stack.push(copy vars.dyn[idx]);
                } else {
                    return Err(~"bad variable name in %g");
                }
            },
            CharConstant => {
                stack.push(Number(c as int));
                state = CharClose;
            },
            CharClose => {
                if cur != '\'' {
                    return Err(~"malformed character constant");
                }
            },
            IntConstant => {
                if cur == '}' {
                    stack.push(match int::parse_bytes(intstate, 10) {
                        Some(n) => Number(n),
                        None => return Err(~"bad int constant")
                    });
                    intstate.clear();
                    state = Nothing;
                } else {
                    intstate.push(cur as u8);
                    old_state = Nothing;
                }
            }
            _ => return Err(~"unimplemented state")
        }
        if state == old_state {
            state = Nothing;
        }
    }
    Ok(output)
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_basic_setabf() {
        let s = bytes!("\\E[48;5;%p1%dm");
        assert_eq!(expand(s, [Number(1)], &mut Variables::new()).unwrap(), bytes!("\\E[48;5;1m").to_owned());
    }

    #[test]
    fn test_multiple_int_constants() {
        assert_eq!(expand(bytes!("%{1}%{2}%d%d"), [], &mut Variables::new()).unwrap(), bytes!("21").to_owned());
    }

    #[test]
    fn test_param_stack_failure_conditions() {
        let mut varstruct = Variables::new();
        let vars = &mut varstruct;
        let caps = ["%d", "%c", "%s", "%Pa", "%l", "%!", "%~"];
        for caps.iter().advance |cap| {
            let res = expand(cap.as_bytes(), [], vars);
            assert!(res.is_err(),
                    "Op %s succeeded incorrectly with 0 stack entries", *cap);
            let p = if *cap == "%s" || *cap == "%l" { String(~"foo") } else { Number(97) };
            let res = expand((bytes!("%p1")).to_owned() + cap.as_bytes(), [p], vars);
            assert!(res.is_ok(),
                    "Op %s failed with 1 stack entry: %s", *cap, res.unwrap_err());
        }
        let caps = ["%+", "%-", "%*", "%/", "%m", "%&", "%|", "%A", "%O"];
        for caps.iter().advance |cap| {
            let res = expand(cap.as_bytes(), [], vars);
            assert!(res.is_err(),
                    "Binop %s succeeded incorrectly with 0 stack entries", *cap);
            let res = expand((bytes!("%{1}")).to_owned() + cap.as_bytes(), [], vars);
            assert!(res.is_err(),
                    "Binop %s succeeded incorrectly with 1 stack entry", *cap);
            let res = expand((bytes!("%{1}%{2}")).to_owned() + cap.as_bytes(), [], vars);
            assert!(res.is_ok(),
                    "Binop %s failed with 2 stack entries: %s", *cap, res.unwrap_err());
        }
    }

    #[test]
    fn test_push_bad_param() {
        assert!(expand(bytes!("%pa"), [], &mut Variables::new()).is_err());
    }
}
<|endoftext|>


<commit_before>#![no_main]
#[macro_use] extern crate libfuzzer_sys;
extern crate image;

use std::io::Cursor;
use image::ImageResult;
use image::codecs::openexr::*;
use image::Rgba32FImage;
use image::error::LimitError;
use image::ImageError;
use image::ImageBuffer;
use image::Rgb32FImage;
use std::io::Seek;
use std::io::BufRead;

// "just dont panic"
fn roundtrip(bytes: &[u8]) -> ImageResult<()> {
    use std::io::Write;

    /// Read the file from the specified path into an `Rgba32FImage`.
    // TODO this method should probably already exist in the main image crate
    fn read_as_rgba_byte_image(read: impl BufRead + Seek) -> ImageResult<(usize,usize,Vec<u8>)> {
        let decoder = OpenExrDecoder::with_alpha_preference(read, Some(true))?;
        let (width, height) = decoder.dimensions();

        let mut buffer = vec![0; usize::try_from(decoder.total_bytes()).unwrap()];
        decoder.read_image(bytemuck::cast_slice_mut(buf.as_mut_slice()))?;

        Ok((width, height, buffer))
    }

    /// Write an `Rgba32FImage`.
    /// Assumes the writer is buffered. In most cases,
    /// you should wrap your writer in a `BufWriter` for best performance.
    // TODO this method should probably already exist in the main image crate
    fn write_rgba_image(write: impl Write/* + Seek*/, image: &Rgba32FImage) -> ImageResult<()> {
        OpenExrEncoder::new(write).write_image(
            bytemuck::cast_slice(image.as_raw().as_slice()),
            image.width(), image.height(),
            ColorType::Rgba32F
        )
    }

    let decoded_image = read_as_rgba_byte_image(Cursor::new(bytes))?;

    let mut bytes = Vec::with_capacity(bytes.len() + 20);
    write_rgba_image(Cursor::new(&mut bytes), &decoded_image)?;

    let redecoded_image = read_as_rgba_byte_image(Cursor::new(bytes))?;

    // if both images are valid, assert read/write consistency
    assert_eq!(decoded_image, redecoded_image, "image was valid but was not reproducible");
    Ok(())
}

fuzz_target!(|data: &[u8]| {
    let _img = roundtrip(data); // fixme not optimized away?
});
<commit_msg>fix more compiler errrors<commit_after>#![no_main]
#[macro_use] extern crate libfuzzer_sys;
extern crate image;

use std::io::Cursor;
use image::ImageResult;
use image::codecs::openexr::*;
use image::Rgba32FImage;
use std::io::Seek;
use std::io::BufRead;
use std::convert::TryFrom;
use image::ImageDecoder;

// "just dont panic"
fn roundtrip(bytes: &[u8]) -> ImageResult<()> {
    use std::io::Write;

    /// Read the file from the specified path into an `Rgba32FImage`.
    // TODO this method should probably already exist in the main image crate
    fn read_as_rgba_byte_image(read: impl BufRead + Seek) -> ImageResult<(usize,usize,Vec<u8>)> {
        let decoder = OpenExrDecoder::with_alpha_preference(read, Some(true))?;
        let (width, height) = decoder.dimensions();

        let mut buffer = vec![0; usize::try_from(decoder.total_bytes()).unwrap()];
        decoder.read_image(buffer.as_mut_slice())?;

        Ok((width, height, buffer))
    }

    /// Write an `Rgba32FImage`.
    /// Assumes the writer is buffered. In most cases,
    /// you should wrap your writer in a `BufWriter` for best performance.
    // TODO this method should probably already exist in the main image crate
    fn write_rgba_image(write: impl Write/* + Seek*/, image: &Rgba32FImage) -> ImageResult<()> {
        OpenExrEncoder::new(write).write_image(
            bytemuck::cast_slice(image.as_raw().as_slice()),
            image.width(), image.height(),
            ColorType::Rgba32F
        )
    }

    let decoded_image = read_as_rgba_byte_image(Cursor::new(bytes))?;

    let mut bytes = Vec::with_capacity(bytes.len() + 20);
    write_rgba_image(Cursor::new(&mut bytes), &decoded_image)?;

    let redecoded_image = read_as_rgba_byte_image(Cursor::new(bytes))?;

    // if both images are valid, assert read/write consistency
    assert_eq!(decoded_image, redecoded_image, "image was valid but was not reproducible");
    Ok(())
}

fuzz_target!(|data: &[u8]| {
    let _img = roundtrip(data); // fixme not optimized away?
});
<|endoftext|>