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

use message::Message;
use error::{Error, Result};

use std::collections::HashMap;
use std::path::Path;
use std::fs::File;
use std::io::Read;
use std::io::Write;

use regex::Regex;
use walkdir::WalkDir;

/// Struct that extracts all messages from source code and can print them
/// to a `.pot` equivalent
pub struct Extractor {
    messages: HashMap<String, Message>,
}

impl Extractor {
    /// Create a new, empty extractor
    pub fn new() -> Extractor {
        Extractor {
            messages: HashMap::new(),
        }
    }

    /// Add all the messages contained in a source file
    pub fn add_messages_from_file<P: AsRef<Path>>(&mut self, file: P) -> Result<()> {
        lazy_static! {
            static ref REMOVE_COMMS: Regex = Regex::new(r#"//[^\n]*"#).unwrap();
            static ref FIND_MSGS: Regex = Regex::new(r#"lformat!\("#).unwrap();
        }

        let filename =  format!("{}", file.as_ref().display());
        let mut f = try!(File::open(file)
                         .map_err(|e| Error::parse(format!("could not open file {}: {}",
                                                           &filename,
                                                           e))));
        let mut content = String::new();
        try!(f.read_to_string(&mut content)
            .map_err(|e| Error::parse(format!("could not read file {}: {}",
                                              &filename,
                                              e))));
        content = REMOVE_COMMS.replace_all(&content, "");

        for caps in FIND_MSGS.captures_iter(&content) {
            let (_, pos) = caps.pos(0).unwrap();
            let line = 1 + &content[..pos].bytes().filter(|b| b == &b'\n').count();

            let bytes = content[pos..].as_bytes();
            let msg = try!(find_string(bytes)
                           .map_err(|_| Error::parse(format!("{}:{}: could not parse as string",
                                                            &filename,
                                                            line))));

            if self.messages.contains_key(&msg) {
                self.messages.get_mut(&msg).unwrap().add_source(filename.as_str(), line);
            } else {
                let mut message = Message::new(msg.as_str());
                message.add_source(filename.as_str(), line);
                self.messages.insert(msg, message);
            }
        }

        Ok(())
    }

    /// Add messages from all `.rs` files contained in a directory
    /// (walks through subdirectories)
    pub fn add_messages_from_dir<P: AsRef<Path>>(&mut self, dir: P) -> Result<()> {
        let filtered =  WalkDir::new(dir)
            .into_iter()
            .filter_map(|e| e.ok())
            .map(|e| e.path()
                 .to_string_lossy()
                 .into_owned())
            .filter(|s| s.ends_with(".rs"));
        for filename in filtered {
            try!(self.add_messages_from_file(&filename));
        }

        Ok(())
    }

    /// Generate a pot-like file from the strings extracted from all files (if any)
    pub fn generate_pot_file(&self) -> String {
        let mut output = String::from("#, fuzzy\n\n");
        for value in self.messages.values() {
            output.push_str(&format!("{}", value));
        }
        output
    }

    /// Write a pot-like file to specified location
    pub fn write_pot_file(self, file: &str) -> Result<()> {
        let mut f = try!(File::create(file).map_err(|e| Error::new(format!("Could not create file {}: {}",
                                                                              file, e))));
        let content = self.generate_pot_file();
        try!(f.write_all(content.as_bytes())
             .map_err(|e| Error::new(format!("Could not write to file {}: {}",
                                             file, e))));
        Ok(())
    }
}

fn find_string(bytes: &[u8]) -> Result<String> {
    let mut begin = None;
    let mut i = 0;
    while i < bytes.len() {
        match bytes[i] {
            b'"' => if begin.is_some() {
                if bytes[i-1] != b'\\' {
                    break
                }
            } else {
                if i + 1 >= bytes.len() {
                    return Err(Error::new(""));
                }
                begin = Some(i + 1);
            },
            _ => (),
        }
        i += 1;
    }
    let begin = if let Some(begin) = begin {
        begin
    } else {
        return Err(Error::new(""));
    };
    Ok(String::from_utf8(bytes[begin..i].to_vec()).unwrap())
}
<commit_msg>Expand a bit Extractor's documentation<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with
// this file, You can obtain one at https://mozilla.org/MPL/2.0/.

use message::Message;
use error::{Error, Result};

use std::collections::HashMap;
use std::path::Path;
use std::fs::File;
use std::io::Read;
use std::io::Write;

use regex::Regex;
use walkdir::WalkDir;

/// Struct that extracts all messages from source code and can print them
/// to a `.pot` file.
///
/// This file can then be used as a starting point to begin translation.
/// It should be relatively similar to `gettext` generated files.
///
/// # Example
///
/// ```
/// use crowbook_localize::Extractor;
/// let mut extractor = Extractor::new();
/// extractor.add_messages_from_dir("src/").unwrap();
/// println!("{}", extractor.generate_pot_file());
/// ```
///
/// # Note
///
/// This struct only add messages that are considered as needing localization,
/// that is, the first argument of calls so `lformat!` macro.
pub struct Extractor {
    messages: HashMap<String, Message>,
}

impl Extractor {
    /// Create a new, empty extractor
    pub fn new() -> Extractor {
        Extractor {
            messages: HashMap::new(),
        }
    }

    /// Add all the messages contained in a source file
    pub fn add_messages_from_file<P: AsRef<Path>>(&mut self, file: P) -> Result<()> {
        lazy_static! {
            static ref REMOVE_COMMS: Regex = Regex::new(r#"//[^\n]*"#).unwrap();
            static ref FIND_MSGS: Regex = Regex::new(r#"lformat!\("#).unwrap();
        }

        let filename =  format!("{}", file.as_ref().display());
        let mut f = try!(File::open(file)
                         .map_err(|e| Error::parse(format!("could not open file {}: {}",
                                                           &filename,
                                                           e))));
        let mut content = String::new();
        try!(f.read_to_string(&mut content)
            .map_err(|e| Error::parse(format!("could not read file {}: {}",
                                              &filename,
                                              e))));
        content = REMOVE_COMMS.replace_all(&content, "");

        for caps in FIND_MSGS.captures_iter(&content) {
            let (_, pos) = caps.pos(0).unwrap();
            let line = 1 + &content[..pos].bytes().filter(|b| b == &b'\n').count();

            let bytes = content[pos..].as_bytes();
            let msg = try!(find_string(bytes)
                           .map_err(|_| Error::parse(format!("{}:{}: could not parse as string",
                                                            &filename,
                                                            line))));

            if self.messages.contains_key(&msg) {
                self.messages.get_mut(&msg).unwrap().add_source(filename.as_str(), line);
            } else {
                let mut message = Message::new(msg.as_str());
                message.add_source(filename.as_str(), line);
                self.messages.insert(msg, message);
            }
        }

        Ok(())
    }

    /// Add messages from all `.rs` files contained in a directory
    /// (walks through subdirectories)
    pub fn add_messages_from_dir<P: AsRef<Path>>(&mut self, dir: P) -> Result<()> {
        let filtered =  WalkDir::new(dir)
            .into_iter()
            .filter_map(|e| e.ok())
            .map(|e| e.path()
                 .to_string_lossy()
                 .into_owned())
            .filter(|s| s.ends_with(".rs"));
        for filename in filtered {
            try!(self.add_messages_from_file(&filename));
        }

        Ok(())
    }

    /// Generate a pot-like file from the strings extracted from all files (if any)
    pub fn generate_pot_file(&self) -> String {
        let mut output = String::from("#, fuzzy\n\n");
        for value in self.messages.values() {
            output.push_str(&format!("{}", value));
        }
        output
    }

    /// Write a pot-like file to specified location
    pub fn write_pot_file(self, file: &str) -> Result<()> {
        let mut f = try!(File::create(file).map_err(|e| Error::new(format!("Could not create file {}: {}",
                                                                              file, e))));
        let content = self.generate_pot_file();
        try!(f.write_all(content.as_bytes())
             .map_err(|e| Error::new(format!("Could not write to file {}: {}",
                                             file, e))));
        Ok(())
    }
}

fn find_string(bytes: &[u8]) -> Result<String> {
    let mut begin = None;
    let mut i = 0;
    while i < bytes.len() {
        match bytes[i] {
            b'"' => if begin.is_some() {
                if bytes[i-1] != b'\\' {
                    break
                }
            } else {
                if i + 1 >= bytes.len() {
                    return Err(Error::new(""));
                }
                begin = Some(i + 1);
            },
            _ => (),
        }
        i += 1;
    }
    let begin = if let Some(begin) = begin {
        begin
    } else {
        return Err(Error::new(""));
    };
    Ok(String::from_utf8(bytes[begin..i].to_vec()).unwrap())
}
<|endoftext|>


<commit_before>use env::{Environment, Point};
use loans_in_scope::{Loan, LoansInScope};
use nll_repr::repr;
use std::error::Error;
use std::fmt;

pub fn borrow_check(env: &Environment, loans_in_scope: &LoansInScope) -> Result<(), Box<Error>> {
    let mut result: Result<(), Box<Error>> = Ok(());
    loans_in_scope.walk(env, |point, opt_action, loans| {
        let borrowck = BorrowCheck { env, point, loans };
        if let Some(action) = opt_action {
            if let Err(e) = borrowck.check_action(action) {
                if !action.should_have_error {
                    result = Err(e);
                }
            } else if action.should_have_error {
                result = Err(Box::new(BorrowError::no_error(point)));
            }
        }
    });

    result
}

struct BorrowCheck<'cx> {
    env: &'cx Environment<'cx>,
    point: Point,
    loans: &'cx [&'cx Loan<'cx>],
}

impl<'cx> BorrowCheck<'cx> {
    fn check_action(&self, action: &repr::Action) -> Result<(), Box<Error>> {
        log!("check_action({:?}) at {:?}", action, self.point);
        match action.kind {
            repr::ActionKind::Init(ref a, ref bs) => {
                self.check_write(a)?;
                for b in bs {
                    self.check_read(b)?;
                }
            }
            repr::ActionKind::Assign(ref a, ref b) => {
                self.check_write(a)?;
                self.check_read(b)?;
            }
            repr::ActionKind::Borrow(ref a, _, repr::BorrowKind::Shared, ref b) => {
                self.check_write(a)?;
                self.check_read(b)?;
            }
            repr::ActionKind::Borrow(ref a, _, repr::BorrowKind::Mut, ref b) => {
                self.check_write(a)?;
                self.check_write(b)?;
            }
            repr::ActionKind::Constraint(_) => {}
            repr::ActionKind::Use(ref p) => {
                self.check_read(p)?;
            }
            repr::ActionKind::Drop(ref p) => {
                self.check_move(p)?;
            }
            repr::ActionKind::StorageDead(p) => {
                self.check_storage_dead(p)?;
            }
            repr::ActionKind::Noop => {}
        }

        Ok(())
    }

    /// Cannot write to a path `p` if:
    /// - the path `p` is frozen by one of the loans
    ///   - see `frozen_by_borrow_of()`
    ///   - this covers writing to `a` (or `a.b`) when `a.b` is borrowed
    /// - some prefix of `p` is borrowed.
    ///   - this covers writing to `a.b.c` when `a.b` is borrowed
    fn check_write(&self, path: &repr::Path) -> Result<(), Box<Error>> {
        log!(
            "check_write of {:?} at {:?} with loans={:#?}",
            path,
            self.point,
            self.loans
        );
        if let Some(loan) = self.find_loan_that_freezes(path) {
            return Err(Box::new(BorrowError::for_write(
                self.point,
                path,
                &loan.path,
                loan.point,
            )));
        }
        Ok(())
    }

    /// Cannot read from a path `a.b.c` if:
    /// - the exact path `a.b.c` is borrowed mutably;
    /// - some subpath `a.b.c.d` is borrowed mutably;
    /// - some prefix of `a.b` is borrowed mutably.
    fn check_read(&self, path: &repr::Path) -> Result<(), Box<Error>> {
        log!(
            "check_read of {:?} at {:?} with loans={:#?}",
            path,
            self.point,
            self.loans
        );
        let path_prefixes = path.prefixes();
        for loan in self.loans {
            match loan.kind {
                repr::BorrowKind::Shared => continue,
                repr::BorrowKind::Mut => {}
            }

            if {
                // `a.b.c` or `a.b` is borrowed
                path_prefixes.contains(&loan.path) ||

                    // `a.b.c.d` is borrowed
                    loan.path.prefixes().contains(&path)
            } {
                return Err(Box::new(BorrowError::for_read(
                    self.point,
                    path,
                    &loan.path,
                    loan.point,
                )));
            }
        }
        Ok(())
    }

    /// Cannot move from a path `p` if:
    /// - `p` is borrowed;
    /// - some subpath `p.foo` is borrowed;
    /// - some prefix of `p` is borrowed.
    ///
    /// Note that this is stricter than both *writes* and
    /// *storage-dead*. In particular, you **can** write to a variable
    /// `x` that contains an `&mut` value when `*x` is borrowed, but
    /// you **cannot** move `x`. This is because moving it would make
    /// the `&mut` available in the new location, but writing (and
    /// storage-dead) both kill it forever.
    fn check_move(&self, path: &repr::Path) -> Result<(), Box<Error>> {
        log!(
            "check_move of {:?} at {:?} with loans={:#?}",
            path,
            self.point,
            self.loans
        );
        let path_prefixes = path.prefixes();
        for loan in self.loans {
            if {
                // accessing `a.b.c` is illegal if `a.b.c` or `a.b` is
                // borrowed...
                path_prefixes.contains(&loan.path) ||

                    // ...or `a.b.c.d` is borrowed
                    loan.path.prefixes().contains(&path)
            } {
                return Err(Box::new(BorrowError::for_move(
                    self.point,
                    path,
                    &loan.path,
                    loan.point,
                )));
            }
        }
        Ok(())
    }

    /// Cannot free a local variable `var` if:
    /// - data interior to `var` is borrowed.
    ///
    /// In particular, having something like `*var` borrowed is ok.
    fn check_storage_dead(&self, var: repr::Variable) -> Result<(), Box<Error>> {
        log!(
            "check_storage_dead of {:?} at {:?} with loans={:#?}",
            var,
            self.point,
            self.loans
        );
        if let Some(loan) = self.find_loan_that_freezes(&repr::Path::Base(var)) {
            return Err(Box::new(BorrowError::for_storage_dead(
                self.point,
                var,
                &loan.path,
                loan.point,
            )));
        }
        Ok(())
    }

    /// Helper for `check_write` and `check_storage_dead`: finds if
    /// there is a loan that "freezes" the given path -- that is, a
    /// loan that would make modifying the `path` (or freeing it)
    /// illegal. This is slightly more permissive than the rules
    /// around move and reads, precisely because overwriting or
    /// freeing `path` makes the previous value unavailable from that
    /// point on.
    fn find_loan_that_freezes(&self, path: &repr::Path) -> Option<&Loan> {
        let prefixes = path.prefixes();
        self.loans.iter().cloned().find(|loan| {
            // If you have borrowed `a.b`, this prevents writes to `a`
            // or `a.b`:
            let frozen_paths = self.frozen_by_borrow_of(&loan.path);
            frozen_paths.contains(&path) ||

                // If you have borrowed `a.b`, this prevents writes to
                // `a.b.c`:
                prefixes.contains(&loan.path)
        })
    }

    /// If `path` is mutably borrowed, returns a vector of paths which -- if
    /// moved or if the storage went away -- would invalidate this
    /// reference.
    fn frozen_by_borrow_of<'a>(&self, mut path: &'a repr::Path) -> Vec<&'a repr::Path> {
        let mut result = vec![];
        loop {
            result.push(path);
            match *path {
                repr::Path::Base(_) => return result,
                repr::Path::Extension(ref base_path, field_name) => {
                    match *self.env.path_ty(base_path) {
                        // If you borrowed `*r`, writing to `r` does
                        // not actually affect the memory at `*r`, so
                        // we can stop iterating backwards now.
                        repr::Ty::Ref(_, _, _) => {
                            assert_eq!(field_name, repr::FieldName::star());
                            return result;
                        }

                        // If you have borrowed `a.b`, then writing to
                        // `a` would overwrite `a.b`, which is
                        // disallowed.
                        repr::Ty::Struct(..) => {
                            path = base_path;
                        }

                        repr::Ty::Unit => panic!("unit has no fields"),
                        repr::Ty::Bound(..) => panic!("unexpected bound type"),
                    }
                }
            }
        }
    }
}

#[derive(Debug)]
pub struct BorrowError {
    description: String,
}

impl BorrowError {
    fn no_error(point: Point) -> Self {
        BorrowError {
            description: format!("point {:?} had no error, but should have", point),
        }
    }

    fn for_move(
        point: Point,
        path: &repr::Path,
        loan_path: &repr::Path,
        loan_point: Point,
    ) -> Self {
        BorrowError {
            description: format!(
                "point {:?} cannot move {:?} because {:?} is borrowed (at point `{:?}`)",
                point,
                path,
                loan_path,
                loan_point
            ),
        }
    }

    fn for_read(
        point: Point,
        path: &repr::Path,
        loan_path: &repr::Path,
        loan_point: Point,
    ) -> Self {
        BorrowError {
            description: format!(
                "point {:?} cannot read {:?} because {:?} is mutably borrowed (at point `{:?}`)",
                point,
                path,
                loan_path,
                loan_point
            ),
        }
    }

    fn for_write(
        point: Point,
        path: &repr::Path,
        loan_path: &repr::Path,
        loan_point: Point,
    ) -> Self {
        BorrowError {
            description: format!(
                "point {:?} cannot write {:?} because {:?} is borrowed (at point `{:?}`)",
                point,
                path,
                loan_path,
                loan_point
            ),
        }
    }

    fn for_storage_dead(
        point: Point,
        var: repr::Variable,
        loan_path: &repr::Path,
        loan_point: Point,
    ) -> Self {
        BorrowError {
            description: format!(
                "point {:?} cannot kill storage for {:?} \
                 because {:?} is borrowed (at point `{:?}`)",
                point,
                var,
                loan_path,
                loan_point
            ),
        }
    }
}

impl Error for BorrowError {
    fn description(&self) -> &str {
        &self.description
    }

    fn cause(&self) -> Option<&Error> {
        None
    }
}

impl fmt::Display for BorrowError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{}", self.description)
    }
}
<commit_msg>extract common code between read/move into a helper<commit_after>use env::{Environment, Point};
use loans_in_scope::{Loan, LoansInScope};
use nll_repr::repr;
use std::error::Error;
use std::fmt;

pub fn borrow_check(env: &Environment, loans_in_scope: &LoansInScope) -> Result<(), Box<Error>> {
    let mut result: Result<(), Box<Error>> = Ok(());
    loans_in_scope.walk(env, |point, opt_action, loans| {
        let borrowck = BorrowCheck { env, point, loans };
        if let Some(action) = opt_action {
            if let Err(e) = borrowck.check_action(action) {
                if !action.should_have_error {
                    result = Err(e);
                }
            } else if action.should_have_error {
                result = Err(Box::new(BorrowError::no_error(point)));
            }
        }
    });

    result
}

struct BorrowCheck<'cx> {
    env: &'cx Environment<'cx>,
    point: Point,
    loans: &'cx [&'cx Loan<'cx>],
}

impl<'cx> BorrowCheck<'cx> {
    fn check_action(&self, action: &repr::Action) -> Result<(), Box<Error>> {
        log!("check_action({:?}) at {:?}", action, self.point);
        match action.kind {
            repr::ActionKind::Init(ref a, ref bs) => {
                self.check_write(a)?;
                for b in bs {
                    self.check_read(b)?;
                }
            }
            repr::ActionKind::Assign(ref a, ref b) => {
                self.check_write(a)?;
                self.check_read(b)?;
            }
            repr::ActionKind::Borrow(ref a, _, repr::BorrowKind::Shared, ref b) => {
                self.check_write(a)?;
                self.check_read(b)?;
            }
            repr::ActionKind::Borrow(ref a, _, repr::BorrowKind::Mut, ref b) => {
                self.check_write(a)?;
                self.check_write(b)?;
            }
            repr::ActionKind::Constraint(_) => {}
            repr::ActionKind::Use(ref p) => {
                self.check_read(p)?;
            }
            repr::ActionKind::Drop(ref p) => {
                self.check_move(p)?;
            }
            repr::ActionKind::StorageDead(p) => {
                self.check_storage_dead(p)?;
            }
            repr::ActionKind::Noop => {}
        }

        Ok(())
    }

    /// Cannot write to a path `p` if:
    /// - the path `p` is frozen by one of the loans
    ///   - see `frozen_by_borrow_of()`
    ///   - this covers writing to `a` (or `a.b`) when `a.b` is borrowed
    /// - some prefix of `p` is borrowed.
    ///   - this covers writing to `a.b.c` when `a.b` is borrowed
    fn check_write(&self, path: &repr::Path) -> Result<(), Box<Error>> {
        log!(
            "check_write of {:?} at {:?} with loans={:#?}",
            path,
            self.point,
            self.loans
        );
        if let Some(loan) = self.find_loan_that_freezes(path) {
            return Err(Box::new(BorrowError::for_write(
                self.point,
                path,
                &loan.path,
                loan.point,
            )));
        }
        Ok(())
    }

    /// Cannot read from a path `a.b.c` if:
    /// - the exact path `a.b.c` is borrowed mutably;
    /// - some subpath `a.b.c.d` is borrowed mutably;
    /// - some prefix of `a.b` is borrowed mutably.
    fn check_read(&self, path: &repr::Path) -> Result<(), Box<Error>> {
        log!(
            "check_read of {:?} at {:?} with loans={:#?}",
            path,
            self.point,
            self.loans
        );
        for loan in self.find_loans_that_intersect(path) {
            match loan.kind {
                repr::BorrowKind::Shared => { /* Ok */ }
                repr::BorrowKind::Mut => {
                    return Err(Box::new(BorrowError::for_read(
                        self.point,
                        path,
                        &loan.path,
                        loan.point,
                    )));
                }
            }
        }
        Ok(())
    }

    /// Cannot move from a path `p` if:
    /// - `p` is borrowed;
    /// - some subpath `p.foo` is borrowed;
    /// - some prefix of `p` is borrowed.
    ///
    /// Note that this is stricter than both *writes* and
    /// *storage-dead*. In particular, you **can** write to a variable
    /// `x` that contains an `&mut` value when `*x` is borrowed, but
    /// you **cannot** move `x`. This is because moving it would make
    /// the `&mut` available in the new location, but writing (and
    /// storage-dead) both kill it forever.
    fn check_move(&self, path: &repr::Path) -> Result<(), Box<Error>> {
        log!(
            "check_move of {:?} at {:?} with loans={:#?}",
            path,
            self.point,
            self.loans
        );
        for loan in self.find_loans_that_intersect(path) {
            return Err(Box::new(BorrowError::for_move(
                self.point,
                path,
                &loan.path,
                loan.point,
            )));
        }
        Ok(())
    }

    /// Cannot free a local variable `var` if:
    /// - data interior to `var` is borrowed.
    ///
    /// In particular, having something like `*var` borrowed is ok.
    fn check_storage_dead(&self, var: repr::Variable) -> Result<(), Box<Error>> {
        log!(
            "check_storage_dead of {:?} at {:?} with loans={:#?}",
            var,
            self.point,
            self.loans
        );
        if let Some(loan) = self.find_loan_that_freezes(&repr::Path::Base(var)) {
            return Err(Box::new(BorrowError::for_storage_dead(
                self.point,
                var,
                &loan.path,
                loan.point,
            )));
        }
        Ok(())
    }

    fn find_loans_that_intersect<'a>(
        &'a self,
        path: &'a repr::Path,
    ) -> impl Iterator<Item = &'a Loan> + 'a {
        let path_prefixes = path.prefixes();
        self.loans.iter().cloned().filter(move |loan| {
            // accessing `a.b.c` intersects a loan of `a.b.c` or `a.b`...
            path_prefixes.contains(&loan.path) ||

            // ...as well as a loan of `a.b.c.d`
                loan.path.prefixes().contains(&path)
        })
    }

    /// Helper for `check_write` and `check_storage_dead`: finds if
    /// there is a loan that "freezes" the given path -- that is, a
    /// loan that would make modifying the `path` (or freeing it)
    /// illegal. This is slightly more permissive than the rules
    /// around move and reads, precisely because overwriting or
    /// freeing `path` makes the previous value unavailable from that
    /// point on.
    fn find_loan_that_freezes(&self, path: &repr::Path) -> Option<&Loan> {
        let prefixes = path.prefixes();
        self.loans.iter().cloned().find(|loan| {
            // If you have borrowed `a.b`, this prevents writes to `a`
            // or `a.b`:
            let frozen_paths = self.frozen_by_borrow_of(&loan.path);
            frozen_paths.contains(&path) ||

                // If you have borrowed `a.b`, this prevents writes to
                // `a.b.c`:
                prefixes.contains(&loan.path)
        })
    }

    /// If `path` is mutably borrowed, returns a vector of paths which -- if
    /// moved or if the storage went away -- would invalidate this
    /// reference.
    fn frozen_by_borrow_of<'a>(&self, mut path: &'a repr::Path) -> Vec<&'a repr::Path> {
        let mut result = vec![];
        loop {
            result.push(path);
            match *path {
                repr::Path::Base(_) => return result,
                repr::Path::Extension(ref base_path, field_name) => {
                    match *self.env.path_ty(base_path) {
                        // If you borrowed `*r`, writing to `r` does
                        // not actually affect the memory at `*r`, so
                        // we can stop iterating backwards now.
                        repr::Ty::Ref(_, _, _) => {
                            assert_eq!(field_name, repr::FieldName::star());
                            return result;
                        }

                        // If you have borrowed `a.b`, then writing to
                        // `a` would overwrite `a.b`, which is
                        // disallowed.
                        repr::Ty::Struct(..) => {
                            path = base_path;
                        }

                        repr::Ty::Unit => panic!("unit has no fields"),
                        repr::Ty::Bound(..) => panic!("unexpected bound type"),
                    }
                }
            }
        }
    }
}

#[derive(Debug)]
pub struct BorrowError {
    description: String,
}

impl BorrowError {
    fn no_error(point: Point) -> Self {
        BorrowError {
            description: format!("point {:?} had no error, but should have", point),
        }
    }

    fn for_move(
        point: Point,
        path: &repr::Path,
        loan_path: &repr::Path,
        loan_point: Point,
    ) -> Self {
        BorrowError {
            description: format!(
                "point {:?} cannot move {:?} because {:?} is borrowed (at point `{:?}`)",
                point,
                path,
                loan_path,
                loan_point
            ),
        }
    }

    fn for_read(
        point: Point,
        path: &repr::Path,
        loan_path: &repr::Path,
        loan_point: Point,
    ) -> Self {
        BorrowError {
            description: format!(
                "point {:?} cannot read {:?} because {:?} is mutably borrowed (at point `{:?}`)",
                point,
                path,
                loan_path,
                loan_point
            ),
        }
    }

    fn for_write(
        point: Point,
        path: &repr::Path,
        loan_path: &repr::Path,
        loan_point: Point,
    ) -> Self {
        BorrowError {
            description: format!(
                "point {:?} cannot write {:?} because {:?} is borrowed (at point `{:?}`)",
                point,
                path,
                loan_path,
                loan_point
            ),
        }
    }

    fn for_storage_dead(
        point: Point,
        var: repr::Variable,
        loan_path: &repr::Path,
        loan_point: Point,
    ) -> Self {
        BorrowError {
            description: format!(
                "point {:?} cannot kill storage for {:?} \
                 because {:?} is borrowed (at point `{:?}`)",
                point,
                var,
                loan_path,
                loan_point
            ),
        }
    }
}

impl Error for BorrowError {
    fn description(&self) -> &str {
        &self.description
    }

    fn cause(&self) -> Option<&Error> {
        None
    }
}

impl fmt::Display for BorrowError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{}", self.description)
    }
}
<|endoftext|>


<commit_before>use env::{Environment, Point};
use loans_in_scope::{Loan, LoansInScope};
use nll_repr::repr;
use std::error::Error;
use std::fmt;

pub fn borrow_check(env: &Environment, loans_in_scope: &LoansInScope) -> Result<(), Box<Error>> {
    let mut result: Result<(), Box<Error>> = Ok(());
    loans_in_scope.walk(env, |point, opt_action, loans| {
        let borrowck = BorrowCheck { env, point, loans };
        if let Some(action) = opt_action {
            if let Err(e) = borrowck.check_action(action) {
                if !action.should_have_error {
                    result = Err(e);
                }
            } else if action.should_have_error {
                result = Err(Box::new(BorrowError::no_error(point)));
            }
        }
    });

    result
}

struct BorrowCheck<'cx> {
    env: &'cx Environment<'cx>,
    point: Point,
    loans: &'cx [&'cx Loan<'cx>],
}

impl<'cx> BorrowCheck<'cx> {
    fn check_action(&self, action: &repr::Action) -> Result<(), Box<Error>> {
        match action.kind {
            repr::ActionKind::Init(ref a, ref bs) => {
                self.check_write(a)?;
                for b in bs {
                    self.check_read(b)?;
                }
            }
            repr::ActionKind::Assign(ref a, ref b) => {
                self.check_write(a)?;
                self.check_read(b)?;
            }
            repr::ActionKind::Borrow(ref a, _, repr::BorrowKind::Shared, ref b) => {
                self.check_write(a)?;
                self.check_read(b)?;
            }
            repr::ActionKind::Borrow(ref a, _, repr::BorrowKind::Mut, ref b) => {
                self.check_write(a)?;
                self.check_write(b)?;
            }
            repr::ActionKind::Constraint(_) => {}
            repr::ActionKind::Use(ref p) => {
                self.check_read(p)?;
            }
            repr::ActionKind::Drop(ref p) => {
                self.check_move(p)?;
            }
            repr::ActionKind::StorageDead(p) => {
                self.check_storage_dead(p)?;
            }
            repr::ActionKind::Noop => {}
        }

        Ok(())
    }

    fn check_write(&self, _path: &repr::Path) -> Result<(), Box<Error>> {
        Ok(())
    }

    /// Cannot read from a path `p` if:
    /// - `p` is borrowed mutably;
    /// - some subpath `p.foo` is borrowed mutably;
    /// - some prefix of `p` is borrowed mutably.
    fn check_read(&self, path: &repr::Path) -> Result<(), Box<Error>> {
        let prefixes = path.prefixes();
        for loan in self.loans {
            match loan.kind {
                repr::BorrowKind::Shared => continue,
                repr::BorrowKind::Mut => {}
            }

            for loan_prefix in loan.path.prefixes() {
                if prefixes.contains(&loan_prefix) {
                    return Err(Box::new(BorrowError::for_read(
                        self.point,
                        path,
                        &loan.path,
                        loan.point,
                    )));
                }
            }
        }
        Ok(())
    }

    /// Cannot move from a path `p` if:
    /// - `p` is borrowed;
    /// - some subpath `p.foo` is borrowed;
    /// - some prefix of `p` is borrowed.
    ///
    /// XXX counterexample?
    ///
    /// ```
    /// let p: &i32;
    /// let q = &*p;
    /// move(p); // but this is not actually a *move*, is the point
    /// ```
    fn check_move(&self, path: &repr::Path) -> Result<(), Box<Error>> {
        let prefixes = path.prefixes();
        for loan in self.loans {
            for loan_prefix in loan.path.prefixes() {
                if prefixes.contains(&loan_prefix) {
                    return Err(Box::new(
                        BorrowError::for_move(self.point, path, &loan.path, loan.point),
                    ));
                }
            }
        }
        Ok(())
    }

    /// Cannot free a local variable `var` if:
    /// - data interior to `var` is borrowed.
    ///
    /// In particular, having something like `*var` borrowed is ok.
    fn check_storage_dead(&self, var: repr::Variable) -> Result<(), Box<Error>> {
        for loan in self.loans {
            if let Some(loan_var) = self.invalidated_by_dead_storage(&loan.path) {
                if var == loan_var {
                    return Err(Box::new(
                        BorrowError::for_storage_dead(self.point, var, &loan.path, loan.point),
                    ));
                }
            }
        }
        Ok(())
    }

    /// If `path` is borrowed, returns a vector of paths which -- if
    /// moved or if the storage went away -- would invalidate this
    /// reference.
    fn invalidated_by_dead_storage(&self, mut path: &repr::Path) -> Option<repr::Variable> {
        loop {
            match *path {
                repr::Path::Base(v) => return Some(v),
                repr::Path::Extension(ref base_path, field_name) => {
                    match *self.env.path_ty(base_path) {
                        // If you borrow `*r`, you can drop the
                        // reference `r` without invalidating that
                        // memory.
                        repr::Ty::Ref(_, _, _) => {
                            assert_eq!(field_name, repr::FieldName::star());
                            return None;
                        }

                        repr::Ty::Unit |
                        repr::Ty::Struct(..) => {
                            path = base_path;
                        }

                        repr::Ty::Bound(..) => panic!("unexpected bound type"),
                    }
                }
            }
        }
    }
}

#[derive(Debug)]
pub struct BorrowError {
    description: String,
}

impl BorrowError {
    fn no_error(point: Point) -> Self {
        BorrowError {
            description: format!("point {:?} had no error, but should have", point),
        }
    }

    fn for_move(
        point: Point,
        path: &repr::Path,
        loan_path: &repr::Path,
        loan_point: Point,
    ) -> Self {
        BorrowError {
            description: format!(
                "point {:?} cannot move {:?} because {:?} is borrowed (at point `{:?}`)",
                point,
                path,
                loan_path,
                loan_point
            ),
        }
    }

    fn for_read(
        point: Point,
        path: &repr::Path,
        loan_path: &repr::Path,
        loan_point: Point,
    ) -> Self {
        BorrowError {
            description: format!(
                "point {:?} cannot read {:?} because {:?} is mutably borrowed (at point `{:?}`)",
                point,
                path,
                loan_path,
                loan_point
            ),
        }
    }

    fn for_storage_dead(
        point: Point,
        var: repr::Variable,
        loan_path: &repr::Path,
        loan_point: Point,
    ) -> Self {
        BorrowError {
            description: format!(
                "point {:?} cannot kill storage for {:?} because {:?} is borrowed (at point `{:?}`)",
                point,
                var,
                loan_path,
                loan_point
            ),
        }
    }
}

impl Error for BorrowError {
    fn description(&self) -> &str {
        &self.description
    }

    fn cause(&self) -> Option<&Error> {
        None
    }
}

impl fmt::Display for BorrowError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{}", self.description)
    }
}
<commit_msg>reapply rustfmt<commit_after>use env::{Environment, Point};
use loans_in_scope::{Loan, LoansInScope};
use nll_repr::repr;
use std::error::Error;
use std::fmt;

pub fn borrow_check(env: &Environment, loans_in_scope: &LoansInScope) -> Result<(), Box<Error>> {
    let mut result: Result<(), Box<Error>> = Ok(());
    loans_in_scope.walk(env, |point, opt_action, loans| {
        let borrowck = BorrowCheck { env, point, loans };
        if let Some(action) = opt_action {
            if let Err(e) = borrowck.check_action(action) {
                if !action.should_have_error {
                    result = Err(e);
                }
            } else if action.should_have_error {
                result = Err(Box::new(BorrowError::no_error(point)));
            }
        }
    });

    result
}

struct BorrowCheck<'cx> {
    env: &'cx Environment<'cx>,
    point: Point,
    loans: &'cx [&'cx Loan<'cx>],
}

impl<'cx> BorrowCheck<'cx> {
    fn check_action(&self, action: &repr::Action) -> Result<(), Box<Error>> {
        match action.kind {
            repr::ActionKind::Init(ref a, ref bs) => {
                self.check_write(a)?;
                for b in bs {
                    self.check_read(b)?;
                }
            }
            repr::ActionKind::Assign(ref a, ref b) => {
                self.check_write(a)?;
                self.check_read(b)?;
            }
            repr::ActionKind::Borrow(ref a, _, repr::BorrowKind::Shared, ref b) => {
                self.check_write(a)?;
                self.check_read(b)?;
            }
            repr::ActionKind::Borrow(ref a, _, repr::BorrowKind::Mut, ref b) => {
                self.check_write(a)?;
                self.check_write(b)?;
            }
            repr::ActionKind::Constraint(_) => {}
            repr::ActionKind::Use(ref p) => {
                self.check_read(p)?;
            }
            repr::ActionKind::Drop(ref p) => {
                self.check_move(p)?;
            }
            repr::ActionKind::StorageDead(p) => {
                self.check_storage_dead(p)?;
            }
            repr::ActionKind::Noop => {}
        }

        Ok(())
    }

    fn check_write(&self, _path: &repr::Path) -> Result<(), Box<Error>> {
        Ok(())
    }

    /// Cannot read from a path `p` if:
    /// - `p` is borrowed mutably;
    /// - some subpath `p.foo` is borrowed mutably;
    /// - some prefix of `p` is borrowed mutably.
    fn check_read(&self, path: &repr::Path) -> Result<(), Box<Error>> {
        let prefixes = path.prefixes();
        for loan in self.loans {
            match loan.kind {
                repr::BorrowKind::Shared => continue,
                repr::BorrowKind::Mut => {}
            }

            for loan_prefix in loan.path.prefixes() {
                if prefixes.contains(&loan_prefix) {
                    return Err(Box::new(BorrowError::for_read(
                        self.point,
                        path,
                        &loan.path,
                        loan.point,
                    )));
                }
            }
        }
        Ok(())
    }

    /// Cannot move from a path `p` if:
    /// - `p` is borrowed;
    /// - some subpath `p.foo` is borrowed;
    /// - some prefix of `p` is borrowed.
    ///
    /// XXX counterexample?
    ///
    /// ```
    /// let p: &i32;
    /// let q = &*p;
    /// move(p); // but this is not actually a *move*, is the point
    /// ```
    fn check_move(&self, path: &repr::Path) -> Result<(), Box<Error>> {
        let prefixes = path.prefixes();
        for loan in self.loans {
            for loan_prefix in loan.path.prefixes() {
                if prefixes.contains(&loan_prefix) {
                    return Err(Box::new(BorrowError::for_move(
                        self.point,
                        path,
                        &loan.path,
                        loan.point,
                    )));
                }
            }
        }
        Ok(())
    }

    /// Cannot free a local variable `var` if:
    /// - data interior to `var` is borrowed.
    ///
    /// In particular, having something like `*var` borrowed is ok.
    fn check_storage_dead(&self, var: repr::Variable) -> Result<(), Box<Error>> {
        for loan in self.loans {
            if let Some(loan_var) = self.invalidated_by_dead_storage(&loan.path) {
                if var == loan_var {
                    return Err(Box::new(BorrowError::for_storage_dead(
                        self.point,
                        var,
                        &loan.path,
                        loan.point,
                    )));
                }
            }
        }
        Ok(())
    }

    /// If `path` is borrowed, returns a vector of paths which -- if
    /// moved or if the storage went away -- would invalidate this
    /// reference.
    fn invalidated_by_dead_storage(&self, mut path: &repr::Path) -> Option<repr::Variable> {
        loop {
            match *path {
                repr::Path::Base(v) => return Some(v),
                repr::Path::Extension(ref base_path, field_name) => {
                    match *self.env.path_ty(base_path) {
                        // If you borrow `*r`, you can drop the
                        // reference `r` without invalidating that
                        // memory.
                        repr::Ty::Ref(_, _, _) => {
                            assert_eq!(field_name, repr::FieldName::star());
                            return None;
                        }

                        repr::Ty::Unit |
                        repr::Ty::Struct(..) => {
                            path = base_path;
                        }

                        repr::Ty::Bound(..) => panic!("unexpected bound type"),
                    }
                }
            }
        }
    }
}

#[derive(Debug)]
pub struct BorrowError {
    description: String,
}

impl BorrowError {
    fn no_error(point: Point) -> Self {
        BorrowError {
            description: format!("point {:?} had no error, but should have", point),
        }
    }

    fn for_move(
        point: Point,
        path: &repr::Path,
        loan_path: &repr::Path,
        loan_point: Point,
    ) -> Self {
        BorrowError {
            description: format!(
                "point {:?} cannot move {:?} because {:?} is borrowed (at point `{:?}`)",
                point,
                path,
                loan_path,
                loan_point
            ),
        }
    }

    fn for_read(
        point: Point,
        path: &repr::Path,
        loan_path: &repr::Path,
        loan_point: Point,
    ) -> Self {
        BorrowError {
            description: format!(
                "point {:?} cannot read {:?} because {:?} is mutably borrowed (at point `{:?}`)",
                point,
                path,
                loan_path,
                loan_point
            ),
        }
    }

    fn for_storage_dead(
        point: Point,
        var: repr::Variable,
        loan_path: &repr::Path,
        loan_point: Point,
    ) -> Self {
        BorrowError {
            description: format!(
                "point {:?} cannot kill storage for {:?} because {:?} is borrowed (at point `{:?}`)",
                point,
                var,
                loan_path,
                loan_point
            ),
        }
    }
}

impl Error for BorrowError {
    fn description(&self) -> &str {
        &self.description
    }

    fn cause(&self) -> Option<&Error> {
        None
    }
}

impl fmt::Display for BorrowError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{}", self.description)
    }
}
<|endoftext|>


<commit_before>//! Functions for computing gradients of image intensities.

use image::{
    GenericImage,
    ImageBuffer,
    Luma
};

use definitions::{
    VecBuffer
};

use filter::{
    filter3x3
};

/// Sobel filter for vertical edges.
static VERTICAL_SOBEL: [i32; 9] = [
    -1, 0, 1,
    -2, 0, 2,
    -1, 0, 1];

/// Sobel filter for horizontal edges.
static HORIZONTAL_SOBEL: [i32; 9] = [
    -1, -2, -1,
     0,  0,  0,
     1,  2,  1];

/// Convolves with the horizontal Sobel kernel to detect horizontal
/// edges in an image.
pub fn horizontal_sobel<I>(image: &I) -> VecBuffer<Luma<i16>>
    where I: GenericImage<Pixel=Luma<u8>> + 'static {

    filter3x3(image, &HORIZONTAL_SOBEL)
}

/// Convolves with the vertical Sobel kernel to detect vertical
/// edges in an image.
pub fn vertical_sobel<I>(image: &I) -> VecBuffer<Luma<i16>>
    where I: GenericImage<Pixel=Luma<u8>> + 'static {

    filter3x3(image, &VERTICAL_SOBEL)
}

/// Returns the magnitudes of gradients in an image.
// TODO: Returns directions as well as magnitudes.
// TODO: Support filtering with allocating a fresh image - filtering functions could
// TODO: take some kind of pixel-sink. This would allow us to compute gradient magnitudes
// TODO: and directions without allocating intermediates for vertical and horizontal gradients.
pub fn sobel_gradients<I>(image: &I) -> VecBuffer<Luma<u16>>
    where I: GenericImage<Pixel=Luma<u8>> + 'static {

    let horizontal = horizontal_sobel(image);
    let vertical = vertical_sobel(image);

    let (width, height) = image.dimensions();
    let mut out = ImageBuffer::new(width, height);

    for y in 0..height {
        for x in 0..width {
            let h = horizontal.get_pixel(x, y)[0] as f32;
            let v = vertical.get_pixel(x, y)[0] as f32;
            let m = (h.powi(2) + v.powi(2)).sqrt() as u16;
            out.put_pixel(x, y, Luma([m]));
        }
    }

    out
}

#[cfg(test)]
mod test {

    use super::{
        horizontal_sobel,
        vertical_sobel
    };

    use image::{
        GrayImage,
        ImageBuffer,
        Luma
    };

    #[test]
    fn test_sobel_constant_image() {

        let image = ImageBuffer::from_fn(5, 5, |_, _| Luma([15u8]));
        let expected = ImageBuffer::from_fn(5, 5, |_, _| Luma([0i16]));
        assert_pixels_eq!(horizontal_sobel(&image), expected);
        assert_pixels_eq!(vertical_sobel(&image), expected);
    }

    #[test]
    fn test_vertical_sobel_gradient_image() {
        let image: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            3, 2, 1,
            6, 5, 4,
            9, 8, 7]).unwrap();

        let expected = ImageBuffer::from_raw(3, 3, vec![
            -4i16, -8i16, -4i16,
            -4i16, -8i16, -4i16,
            -4i16, -8i16, -4i16]).unwrap();

        let filtered = vertical_sobel(&image);
        assert_pixels_eq!(filtered, expected);
    }

    #[test]
    fn test_horizontal_sobel_gradient_image() {
        let image: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            3, 6, 9,
            2, 5, 8,
            1, 4, 7]).unwrap();

        let expected = ImageBuffer::from_raw(3, 3, vec![
            -4i16, -4i16, -4i16,
            -8i16, -8i16, -8i16,
            -4i16, -4i16, -4i16]).unwrap();

        let filtered = horizontal_sobel(&image);
        assert_pixels_eq!(filtered, expected);
    }
}
<commit_msg>Add Prewitt filters<commit_after>//! Functions for computing gradients of image intensities.

use image::{
    GenericImage,
    ImageBuffer,
    Luma
};

use definitions::{
    VecBuffer
};

use filter::{
    filter3x3
};

/// Sobel filter for vertical edges.
static VERTICAL_SOBEL: [i32; 9] = [
    -1, 0, 1,
    -2, 0, 2,
    -1, 0, 1];

/// Sobel filter for horizontal edges.
static HORIZONTAL_SOBEL: [i32; 9] = [
    -1, -2, -1,
     0,  0,  0,
     1,  2,  1];

/// Convolves with the horizontal Sobel kernel to detect horizontal
/// edges in an image.
pub fn horizontal_sobel<I>(image: &I) -> VecBuffer<Luma<i16>>
    where I: GenericImage<Pixel=Luma<u8>> + 'static
{
    filter3x3(image, &HORIZONTAL_SOBEL)
}

/// Convolves with the vertical Sobel kernel to detect vertical
/// edges in an image.
pub fn vertical_sobel<I>(image: &I) -> VecBuffer<Luma<i16>>
    where I: GenericImage<Pixel=Luma<u8>> + 'static
{
    filter3x3(image, &VERTICAL_SOBEL)
}

/// Prewitt filter for vertical edges.
static VERTICAL_PREWITT: [i32; 9] = [
    -1, 0, 1,
    -1, 0, 1,
    -1, 0, 1];

/// Prewitt filter for horizontal edges.
static HORIZONTAL_PREWITT: [i32; 9] = [
    -1, -1, -1,
     0,  0,  0,
     1,  1,  1];

/// Convolves with the horizontal Prewitt kernel to detect horizontal
/// edges in an image.
pub fn horizontal_prewitt<I>(image: &I) -> VecBuffer<Luma<i16>>
    where I: GenericImage<Pixel=Luma<u8>> + 'static
{
    filter3x3(image, &HORIZONTAL_PREWITT)
}

/// Convolves with the vertical Prewitt kernel to detect vertical
/// edges in an image.
pub fn vertical_prewitt<I>(image: &I) -> VecBuffer<Luma<i16>>
    where I: GenericImage<Pixel=Luma<u8>> + 'static
{
    filter3x3(image, &VERTICAL_PREWITT)
}
/// Returns the magnitudes of gradients in an image.
// TODO: Returns directions as well as magnitudes.
// TODO: Support filtering with allocating a fresh image - filtering functions could
// TODO: take some kind of pixel-sink. This would allow us to compute gradient magnitudes
// TODO: and directions without allocating intermediates for vertical and horizontal gradients.
pub fn sobel_gradients<I>(image: &I) -> VecBuffer<Luma<u16>>
    where I: GenericImage<Pixel=Luma<u8>> + 'static {

    let horizontal = horizontal_sobel(image);
    let vertical = vertical_sobel(image);

    let (width, height) = image.dimensions();
    let mut out = ImageBuffer::new(width, height);

    for y in 0..height {
        for x in 0..width {
            let h = horizontal.get_pixel(x, y)[0] as f32;
            let v = vertical.get_pixel(x, y)[0] as f32;
            let m = (h.powi(2) + v.powi(2)).sqrt() as u16;
            out.put_pixel(x, y, Luma([m]));
        }
    }

    out
}

#[cfg(test)]
mod test {

    use super::{
        horizontal_sobel,
        vertical_sobel,
        horizontal_prewitt,
        vertical_prewitt
    };

    use image::{
        GrayImage,
        ImageBuffer,
        Luma
    };

    #[test]
    fn test_gradients_constant_image() {
        let image = ImageBuffer::from_fn(5, 5, |_, _| Luma([15u8]));
        let expected = ImageBuffer::from_fn(5, 5, |_, _| Luma([0i16]));
        assert_pixels_eq!(horizontal_sobel(&image), expected);
        assert_pixels_eq!(vertical_sobel(&image), expected);
        assert_pixels_eq!(horizontal_prewitt(&image), expected);
        assert_pixels_eq!(vertical_prewitt(&image), expected);
    }

    #[test]
    fn test_vertical_sobel_gradient_image() {
        let image: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            3, 2, 1,
            6, 5, 4,
            9, 8, 7]).unwrap();

        let expected = ImageBuffer::from_raw(3, 3, vec![
            -4i16, -8i16, -4i16,
            -4i16, -8i16, -4i16,
            -4i16, -8i16, -4i16]).unwrap();

        let filtered = vertical_sobel(&image);
        assert_pixels_eq!(filtered, expected);
    }

    #[test]
    fn test_horizontal_sobel_gradient_image() {
        let image: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            3, 6, 9,
            2, 5, 8,
            1, 4, 7]).unwrap();

        let expected = ImageBuffer::from_raw(3, 3, vec![
            -4i16, -4i16, -4i16,
            -8i16, -8i16, -8i16,
            -4i16, -4i16, -4i16]).unwrap();

        let filtered = horizontal_sobel(&image);
        assert_pixels_eq!(filtered, expected);
    }

    #[test]
    fn test_vertical_prewitt_gradient_image() {
        let image: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            3, 2, 1,
            6, 5, 4,
            9, 8, 7]).unwrap();

        let expected = ImageBuffer::from_raw(3, 3, vec![
            -4i16, -8i16, -4i16,
            -4i16, -8i16, -4i16,
            -4i16, -8i16, -4i16]).unwrap();

        let filtered = vertical_prewitt(&image);
        assert_pixels_eq!(filtered, expected);
    }

    #[test]
    fn test_horizontal_prewitt_gradient_image() {
        let image: GrayImage = ImageBuffer::from_raw(3, 3, vec![
            3, 6, 9,
            2, 5, 8,
            1, 4, 7]).unwrap();

        let expected = ImageBuffer::from_raw(3, 3, vec![
            -4i16, -4i16, -4i16,
            -8i16, -8i16, -8i16,
            -4i16, -4i16, -4i16]).unwrap();

        let filtered = horizontal_prewitt(&image);
        assert_pixels_eq!(filtered, expected);
    }
}
<|endoftext|>


<commit_before>use super::error::Error as CdsError;
use super::models;

use std::collections::HashMap;

use reqwest::Client as HttpClient;
use serde::de::DeserializeOwned;
use serde::Serialize;

const SESSION_TOKEN_HEADER: &'static str = "Session-Token";

#[derive(Serialize, Deserialize, Default, Debug)]
#[serde(default)]
pub struct Client<'a> {
    pub name: &'a str, //Useful for multi instance to give a name to your instance
    pub host: &'a str,
    pub username: &'a str,
    pub token: &'a str,
    pub insecure_skip_verify_tls: bool,
}

impl<'a> Client<'a> {
    pub fn new<T: Into<&'a str>>(host: T, username: T, token: T) -> Self {
        let host: &'a str = host.into();
        Client {
            host: host,
            username: username.into(),
            token: token.into(),
            insecure_skip_verify_tls: !host.starts_with("https"),
            ..Default::default()
        }
    }

    pub fn status(&self) -> Result<models::Status, CdsError> {
        let body: Vec<u8> = vec![];
        self.stream_json("GET".to_string(), "/mon/status".to_string(), body)
    }

    pub fn config(&self) -> Result<HashMap<String, String>, CdsError> {
        let body: Vec<u8> = vec![];
        self.stream_json("GET".to_string(), "/config/user".to_string(), body)
    }

    pub fn me(&self) -> Result<models::User, CdsError> {
        let body: Vec<u8> = vec![];
        self.stream_json("GET".to_string(), format!("/user/{}", self.username), body)
    }

    pub fn broadcasts(&self) -> Result<Vec<models::Broadcast>, CdsError> {
        let body: Vec<u8> = vec![];
        self.stream_json("GET".to_string(), "/broadcast".to_string(), body)
    }

    pub fn projects(&self) -> Result<Vec<models::Project>, CdsError> {
        let body: Vec<u8> = vec![];
        self.stream_json("GET".to_string(), "/project".to_string(), body)
    }

    pub fn applications(&self, project_key: &str) -> Result<Vec<models::Application>, CdsError> {
        let body: Vec<u8> = vec![];
        self.stream_json(
            "GET".to_string(),
            format!("/project/{}/applications", project_key),
            body,
        )
    }

    pub fn application(
        &self,
        project_key: &str,
        application_name: &str,
    ) -> Result<models::Application, CdsError> {
        let body: Vec<u8> = vec![];
        self.stream_json(
            "GET".to_string(),
            format!("/project/{}/application/{}", project_key, application_name),
            body,
        )
    }

    pub fn workflows(&self, project_key: &str) -> Result<Vec<models::Workflow>, CdsError> {
        let body: Vec<u8> = vec![];
        self.stream_json(
            "GET".to_string(),
            format!("/project/{}/workflows", project_key),
            body,
        )
    }

    pub fn workflow(
        &self,
        project_key: &str,
        workflow_name: &str,
    ) -> Result<models::Workflow, CdsError> {
        let body: Vec<u8> = vec![];
        self.stream_json(
            "GET".to_string(),
            format!("/project/{}/workflows/{}", project_key, workflow_name),
            body,
        )
    }

    pub fn queue_count(&self) -> Result<models::QueueCount, CdsError> {
        let body: Vec<u8> = vec![];
        self.stream_json(
            "GET".to_string(),
            "/queue/workflows/count".to_string(),
            body,
        )
    }

    pub fn bookmarks(&self) -> Result<Vec<models::Bookmark>, CdsError> {
        let body: Vec<u8> = vec![];
        self.stream_json("GET".to_string(), String::from("/bookmarks"), body)
    }

    pub fn last_run(
        &self,
        project_key: &str,
        workflow_name: &str,
    ) -> Result<models::WorkflowRun, CdsError> {
        let body: Vec<u8> = vec![];
        self.stream_json(
            "GET".to_string(),
            format!(
                "/project/{}/workflows/{}/runs/latest",
                project_key, workflow_name
            ),
            body,
        )
    }

    pub fn stream_json<T: Serialize, U: DeserializeOwned>(
        &self,
        method: String,
        path: String,
        body: T,
    ) -> Result<U, CdsError> {
        let url = format!("{}{}", self.host, path);
        let mut resp_http = HttpClient::new()
            .request(reqwest::Method::from_bytes(method.as_bytes())?, &url)
            .header(reqwest::header::CONTENT_TYPE, "application/json")
            .header(SESSION_TOKEN_HEADER, self.token)
            .basic_auth(self.username, Some(self.token))
            .json(&body)
            .send()?;

        if resp_http.status().as_u16() > 400u16 {
            let mut err: CdsError = resp_http.json().map_err(CdsError::from)?;
            err.status = resp_http.status().as_u16();
            return Err(err);
        }

        resp_http.json().map_err(CdsError::from)
    }
}
<commit_msg>fix(sdk): add user agent in rust sdk<commit_after>use super::error::Error as CdsError;
use super::models;

use std::collections::HashMap;

use reqwest::Client as HttpClient;
use serde::de::DeserializeOwned;
use serde::Serialize;

const SESSION_TOKEN_HEADER: &'static str = "Session-Token";

#[derive(Serialize, Deserialize, Default, Debug)]
#[serde(default)]
pub struct Client<'a> {
    pub name: &'a str, //Useful for multi instance to give a name to your instance
    pub host: &'a str,
    pub username: &'a str,
    pub token: &'a str,
    pub insecure_skip_verify_tls: bool,
}

impl<'a> Client<'a> {
    pub fn new<T: Into<&'a str>>(host: T, username: T, token: T) -> Self {
        let host: &'a str = host.into();
        Client {
            host: host,
            username: username.into(),
            token: token.into(),
            insecure_skip_verify_tls: !host.starts_with("https"),
            ..Default::default()
        }
    }

    pub fn status(&self) -> Result<models::Status, CdsError> {
        let body: Vec<u8> = vec![];
        self.stream_json("GET".to_string(), "/mon/status".to_string(), body)
    }

    pub fn config(&self) -> Result<HashMap<String, String>, CdsError> {
        let body: Vec<u8> = vec![];
        self.stream_json("GET".to_string(), "/config/user".to_string(), body)
    }

    pub fn me(&self) -> Result<models::User, CdsError> {
        let body: Vec<u8> = vec![];
        self.stream_json("GET".to_string(), format!("/user/{}", self.username), body)
    }

    pub fn broadcasts(&self) -> Result<Vec<models::Broadcast>, CdsError> {
        let body: Vec<u8> = vec![];
        self.stream_json("GET".to_string(), "/broadcast".to_string(), body)
    }

    pub fn projects(&self) -> Result<Vec<models::Project>, CdsError> {
        let body: Vec<u8> = vec![];
        self.stream_json("GET".to_string(), "/project".to_string(), body)
    }

    pub fn applications(&self, project_key: &str) -> Result<Vec<models::Application>, CdsError> {
        let body: Vec<u8> = vec![];
        self.stream_json(
            "GET".to_string(),
            format!("/project/{}/applications", project_key),
            body,
        )
    }

    pub fn application(
        &self,
        project_key: &str,
        application_name: &str,
    ) -> Result<models::Application, CdsError> {
        let body: Vec<u8> = vec![];
        self.stream_json(
            "GET".to_string(),
            format!("/project/{}/application/{}", project_key, application_name),
            body,
        )
    }

    pub fn workflows(&self, project_key: &str) -> Result<Vec<models::Workflow>, CdsError> {
        let body: Vec<u8> = vec![];
        self.stream_json(
            "GET".to_string(),
            format!("/project/{}/workflows", project_key),
            body,
        )
    }

    pub fn workflow(
        &self,
        project_key: &str,
        workflow_name: &str,
    ) -> Result<models::Workflow, CdsError> {
        let body: Vec<u8> = vec![];
        self.stream_json(
            "GET".to_string(),
            format!("/project/{}/workflows/{}", project_key, workflow_name),
            body,
        )
    }

    pub fn queue_count(&self) -> Result<models::QueueCount, CdsError> {
        let body: Vec<u8> = vec![];
        self.stream_json(
            "GET".to_string(),
            "/queue/workflows/count".to_string(),
            body,
        )
    }

    pub fn bookmarks(&self) -> Result<Vec<models::Bookmark>, CdsError> {
        let body: Vec<u8> = vec![];
        self.stream_json("GET".to_string(), String::from("/bookmarks"), body)
    }

    pub fn last_run(
        &self,
        project_key: &str,
        workflow_name: &str,
    ) -> Result<models::WorkflowRun, CdsError> {
        let body: Vec<u8> = vec![];
        self.stream_json(
            "GET".to_string(),
            format!(
                "/project/{}/workflows/{}/runs/latest",
                project_key, workflow_name
            ),
            body,
        )
    }

    pub fn stream_json<T: Serialize, U: DeserializeOwned>(
        &self,
        method: String,
        path: String,
        body: T,
    ) -> Result<U, CdsError> {
        let url = format!("{}{}", self.host, path);
        let mut resp_http = HttpClient::new()
            .request(reqwest::Method::from_bytes(method.as_bytes())?, &url)
            .header(reqwest::header::CONTENT_TYPE, "application/json")
            .header(reqwest::header::USER_AGENT, "CDS/sdk")
            .header(SESSION_TOKEN_HEADER, self.token)
            .basic_auth(self.username, Some(self.token))
            .json(&body)
            .send()?;

        if resp_http.status().as_u16() > 400u16 {
            let mut err: CdsError = resp_http.json().map_err(CdsError::from)?;
            err.status = resp_http.status().as_u16();
            return Err(err);
        }

        resp_http.json().map_err(CdsError::from)
    }
}
<|endoftext|>


<commit_before>use futures_core::{Poll, Async, Stream};
use futures_core::task;

/// A stream which emits single element and then EOF.
///
/// This stream will never block and is always ready.
#[derive(Debug)]
#[must_use = "streams do nothing unless polled"]
pub struct Once<T, E>(Option<Result<T, E>>);

/// Creates a stream of single element
///
/// ```rust
/// # extern crate futures;
/// # extern crate futures_executor;
/// use futures::prelude::*;
/// use futures::stream;
/// use futures_executor::block_on;
///
/// # fn main() {
/// let mut stream = stream::once::<(), _>(Err(17));
/// let collected: Result<Vec<_>, _> = block_on(stream.collect());
/// assert_eq!(collected, Err(17));
///
/// let mut stream = stream::once::<_, ()>(Ok(92));
/// let collected: Result<Vec<_>, _> = block_on(stream.collect());
/// assert_eq!(collected, Ok(vec![92]));
/// # }
/// ```
pub fn once<T, E>(item: Result<T, E>) -> Once<T, E> {
    Once(Some(item))
}

impl<T, E> Stream for Once<T, E> {
    type Item = T;
    type Error = E;

    fn poll_next(&mut self, _: &mut task::Context) -> Poll<Option<T>, E> {
        match self.0.take() {
            Some(Ok(e)) => Ok(Async::Ready(Some(e))),
            Some(Err(e)) => Err(e),
            None => Ok(Async::Ready(None)),
        }
    }
}
<commit_msg>Generalize `once` to use `IntoFuture`<commit_after>use futures_core::{Poll, Async, Stream, IntoFuture, Future};
use futures_core::task;

/// A stream which emits single element and then EOF.
///
/// This stream will never block and is always ready.
#[derive(Debug)]
#[must_use = "streams do nothing unless polled"]
pub struct Once<F>(Option<F>);

/// Creates a stream of single element
///
/// ```rust
/// # extern crate futures;
/// # extern crate futures_executor;
/// use futures::prelude::*;
/// use futures::stream;
/// use futures_executor::block_on;
///
/// # fn main() {
/// let mut stream = stream::once::<Result<(), _>>(Err(17));
/// let collected: Result<Vec<_>, _> = block_on(stream.collect());
/// assert_eq!(collected, Err(17));
///
/// let mut stream = stream::once::<Result<_, ()>>(Ok(92));
/// let collected: Result<Vec<_>, _> = block_on(stream.collect());
/// assert_eq!(collected, Ok(vec![92]));
/// # }
/// ```
pub fn once<F: IntoFuture>(item: F) -> Once<F::Future> {
    Once(Some(item.into_future()))
}

impl<F: Future> Stream for Once<F> {
    type Item = F::Item;
    type Error = F::Error;

    fn poll_next(&mut self, cx: &mut task::Context) -> Poll<Option<F::Item>, F::Error> {
        if let Some(mut f) = self.0.take() {
            match f.poll(cx)? {
                Async::Ready(x) => Ok(Async::Ready(Some(x))),
                Async::Pending => {
                    self.0 = Some(f);
                    Ok(Async::Pending)
                }
            }
        } else {
            Ok(Async::Ready(None))
        }
    }
}
<|endoftext|>


<commit_before>use diesel;
use diesel::prelude::*;
use rocket_contrib::{JSON, SerdeError};
use uuid::Uuid;

use data::types::*;
use data::researches::get_research_list;
use data::helper::{get_research_dependency_strings, dependencies_fulfilled};

use helpers::db::DB;
use responses::{APIResponse, bad_request, created, ok};

use models::pod::Pod;
use models::user::User;
use models::research::Research;
use models::resource::Resource;

use schema::queue_entries;
use schema::pods::dsl as pods_dsl;
use schema::queues::dsl as queues_dsl;
use schema::resources::dsl as resources_dsl;
use schema::researches::dsl as research_dsl;
use schema::queue_entries::dsl as queue_entries_dsl;

use models::queue::{QueueEntry, Queue, NewQueueEntry};
use validation::queue::ResearchSerializer;

/// The user needs to be logged in to access this route!
///
/// This route returns the list of all researches and their levels/costs,
/// as well as the current level of the research for the pod of the current user.
#[get("/pod")]
pub fn get_researches(current_user: User, db: DB) -> APIResponse {

    let mut research_list = get_research_list();
    // Create changed pod model and push it to the DB

    let pod = pods_dsl::pods
        .filter(pods_dsl::user_id.eq(current_user.id))
        .get_result::<Pod>(&*db)
        .expect("Failed to get user pod.");

    let pod_result = research_dsl::researches
        .filter(research_dsl::pod_id.eq(pod.id))
        .get_results::<Research>(&*db);

    if pod_result.is_ok() {
        let researches = pod_result.unwrap();
        for research in researches {
            let type_result = ResearchTypes::from_string(&research.name);
            if type_result.is_err() {
                return bad_request()
                           .message(format!("Found research {}, but no matching ResearchType!",
                                            research.name)
                                            .as_str());
            }
            let research_type = type_result.unwrap();
            let list_result = research_list.get_mut(&research_type);
            if list_result.is_none() {
                return bad_request().message(format!("Found type {}, but no matching entry in our research list!", research_type).as_str());
            }

            let mut list_entry = list_result.unwrap();
            list_entry.current_level = research.level;
        }
    }

    ok().message("Research data.").data(json!(&research_list))
}

/// Add a new research to the queue of the pod
/// This endpoint:
/// - Checks if dependencies for research are fulfilled
/// - Checks if there are enough resources
/// - Removes resources from db
#[post("/pod", data = "<request_data>", format = "application/json")]
pub fn start_research(request_data: Result<JSON<ResearchSerializer>, SerdeError>,
                          current_user: User,
                          db: DB)
                          -> APIResponse {

    // Unwrap or return specific error if invalid JSON has been sent.
    if let Err(error) = request_data {
        return bad_request().message(format!("{}", error).as_str());
    };
    let research_data = request_data.unwrap();

    // Check if the given research name maps to a research type.
    let research_result = ResearchTypes::from_string(&research_data.research_name);

    // Early return if we don't know this research name
    if research_result.is_err() {
        return bad_request().message(format!("No such research type `{}`",
                                             research_data.research_name)
                                             .as_str());
    }
    let research_type =  research_result.unwrap();
    let dependency_strings = get_research_dependency_strings(&research_type);
    let mut research_level: i32;
    let research_list = get_research_list();
    // Get pod and queue from db
    let pod = pods_dsl::pods
        .filter(pods_dsl::user_id.eq(current_user.id))
        .first::<Pod>(&*db)
        .unwrap();

    let research = research_dsl::researches
        .filter(research_dsl::pod_id.eq(pod.id))
        .filter(research_dsl::name.eq(research_type.to_string()))
        .get_result::<Research>(&*db);

    match research {
        // Research exists, we don't need to check for dependencies
        // We just increase the level by 1
        Ok(research) => {
            research_level = research.level + 1;
        }
        // The research is not yet here. We need to check for dependencies.
        // We just increase the level by 1
        Err(_) => {
            let dependencies = research_dsl::researches
                .filter(research_dsl::name.eq_any(dependency_strings))
                .get_results::<Research>(&*db);

            let fulfilled = dependencies_fulfilled(&research_type,
                                                   dependencies,
                                                   &research_list);
            if !fulfilled {
                return bad_request().message("Dependencies not fulfilled.");
            }
            research_level = 1;
        }
    }

    let queue = queues_dsl::queues
        .filter(queues_dsl::pod_id.eq(pod.id))
        .first::<Queue>(&*db)
        .unwrap();

    // Check if there already are existing queue entries for this research.
    // In case there are, we increase the level by the amount of existing entries.
    let existing_entries: i64 = queue_entries_dsl::queue_entries
        .count()
        .filter(queue_entries_dsl::queue_id.eq(queue.id))
        .filter(queue_entries_dsl::research_name.eq(research_type.to_string()))
        .get_result(&*db)
        .unwrap_or(0);

    let pod_resources = resources_dsl::resources
        .filter(resources_dsl::pod_id.eq(pod.id))
        .get_results(&*db)
        .expect("Failed to get user resources.");

    research_level += existing_entries as i32;

    let all_levels = &research_list
                          .get(&research_type)
                          .as_ref()
                          .expect("No research in yml for this type.")
                          .levels;

    if !(all_levels.len() <= research_level as usize) {
        return bad_request().message("Already at max level.");
    }
    let costs = &all_levels[research_level as usize].resources;
    if costs.is_some() && !Resource::check_resources(costs, pod_resources, &db) {
        return bad_request().message("Insufficient resources.");
    }

    // Create a new queue entry with the given research type.
    let new_entry_model = NewQueueEntry {
        queue_id: queue.id.clone(),
        research_name: Some(research_data.research_name.clone()),
        module_name: None,
        module_id: None,
        level: research_level,
    };

    let new_queue_entry = diesel::insert(&new_entry_model)
        .into(queue_entries::table)
        .get_result::<QueueEntry>(&*db)
        .expect("Failed to insert new queue entry.");

    created()
        .message("Queue entry added.")
        .data(json!(&new_queue_entry))
}

/// Remove research from queue
#[delete("/pod/<entry_uuid>")]
pub fn stop_research(entry_uuid: &str, current_user: User, db: DB) -> APIResponse {

    // Parse and check if we got a valid id
    let result = Uuid::parse_str(entry_uuid);
    if result.is_err() {
        return bad_request().message("Got an invalid uuid");
    }
    let queue_entry_id = result.unwrap();

    // Get the queue entry
    let queue_entry_result = queue_entries_dsl::queue_entries
        .filter(queue_entries_dsl::id.eq(queue_entry_id))
        .first::<QueueEntry>(&*db);
    if queue_entry_result.is_err() {
        return bad_request().message("No queue entry with this id.");
    }
    let queue_entry = queue_entry_result.unwrap();

    // Check if we got an research queue entry
    if queue_entry.research_name.is_none() {
        return bad_request().message("Queue entry is a model queue entry.");
    }

    // Check if there already are existing queue entries for this research.
    // If there are entries with a higher level, we return a bad request.
    let level = queue_entry.level;
    let name = queue_entry.research_name.unwrap();
    let higher_entry = queue_entries_dsl::queue_entries
        .filter(queue_entries_dsl::queue_id.eq(queue_entry.queue_id))
        .filter(queue_entries_dsl::research_name.eq(&name))
        .filter(queue_entries_dsl::level.gt(queue_entry.level))
        .get_result::<QueueEntry>(&*db);
    if higher_entry.is_ok() {
        return bad_request().message("Can't delete. There is an queue entry with a higher level for this research.");
    }

    // Get all needed info for resource manipulation
    let research_list = get_research_list();

    let pod = pods_dsl::pods
        .filter(pods_dsl::user_id.eq(current_user.id))
        .first::<Pod>(&*db)
        .unwrap();

    let pod_resources = resources_dsl::resources
        .filter(resources_dsl::pod_id.eq(pod.id))
        .get_results::<Resource>(&*db)
        .expect("Failed to get user resources.");

    // Add resources from research to pod resources
    let all_levels = &research_list
                          .get(&ResearchTypes::from_string(&name).unwrap())
                          .unwrap()
                          .levels;
    let costs_result = &all_levels[level as usize].resources;

    if let Some(ref costs) = *costs_result {
        Resource::update_resources(costs, pod_resources, false, &db);
    }

    // Remove queue_entry from database
    diesel::delete(queue_entries_dsl::queue_entries
                       .filter(queue_entries_dsl::id.eq(queue_entry_id)))
            .execute(&*db)
            .expect("Failed to remove queue_entry.");

    ok().message("Resource removed.")
}
<commit_msg>Refactor stop research<commit_after>use diesel;
use diesel::prelude::*;
use rocket_contrib::{JSON, SerdeError};

use data::types::*;
use data::researches::get_research_list;
use data::helper::{get_research_dependency_strings, dependencies_fulfilled};

use helpers::db::DB;
use responses::{APIResponse, bad_request, created, ok};

use models::pod::Pod;
use models::user::User;
use models::research::Research;
use models::resource::Resource;

use schema::queue_entries;
use schema::pods::dsl as pods_dsl;
use schema::queues::dsl as queues_dsl;
use schema::resources::dsl as resources_dsl;
use schema::researches::dsl as research_dsl;
use schema::queue_entries::dsl as queue_entries_dsl;

use models::queue::{QueueEntry, Queue, NewQueueEntry};
use validation::queue::ResearchSerializer;

/// The user needs to be logged in to access this route!
///
/// This route returns the list of all researches and their levels/costs,
/// as well as the current level of the research for the pod of the current user.
#[get("/pod")]
pub fn get_researches(current_user: User, db: DB) -> APIResponse {

    let mut research_list = get_research_list();
    // Create changed pod model and push it to the DB

    let pod = pods_dsl::pods
        .filter(pods_dsl::user_id.eq(current_user.id))
        .get_result::<Pod>(&*db)
        .expect("Failed to get user pod.");

    let pod_result = research_dsl::researches
        .filter(research_dsl::pod_id.eq(pod.id))
        .get_results::<Research>(&*db);

    if pod_result.is_ok() {
        let researches = pod_result.unwrap();
        for research in researches {
            let type_result = ResearchTypes::from_string(&research.name);
            if type_result.is_err() {
                return bad_request()
                           .message(format!("Found research {}, but no matching ResearchType!",
                                            research.name)
                                            .as_str());
            }
            let research_type = type_result.unwrap();
            let list_result = research_list.get_mut(&research_type);
            if list_result.is_none() {
                return bad_request().message(format!("Found type {}, but no matching entry in our research list!", research_type).as_str());
            }

            let mut list_entry = list_result.unwrap();
            list_entry.current_level = research.level;
        }
    }

    ok().message("Research data.").data(json!(&research_list))
}

/// Add a new research to the queue of the pod
/// This endpoint:
/// - Checks if dependencies for research are fulfilled
/// - Checks if there are enough resources
/// - Removes resources from db
#[post("/pod", data = "<request_data>", format = "application/json")]
pub fn start_research(request_data: Result<JSON<ResearchSerializer>, SerdeError>,
                          current_user: User,
                          db: DB)
                          -> APIResponse {

    // Unwrap or return specific error if invalid JSON has been sent.
    if let Err(error) = request_data {
        return bad_request().message(format!("{}", error).as_str());
    };
    let research_data = request_data.unwrap();

    // Check if the given research name maps to a research type.
    let research_result = ResearchTypes::from_string(&research_data.research_name);

    // Early return if we don't know this research name
    if research_result.is_err() {
        return bad_request().message(format!("No such research type `{}`",
                                             research_data.research_name)
                                             .as_str());
    }
    let research_type =  research_result.unwrap();
    let dependency_strings = get_research_dependency_strings(&research_type);
    let mut research_level: i32;
    let research_list = get_research_list();
    // Get pod and queue from db
    let pod = pods_dsl::pods
        .filter(pods_dsl::user_id.eq(current_user.id))
        .first::<Pod>(&*db)
        .unwrap();

    let research = research_dsl::researches
        .filter(research_dsl::pod_id.eq(pod.id))
        .filter(research_dsl::name.eq(research_type.to_string()))
        .get_result::<Research>(&*db);

    match research {
        // Research exists, we don't need to check for dependencies
        // We just increase the level by 1
        Ok(research) => {
            research_level = research.level + 1;
        }
        // The research is not yet here. We need to check for dependencies.
        // We just increase the level by 1
        Err(_) => {
            let dependencies = research_dsl::researches
                .filter(research_dsl::name.eq_any(dependency_strings))
                .get_results::<Research>(&*db);

            let fulfilled = dependencies_fulfilled(&research_type,
                                                   dependencies,
                                                   &research_list);
            if !fulfilled {
                return bad_request().message("Dependencies not fulfilled.");
            }
            research_level = 1;
        }
    }

    let queue = queues_dsl::queues
        .filter(queues_dsl::pod_id.eq(pod.id))
        .first::<Queue>(&*db)
        .unwrap();

    // Check if there already are existing queue entries for this research.
    // In case there are, we increase the level by the amount of existing entries.
    let existing_entries: i64 = queue_entries_dsl::queue_entries
        .count()
        .filter(queue_entries_dsl::queue_id.eq(queue.id))
        .filter(queue_entries_dsl::research_name.eq(research_type.to_string()))
        .get_result(&*db)
        .unwrap_or(0);

    let pod_resources = resources_dsl::resources
        .filter(resources_dsl::pod_id.eq(pod.id))
        .get_results(&*db)
        .expect("Failed to get user resources.");

    research_level += existing_entries as i32;

    let all_levels = &research_list
                          .get(&research_type)
                          .as_ref()
                          .expect("No research in yml for this type.")
                          .levels;

    if !(all_levels.len() <= research_level as usize) {
        return bad_request().message("Already at max level.");
    }
    let costs = &all_levels[research_level as usize].resources;
    if costs.is_some() && !Resource::check_resources(costs, pod_resources, &db) {
        return bad_request().message("Insufficient resources.");
    }

    // Create a new queue entry with the given research type.
    let new_entry_model = NewQueueEntry {
        queue_id: queue.id.clone(),
        research_name: Some(research_data.research_name.clone()),
        module_name: None,
        module_id: None,
        level: research_level,
    };

    let new_queue_entry = diesel::insert(&new_entry_model)
        .into(queue_entries::table)
        .get_result::<QueueEntry>(&*db)
        .expect("Failed to insert new queue entry.");

    created()
        .message("Queue entry added.")
        .data(json!(&new_queue_entry))
}

/// Remove research from queue
#[delete("/pod/<research_name>")]
pub fn stop_research(research_name: &str, current_user: User, db: DB) -> APIResponse {

    // Check if there is a research for this research_name
    let research_result = ResearchTypes::from_str(research_name);
    // Early return if we don't know this research name
    if research_result.is_err() {
        return bad_request().message(format!("No such research type `{}`", research_name).as_str());
    }

    // Get user pod and pod queue
    let pod = pods_dsl::pods
        .filter(pods_dsl::user_id.eq(current_user.id))
        .first::<Pod>(&*db)
        .unwrap();
    let queue = queues_dsl::queues
        .filter(queues_dsl::pod_id.eq(pod.id))
        .first::<Queue>(&*db)
        .unwrap();

    // Check if there exists a queue entry for this research and this pod.
    // Early return if this isn't the case.
    let research_entry_result = queue_entries_dsl::queue_entries
        .filter(queue_entries_dsl::queue_id.eq(queue.id))
        .filter(queue_entries_dsl::research_name.eq(research_name))
        .order(queue_entries_dsl::level.desc())
        .get_result::<QueueEntry>(&*db);
    if research_entry_result.is_ok() {
        return bad_request().message("Can't delete. There is no queue entry for this research.");
    }
    let research_entry = research_entry_result.unwrap();

    // Get all needed info for resource manipulation
    let research_list = get_research_list();

    let pod_resources = resources_dsl::resources
        .filter(resources_dsl::pod_id.eq(pod.id))
        .get_results::<Resource>(&*db)
        .expect("Failed to get user resources.");

    // Add resources from research to pod resources
    let all_levels = &research_list
                          .get(&ResearchTypes::from_str(research_name).unwrap())
                          .unwrap()
                          .levels;
    let costs_result = &all_levels[research_entry.level as usize].resources;

    if let Some(ref costs) = *costs_result {
        Resource::update_resources(costs, pod_resources, false, &db);
    }

    // Remove queue_entry from database
    diesel::delete(queue_entries_dsl::queue_entries
                       .filter(queue_entries_dsl::id.eq(research_entry.id)))
            .execute(&*db)
            .expect("Failed to remove queue_entry.");

    ok().message("Resource removed.")
}
<|endoftext|>