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

#![allow(clippy::default_hash_types)]

use itertools::Itertools;
use lazy_static::lazy_static;
use regex::Regex;
use walkdir::WalkDir;
use std::collections::HashMap;
use std::ffi::OsStr;
use std::fs;
use std::io::prelude::*;

lazy_static! {
    static ref DEC_CLIPPY_LINT_RE: Regex = Regex::new(r#"(?x)
        declare_clippy_lint!\s*[\{(]\s*
        pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
        (?P<cat>[a-z_]+)\s*,\s*
        "(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
    "#).unwrap();
    static ref DEC_DEPRECATED_LINT_RE: Regex = Regex::new(r#"(?x)
        declare_deprecated_lint!\s*[{(]\s*
        pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
        "(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
    "#).unwrap();
    static ref NL_ESCAPE_RE: Regex = Regex::new(r#"\\\n\s*"#).unwrap();
    pub static ref DOCS_LINK: String = "https://rust-lang-nursery.github.io/rust-clippy/master/index.html".to_string();
}

/// Lint data parsed from the Clippy source code.
#[derive(Clone, PartialEq, Debug)]
pub struct Lint {
    pub name: String,
    pub group: String,
    pub desc: String,
    pub deprecation: Option<String>,
    pub module: String,
}

impl Lint {
    pub fn new(name: &str, group: &str, desc: &str, deprecation: Option<&str>, module: &str) -> Self {
        Self {
            name: name.to_lowercase(),
            group: group.to_string(),
            desc: NL_ESCAPE_RE.replace(&desc.replace("\\\"", "\""), "").to_string(),
            deprecation: deprecation.map(|d| d.to_string()),
            module: module.to_string(),
        }
    }

    /// Returns all non-deprecated lints
    pub fn active_lints(lints: impl Iterator<Item=Self>) -> impl Iterator<Item=Self> {
        lints.filter(|l| l.deprecation.is_none())
    }

    /// Returns the lints in a HashMap, grouped by the different lint groups
    pub fn by_lint_group(lints: &[Self]) -> HashMap<String, Vec<Self>> {
        lints.iter().map(|lint| (lint.group.to_string(), lint.clone())).into_group_map()
    }
}

/// Gathers all files in `src/clippy_lints` and gathers all lints inside
pub fn gather_all() -> impl Iterator<Item=Lint> {
    lint_files().flat_map(|f| gather_from_file(&f))
}

fn gather_from_file(dir_entry: &walkdir::DirEntry) -> impl Iterator<Item=Lint> {
    let mut file = fs::File::open(dir_entry.path()).unwrap();
    let mut content = String::new();
    file.read_to_string(&mut content).unwrap();
    parse_contents(&content, dir_entry.path().file_stem().unwrap().to_str().unwrap())
}

fn parse_contents(content: &str, filename: &str) -> impl Iterator<Item=Lint> {
    let lints = DEC_CLIPPY_LINT_RE
        .captures_iter(content)
        .map(|m| Lint::new(&m["name"], &m["cat"], &m["desc"], None, filename));
    let deprecated = DEC_DEPRECATED_LINT_RE
        .captures_iter(content)
        .map(|m| Lint::new( &m["name"], "Deprecated", &m["desc"], Some(&m["desc"]), filename));
    // Removing the `.collect::<Vec<Lint>>().into_iter()` causes some lifetime issues due to the map
    lints.chain(deprecated).collect::<Vec<Lint>>().into_iter()
}

/// Collects all .rs files in the `clippy_lints/src` directory
fn lint_files() -> impl Iterator<Item=walkdir::DirEntry> {
    WalkDir::new("../clippy_lints/src")
        .into_iter()
        .filter_map(|f| f.ok())
        .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
}

#[test]
fn test_parse_contents() {
    let result: Vec<Lint> = parse_contents(
        r#"
declare_clippy_lint! {
    pub PTR_ARG,
    style,
    "really long \
     text"
}

declare_clippy_lint!{
    pub DOC_MARKDOWN,
    pedantic,
    "single line"
}

/// some doc comment
declare_deprecated_lint! {
    pub SHOULD_ASSERT_EQ,
    "`assert!()` will be more flexible with RFC 2011"
}
    "#,
    "module_name").collect();

    let expected = vec![
        Lint::new("ptr_arg", "style", "really long text", None, "module_name"),
        Lint::new("doc_markdown", "pedantic", "single line", None, "module_name"),
        Lint::new(
            "should_assert_eq",
            "Deprecated",
            "`assert!()` will be more flexible with RFC 2011",
            Some("`assert!()` will be more flexible with RFC 2011"),
            "module_name"
        ),
    ];
    assert_eq!(expected, result);
}

#[test]
fn test_active_lints() {
    let lints = vec![
        Lint::new("should_assert_eq", "Deprecated", "abc", Some("Reason"), "module_name"),
        Lint::new("should_assert_eq2", "Not Deprecated", "abc", None, "module_name")
    ];
    let expected = vec![
        Lint::new("should_assert_eq2", "Not Deprecated", "abc", None, "module_name")
    ];
    assert_eq!(expected, Lint::active_lints(lints.into_iter()).collect::<Vec<Lint>>());
}

#[test]
fn test_by_lint_group() {
    let lints = vec![
        Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
        Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"),
        Lint::new("incorrect_match", "group1", "abc", None, "module_name"),
    ];
    let mut expected: HashMap<String, Vec<Lint>> = HashMap::new();
    expected.insert("group1".to_string(), vec![
        Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
        Lint::new("incorrect_match", "group1", "abc", None, "module_name"),
    ]);
    expected.insert("group2".to_string(), vec![
        Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")
    ]);
    assert_eq!(expected, Lint::by_lint_group(&lints));
}
<commit_msg>Add comment on WalkDir vs. fs::read_dir<commit_after>// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![allow(clippy::default_hash_types)]

use itertools::Itertools;
use lazy_static::lazy_static;
use regex::Regex;
use walkdir::WalkDir;
use std::collections::HashMap;
use std::ffi::OsStr;
use std::fs;
use std::io::prelude::*;

lazy_static! {
    static ref DEC_CLIPPY_LINT_RE: Regex = Regex::new(r#"(?x)
        declare_clippy_lint!\s*[\{(]\s*
        pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
        (?P<cat>[a-z_]+)\s*,\s*
        "(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
    "#).unwrap();
    static ref DEC_DEPRECATED_LINT_RE: Regex = Regex::new(r#"(?x)
        declare_deprecated_lint!\s*[{(]\s*
        pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
        "(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
    "#).unwrap();
    static ref NL_ESCAPE_RE: Regex = Regex::new(r#"\\\n\s*"#).unwrap();
    pub static ref DOCS_LINK: String = "https://rust-lang-nursery.github.io/rust-clippy/master/index.html".to_string();
}

/// Lint data parsed from the Clippy source code.
#[derive(Clone, PartialEq, Debug)]
pub struct Lint {
    pub name: String,
    pub group: String,
    pub desc: String,
    pub deprecation: Option<String>,
    pub module: String,
}

impl Lint {
    pub fn new(name: &str, group: &str, desc: &str, deprecation: Option<&str>, module: &str) -> Self {
        Self {
            name: name.to_lowercase(),
            group: group.to_string(),
            desc: NL_ESCAPE_RE.replace(&desc.replace("\\\"", "\""), "").to_string(),
            deprecation: deprecation.map(|d| d.to_string()),
            module: module.to_string(),
        }
    }

    /// Returns all non-deprecated lints
    pub fn active_lints(lints: impl Iterator<Item=Self>) -> impl Iterator<Item=Self> {
        lints.filter(|l| l.deprecation.is_none())
    }

    /// Returns the lints in a HashMap, grouped by the different lint groups
    pub fn by_lint_group(lints: &[Self]) -> HashMap<String, Vec<Self>> {
        lints.iter().map(|lint| (lint.group.to_string(), lint.clone())).into_group_map()
    }
}

/// Gathers all files in `src/clippy_lints` and gathers all lints inside
pub fn gather_all() -> impl Iterator<Item=Lint> {
    lint_files().flat_map(|f| gather_from_file(&f))
}

fn gather_from_file(dir_entry: &walkdir::DirEntry) -> impl Iterator<Item=Lint> {
    let mut file = fs::File::open(dir_entry.path()).unwrap();
    let mut content = String::new();
    file.read_to_string(&mut content).unwrap();
    parse_contents(&content, dir_entry.path().file_stem().unwrap().to_str().unwrap())
}

fn parse_contents(content: &str, filename: &str) -> impl Iterator<Item=Lint> {
    let lints = DEC_CLIPPY_LINT_RE
        .captures_iter(content)
        .map(|m| Lint::new(&m["name"], &m["cat"], &m["desc"], None, filename));
    let deprecated = DEC_DEPRECATED_LINT_RE
        .captures_iter(content)
        .map(|m| Lint::new( &m["name"], "Deprecated", &m["desc"], Some(&m["desc"]), filename));
    // Removing the `.collect::<Vec<Lint>>().into_iter()` causes some lifetime issues due to the map
    lints.chain(deprecated).collect::<Vec<Lint>>().into_iter()
}

/// Collects all .rs files in the `clippy_lints/src` directory
fn lint_files() -> impl Iterator<Item=walkdir::DirEntry> {
    // We use `WalkDir` instead of `fs::read_dir` here in order to recurse into subdirectories.
    // Otherwise we would not collect all the lints, for example in `clippy_lints/src/methods/`.
    WalkDir::new("../clippy_lints/src")
        .into_iter()
        .filter_map(|f| f.ok())
        .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
}

#[test]
fn test_parse_contents() {
    let result: Vec<Lint> = parse_contents(
        r#"
declare_clippy_lint! {
    pub PTR_ARG,
    style,
    "really long \
     text"
}

declare_clippy_lint!{
    pub DOC_MARKDOWN,
    pedantic,
    "single line"
}

/// some doc comment
declare_deprecated_lint! {
    pub SHOULD_ASSERT_EQ,
    "`assert!()` will be more flexible with RFC 2011"
}
    "#,
    "module_name").collect();

    let expected = vec![
        Lint::new("ptr_arg", "style", "really long text", None, "module_name"),
        Lint::new("doc_markdown", "pedantic", "single line", None, "module_name"),
        Lint::new(
            "should_assert_eq",
            "Deprecated",
            "`assert!()` will be more flexible with RFC 2011",
            Some("`assert!()` will be more flexible with RFC 2011"),
            "module_name"
        ),
    ];
    assert_eq!(expected, result);
}

#[test]
fn test_active_lints() {
    let lints = vec![
        Lint::new("should_assert_eq", "Deprecated", "abc", Some("Reason"), "module_name"),
        Lint::new("should_assert_eq2", "Not Deprecated", "abc", None, "module_name")
    ];
    let expected = vec![
        Lint::new("should_assert_eq2", "Not Deprecated", "abc", None, "module_name")
    ];
    assert_eq!(expected, Lint::active_lints(lints.into_iter()).collect::<Vec<Lint>>());
}

#[test]
fn test_by_lint_group() {
    let lints = vec![
        Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
        Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"),
        Lint::new("incorrect_match", "group1", "abc", None, "module_name"),
    ];
    let mut expected: HashMap<String, Vec<Lint>> = HashMap::new();
    expected.insert("group1".to_string(), vec![
        Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
        Lint::new("incorrect_match", "group1", "abc", None, "module_name"),
    ]);
    expected.insert("group2".to_string(), vec![
        Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")
    ]);
    assert_eq!(expected, Lint::by_lint_group(&lints));
}
<|endoftext|>


<commit_before>use resources::*;
use resources::common::*;
use caches::common::*;

use rustc_serialize::base64::{self, ToBase64};

use std::io::Read;

use ::hyper::Client;
use ::hyper::net::HttpsConnector;
use ::hyper_native_tls::NativeTlsClient;
use ::hyper::header::{Headers, UserAgent};

pub struct ImageResource<'a, T: 'a + Cache> {
    cache: &'a mut Box<T>,
    client: Client
}

impl<'a, T: 'a + Cache> ImageResource<'a, T> {
    pub fn new(cache: &'a mut Box<T>) -> Self {
        let ssl = NativeTlsClient::new().unwrap();
        let connector = HttpsConnector::new(ssl);
        ImageResource {
            cache: cache,
            client: Client::with_connector(connector)
        }
    }
}

impl<'a, T: 'a + Cache> Resource for ImageResource<'a, T> {
    fn fetch(&mut self, item: &ChannelItem) -> ChannelItem {
        match item.extra.clone() {
            Some(o) => {
                match o {
                    ChannelItemType::Image(extra) => {
                        let url = extra.url;
                        let url2 = url.clone();
                        let img_path = "data/cache/img/";
                        let img_file_name = url.into_bytes().as_slice().to_base64(base64::URL_SAFE);
                        let (from_cache, result, reason) = match self.cache.read(&img_path, &img_file_name) {
                            Ok(result) => (true, result, "".to_string()),
                            Err(_) => {

                                let mut headers = Headers::new();
                                headers.set(UserAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36".to_owned()));

                                match self.client.get(&url2).headers(headers).send() {
                                    Ok(mut resp) => {
                                            let mut buffer = Vec::new();
                                            resp.read_to_end(&mut buffer).expect("fail to read buffer from the http response");
                                            self.cache.write(&img_path, &img_file_name, buffer.clone()).expect("fail to write cache");
                                            (false, buffer, "".to_string())
                                        }
                                    Err(e) => (false, Vec::new(), e.to_string())
                                }
                            }
                        };

                        let result_item = ChannelItem {
                            extra: Some(ChannelItemType::Image(ChannelImageItem { url: url2, bytes: result })),
                            result: reason,
                        };
                        result_item
                    },
                    _ => {
                        ChannelItem {
                            extra: Some(ChannelItemType::Image(Default::default())),
                            result: Default::default(),
                        }
                    }
                }
            }
            None => {
                ChannelItem {
                    extra: Some(ChannelItemType::Image(Default::default())),
                    result: Default::default(),
                }
            }
        }
    }
}
<commit_msg>put more logs in image resource<commit_after>use resources::*;
use resources::common::*;
use caches::common::*;

use rustc_serialize::base64::{self, ToBase64};

use std::io::Read;

use ::hyper::Client;
use ::hyper::net::HttpsConnector;
use ::hyper_native_tls::NativeTlsClient;
use ::hyper::header::{Headers, UserAgent};

pub struct ImageResource<'a, T: 'a + Cache> {
    cache: &'a mut Box<T>,
    client: Client
}

impl<'a, T: 'a + Cache> ImageResource<'a, T> {
    pub fn new(cache: &'a mut Box<T>) -> Self {
        let ssl = NativeTlsClient::new().unwrap();
        let connector = HttpsConnector::new(ssl);
        ImageResource {
            cache: cache,
            client: Client::with_connector(connector)
        }
    }
}

impl<'a, T: 'a + Cache> Resource for ImageResource<'a, T> {
    fn fetch(&mut self, item: &ChannelItem) -> ChannelItem {
        match item.extra.clone() {
            Some(o) => {
                match o {
                    ChannelItemType::Image(extra) => {
                        let url = extra.url;
                        let url2 = url.clone();
                        let img_path = "data/cache/img/";
                        let img_file_name = url.into_bytes().as_slice().to_base64(base64::URL_SAFE);

                        info!("image resource - before find in cache. url: {}", url2.clone());
                        let (from_cache, result, reason) = match self.cache.read(&img_path, &img_file_name) {
                            Ok(result) => {
                                info!("image resource - find in cache success. url:  {}", url2.clone());
                                (true, result, "".to_string())
                            }
                            Err(_) => {
                                info!("image resource - find in cache fail. url:  {}", url2.clone());

                                let mut headers = Headers::new();
                                headers.set(UserAgent("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36".to_owned()));

                                match self.client.get(&url2).headers(headers).send() {
                                    Ok(mut resp) => {
                                            info!("image resource - http request success url:  {}", url2.clone());
                                            let mut buffer = Vec::new();
                                            resp.read_to_end(&mut buffer).expect("fail to read buffer from the http response");
                                            self.cache.write(&img_path, &img_file_name, buffer.clone()).expect("fail to write cache");
                                            (false, buffer, "".to_string())
                                        }
                                    Err(e) => {
                                        info!("image resource - http request fail url:  {}", url2.clone());
                                        (false, Vec::new(), e.to_string())
                                    }
                                }
                            }
                        };

                        let url3 = url2.clone();
                        info!("image url: {} reason: {}", url3, reason);
                        let result_item = ChannelItem {
                            extra: Some(ChannelItemType::Image(ChannelImageItem { url: url2, bytes: result })),
                            result: reason,
                        };
                        result_item
                    },
                    _ => {
                        ChannelItem {
                            extra: Some(ChannelItemType::Image(Default::default())),
                            result: Default::default(),
                        }
                    }
                }
            }
            None => {
                ChannelItem {
                    extra: Some(ChannelItemType::Image(Default::default())),
                    result: Default::default(),
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>// Because the memory map is so important to not be aliased, it is defined here, in one place
// The lower 256 PML4 entries are reserved for userspace
// Each PML4 entry references up to 512 GB of memory
// The top (511) PML4 is reserved for recursive mapping
// The second from the top (510) PML4 is reserved for the kernel
    /// The size of a single PML4
    pub const PML4_SIZE: usize = 0x0000_0080_0000_0000;
    pub const PML4_MASK: usize = 0x0000_ff80_0000_0000;

    /// Size of a page and frame
    pub const PAGE_SIZE: usize = 4096;

    /// Offset of recursive paging
    pub const RECURSIVE_PAGE_OFFSET: usize = (-(PML4_SIZE as isize)) as usize;
    pub const RECURSIVE_PAGE_PML4: usize = (RECURSIVE_PAGE_OFFSET & PML4_MASK)/PML4_SIZE;

    /// Offset of kernel
    pub const KERNEL_OFFSET: usize = RECURSIVE_PAGE_OFFSET - PML4_SIZE;
    pub const KERNEL_PML4: usize = (KERNEL_OFFSET & PML4_MASK)/PML4_SIZE;

    /// Kernel stack size - must be kept in sync with early_init.S. Used by memory::init
    pub const KERNEL_STACK_SIZE: usize = PAGE_SIZE;

    /// Offset to kernel heap
    pub const KERNEL_HEAP_OFFSET: usize = KERNEL_OFFSET - PML4_SIZE;
    pub const KERNEL_HEAP_PML4: usize = (KERNEL_HEAP_OFFSET & PML4_MASK)/PML4_SIZE;

    /// Size of kernel heap
    pub const KERNEL_HEAP_SIZE: usize = 1 * 1024 * 1024; // 1 MB

    /// Offset of device map region
    pub const KERNEL_DEVMAP_OFFSET: usize = KERNEL_HEAP_OFFSET - PML4_SIZE;

    /// Offset of environment region
    pub const KERNEL_ENV_OFFSET: usize = KERNEL_DEVMAP_OFFSET - PML4_SIZE;

    /// Offset of temporary mapping for misc kernel bring-up actions
    pub const KERNEL_TMP_MISC_OFFSET: usize = KERNEL_ENV_OFFSET - PML4_SIZE;

    /// Offset of FDT DTB image
    pub const KERNEL_DTB_OFFSET: usize = KERNEL_TMP_MISC_OFFSET - PML4_SIZE;
    pub const KERNEL_DTB_MAX_SIZE: usize = 2 * 1024 * 1024; // 2 MB

    /// Offset to kernel percpu variables
    //TODO: Use 64-bit fs offset to enable this pub const KERNEL_PERCPU_OFFSET: usize = KERNEL_HEAP_OFFSET - PML4_SIZE;
    pub const KERNEL_PERCPU_OFFSET: usize = KERNEL_DTB_OFFSET - PML4_SIZE;

    /// Size of kernel percpu variables
    pub const KERNEL_PERCPU_SIZE: usize = 64 * 1024; // 64 KB

    /// Offset to user image
    pub const USER_OFFSET: usize = 0;
    pub const USER_PML4: usize = (USER_OFFSET & PML4_MASK)/PML4_SIZE;

    /// Offset to user TCB
    pub const USER_TCB_OFFSET: usize = 0xB000_0000;

    /// Offset to user arguments
    pub const USER_ARG_OFFSET: usize = USER_OFFSET + PML4_SIZE/2;

    /// Offset to user heap
    pub const USER_HEAP_OFFSET: usize = USER_OFFSET + PML4_SIZE;
    pub const USER_HEAP_PML4: usize = (USER_HEAP_OFFSET & PML4_MASK)/PML4_SIZE;

    /// Offset to user grants
    pub const USER_GRANT_OFFSET: usize = USER_HEAP_OFFSET + PML4_SIZE;
    pub const USER_GRANT_PML4: usize = (USER_GRANT_OFFSET & PML4_MASK)/PML4_SIZE;

    /// Offset to user stack
    pub const USER_STACK_OFFSET: usize = USER_GRANT_OFFSET + PML4_SIZE;
    pub const USER_STACK_PML4: usize = (USER_STACK_OFFSET & PML4_MASK)/PML4_SIZE;
    /// Size of user stack
    pub const USER_STACK_SIZE: usize = 1024 * 1024; // 1 MB

    /// Offset to user sigstack
    pub const USER_SIGSTACK_OFFSET: usize = USER_STACK_OFFSET + PML4_SIZE;
    pub const USER_SIGSTACK_PML4: usize = (USER_SIGSTACK_OFFSET & PML4_MASK)/PML4_SIZE;
    /// Size of user sigstack
    pub const USER_SIGSTACK_SIZE: usize = 256 * 1024; // 256 KB

    /// Offset to user TLS
    pub const USER_TLS_OFFSET: usize = USER_SIGSTACK_OFFSET + PML4_SIZE;
    pub const USER_TLS_PML4: usize = (USER_TLS_OFFSET & PML4_MASK)/PML4_SIZE;
    pub const USER_TLS_SIZE: usize = 64 * 1024;

    /// Offset to user temporary image (used when cloning)
    pub const USER_TMP_OFFSET: usize = USER_TLS_OFFSET + PML4_SIZE;
    pub const USER_TMP_PML4: usize = (USER_TMP_OFFSET & PML4_MASK)/PML4_SIZE;

    /// Offset to user temporary heap (used when cloning)
    pub const USER_TMP_HEAP_OFFSET: usize = USER_TMP_OFFSET + PML4_SIZE;
    pub const USER_TMP_HEAP_PML4: usize = (USER_TMP_HEAP_OFFSET & PML4_MASK)/PML4_SIZE;

    /// Offset to user temporary page for grants
    pub const USER_TMP_GRANT_OFFSET: usize = USER_TMP_HEAP_OFFSET + PML4_SIZE;
    pub const USER_TMP_GRANT_PML4: usize = (USER_TMP_GRANT_OFFSET & PML4_MASK)/PML4_SIZE;

    /// Offset to user temporary stack (used when cloning)
    pub const USER_TMP_STACK_OFFSET: usize = USER_TMP_GRANT_OFFSET + PML4_SIZE;
    pub const USER_TMP_STACK_PML4: usize = (USER_TMP_STACK_OFFSET & PML4_MASK)/PML4_SIZE;

    /// Offset to user temporary sigstack (used when cloning)
    pub const USER_TMP_SIGSTACK_OFFSET: usize = USER_TMP_STACK_OFFSET + PML4_SIZE;
    pub const USER_TMP_SIGSTACK_PML4: usize = (USER_TMP_SIGSTACK_OFFSET & PML4_MASK)/PML4_SIZE;

    /// Offset to user temporary tls (used when cloning)
    pub const USER_TMP_TLS_OFFSET: usize = USER_TMP_SIGSTACK_OFFSET + PML4_SIZE;
    pub const USER_TMP_TLS_PML4: usize = (USER_TMP_TLS_OFFSET & PML4_MASK)/PML4_SIZE;

    /// Offset for usage in other temporary pages
    pub const USER_TMP_MISC_OFFSET: usize = USER_TMP_TLS_OFFSET + PML4_SIZE;
    pub const USER_TMP_MISC_PML4: usize = (USER_TMP_MISC_OFFSET & PML4_MASK)/PML4_SIZE;
<commit_msg>aarch64: consts: Use the same USER_TLS_SIZE as x86_64<commit_after>// Because the memory map is so important to not be aliased, it is defined here, in one place
// The lower 256 PML4 entries are reserved for userspace
// Each PML4 entry references up to 512 GB of memory
// The top (511) PML4 is reserved for recursive mapping
// The second from the top (510) PML4 is reserved for the kernel
    /// The size of a single PML4
    pub const PML4_SIZE: usize = 0x0000_0080_0000_0000;
    pub const PML4_MASK: usize = 0x0000_ff80_0000_0000;

    /// Size of a page and frame
    pub const PAGE_SIZE: usize = 4096;

    /// Offset of recursive paging
    pub const RECURSIVE_PAGE_OFFSET: usize = (-(PML4_SIZE as isize)) as usize;
    pub const RECURSIVE_PAGE_PML4: usize = (RECURSIVE_PAGE_OFFSET & PML4_MASK)/PML4_SIZE;

    /// Offset of kernel
    pub const KERNEL_OFFSET: usize = RECURSIVE_PAGE_OFFSET - PML4_SIZE;
    pub const KERNEL_PML4: usize = (KERNEL_OFFSET & PML4_MASK)/PML4_SIZE;

    /// Kernel stack size - must be kept in sync with early_init.S. Used by memory::init
    pub const KERNEL_STACK_SIZE: usize = PAGE_SIZE;

    /// Offset to kernel heap
    pub const KERNEL_HEAP_OFFSET: usize = KERNEL_OFFSET - PML4_SIZE;
    pub const KERNEL_HEAP_PML4: usize = (KERNEL_HEAP_OFFSET & PML4_MASK)/PML4_SIZE;

    /// Size of kernel heap
    pub const KERNEL_HEAP_SIZE: usize = 1 * 1024 * 1024; // 1 MB

    /// Offset of device map region
    pub const KERNEL_DEVMAP_OFFSET: usize = KERNEL_HEAP_OFFSET - PML4_SIZE;

    /// Offset of environment region
    pub const KERNEL_ENV_OFFSET: usize = KERNEL_DEVMAP_OFFSET - PML4_SIZE;

    /// Offset of temporary mapping for misc kernel bring-up actions
    pub const KERNEL_TMP_MISC_OFFSET: usize = KERNEL_ENV_OFFSET - PML4_SIZE;

    /// Offset of FDT DTB image
    pub const KERNEL_DTB_OFFSET: usize = KERNEL_TMP_MISC_OFFSET - PML4_SIZE;
    pub const KERNEL_DTB_MAX_SIZE: usize = 2 * 1024 * 1024; // 2 MB

    /// Offset to kernel percpu variables
    //TODO: Use 64-bit fs offset to enable this pub const KERNEL_PERCPU_OFFSET: usize = KERNEL_HEAP_OFFSET - PML4_SIZE;
    pub const KERNEL_PERCPU_OFFSET: usize = KERNEL_DTB_OFFSET - PML4_SIZE;

    /// Size of kernel percpu variables
    pub const KERNEL_PERCPU_SIZE: usize = 64 * 1024; // 64 KB

    /// Offset to user image
    pub const USER_OFFSET: usize = 0;
    pub const USER_PML4: usize = (USER_OFFSET & PML4_MASK)/PML4_SIZE;

    /// Offset to user TCB
    pub const USER_TCB_OFFSET: usize = 0xB000_0000;

    /// Offset to user arguments
    pub const USER_ARG_OFFSET: usize = USER_OFFSET + PML4_SIZE/2;

    /// Offset to user heap
    pub const USER_HEAP_OFFSET: usize = USER_OFFSET + PML4_SIZE;
    pub const USER_HEAP_PML4: usize = (USER_HEAP_OFFSET & PML4_MASK)/PML4_SIZE;

    /// Offset to user grants
    pub const USER_GRANT_OFFSET: usize = USER_HEAP_OFFSET + PML4_SIZE;
    pub const USER_GRANT_PML4: usize = (USER_GRANT_OFFSET & PML4_MASK)/PML4_SIZE;

    /// Offset to user stack
    pub const USER_STACK_OFFSET: usize = USER_GRANT_OFFSET + PML4_SIZE;
    pub const USER_STACK_PML4: usize = (USER_STACK_OFFSET & PML4_MASK)/PML4_SIZE;
    /// Size of user stack
    pub const USER_STACK_SIZE: usize = 1024 * 1024; // 1 MB

    /// Offset to user sigstack
    pub const USER_SIGSTACK_OFFSET: usize = USER_STACK_OFFSET + PML4_SIZE;
    pub const USER_SIGSTACK_PML4: usize = (USER_SIGSTACK_OFFSET & PML4_MASK)/PML4_SIZE;
    /// Size of user sigstack
    pub const USER_SIGSTACK_SIZE: usize = 256 * 1024; // 256 KB

    /// Offset to user TLS
    pub const USER_TLS_OFFSET: usize = USER_SIGSTACK_OFFSET + PML4_SIZE;
    pub const USER_TLS_PML4: usize = (USER_TLS_OFFSET & PML4_MASK)/PML4_SIZE;
    // Maximum TLS allocated to each PID, should be approximately 8 MB
    pub const USER_TLS_SIZE: usize = PML4_SIZE / 65536;

    /// Offset to user temporary image (used when cloning)
    pub const USER_TMP_OFFSET: usize = USER_TLS_OFFSET + PML4_SIZE;
    pub const USER_TMP_PML4: usize = (USER_TMP_OFFSET & PML4_MASK)/PML4_SIZE;

    /// Offset to user temporary heap (used when cloning)
    pub const USER_TMP_HEAP_OFFSET: usize = USER_TMP_OFFSET + PML4_SIZE;
    pub const USER_TMP_HEAP_PML4: usize = (USER_TMP_HEAP_OFFSET & PML4_MASK)/PML4_SIZE;

    /// Offset to user temporary page for grants
    pub const USER_TMP_GRANT_OFFSET: usize = USER_TMP_HEAP_OFFSET + PML4_SIZE;
    pub const USER_TMP_GRANT_PML4: usize = (USER_TMP_GRANT_OFFSET & PML4_MASK)/PML4_SIZE;

    /// Offset to user temporary stack (used when cloning)
    pub const USER_TMP_STACK_OFFSET: usize = USER_TMP_GRANT_OFFSET + PML4_SIZE;
    pub const USER_TMP_STACK_PML4: usize = (USER_TMP_STACK_OFFSET & PML4_MASK)/PML4_SIZE;

    /// Offset to user temporary sigstack (used when cloning)
    pub const USER_TMP_SIGSTACK_OFFSET: usize = USER_TMP_STACK_OFFSET + PML4_SIZE;
    pub const USER_TMP_SIGSTACK_PML4: usize = (USER_TMP_SIGSTACK_OFFSET & PML4_MASK)/PML4_SIZE;

    /// Offset to user temporary tls (used when cloning)
    pub const USER_TMP_TLS_OFFSET: usize = USER_TMP_SIGSTACK_OFFSET + PML4_SIZE;
    pub const USER_TMP_TLS_PML4: usize = (USER_TMP_TLS_OFFSET & PML4_MASK)/PML4_SIZE;

    /// Offset for usage in other temporary pages
    pub const USER_TMP_MISC_OFFSET: usize = USER_TMP_TLS_OFFSET + PML4_SIZE;
    pub const USER_TMP_MISC_PML4: usize = (USER_TMP_MISC_OFFSET & PML4_MASK)/PML4_SIZE;
<|endoftext|>


<commit_before>//! Non-mutating world and entity state querying functions.

use std::rc::Rc;
use calx_grid::Dir6;
use calx_ecs::Entity;
use calx_resource::{Resource, ResourceStore};
use world::World;
use components::{Alignment, BrainState};
use stats::{Intrinsic, Stats};
use location::Location;
use spatial::Place;
use item::{ItemType, Slot};
use brush::Brush;
use terrain;
use {FovStatus, Light};

/// Game update control.
#[derive(Copy, Clone, PartialEq)]
pub enum ControlState {
    AwaitingInput,
    ReadyToUpdate,
}

/// Return whether the entity is dead and should be removed from the world.
pub fn is_alive(w: &World, e: Entity) -> bool { location(w, e).is_some() }

/// Return the player entity if one exists.
pub fn player(w: &World) -> Option<Entity> {
    if let Some(p) = w.flags.player {
        if is_alive(w, p) {
            return Some(p);
        }
    }

    None
}

/// Return true if the game has ended and the player can make no further
/// actions.
pub fn game_over(w: &World) -> bool { player(w).is_none() }

/// Get the current control state.
pub fn control_state(w: &World) -> ControlState {
    if w.flags.player_acted {
        return ControlState::ReadyToUpdate;
    }

    let p = player(w);
    if let Some(p) = p {
        if acts_this_frame(w, p) {
            return ControlState::AwaitingInput;
        }
    }

    return ControlState::ReadyToUpdate;
}

/// Return whether this mob is the player avatar.
pub fn is_player(w: &World, e: Entity) -> bool {
    // TODO: Should this just check w.flags.player?
    brain_state(w, e) == Some(BrainState::PlayerControl) && is_alive(w, e)
}

/// Return whether the entity is an awake mob.
pub fn is_active(w: &World, e: Entity) -> bool {
    match brain_state(w, e) {
        Some(BrainState::Asleep) => false,
        Some(_) => true,
        _ => false,
    }
}

/// Return whether the entity is a mob that will act this frame.
pub fn acts_this_frame(w: &World, e: Entity) -> bool {
    if !is_active(w, e) {
        return false;
    }
    return ticks_this_frame(w, e);
}

pub fn brain_state(w: &World, e: Entity) -> Option<BrainState> {
    w.ecs.brain.get(e).map_or(None, |brain| Some(brain.state))
}

/// Return if the entity is a mob that should get an update this frame
/// based on its speed properties. Does not check for status effects like
/// sleep that might prevent actual action.
pub fn ticks_this_frame(w: &World, e: Entity) -> bool {
    if !is_mob(w, e) || !is_alive(w, e) {
        return false;
    }

    let tick = w.flags.tick;
    // Go through a cycle of 5 phases to get 4 possible speeds.
    // System idea from Jeff Lait.
    let phase = tick % 5;
    match phase {
        0 => return true,
        1 => return has_intrinsic(w, e, Intrinsic::Fast),
        2 => return true,
        3 => return has_intrinsic(w, e, Intrinsic::Quick),
        4 => return !has_intrinsic(w, e, Intrinsic::Slow),
        _ => panic!("Invalid action phase"),
    }
}

pub fn has_intrinsic(w: &World, e: Entity, intrinsic: Intrinsic) -> bool {
    stats(w, e).intrinsics & intrinsic as u32 != 0
}

/// Return the (composite) stats for an entity.
pub fn stats(w: &World, e: Entity) -> Stats {
    if w.ecs.composite_stats.contains(e) { w.ecs.composite_stats[e].0 } else { base_stats(w, e) }
}

/// Return the base stats of the entity. Does not include any added effects.
/// You almost always want to use the stats function instead of this one.
pub fn base_stats(w: &World, e: Entity) -> Stats {
    if w.ecs.stats.contains(e) { w.ecs.stats[e] } else { Default::default() }
}

pub fn is_mob(w: &World, e: Entity) -> bool { w.ecs.brain.contains(e) }

/// Return the location of an entity.
///
/// Returns the location of the containing entity for entities inside
/// containers. It is possible for entities to not have a location.
pub fn location(w: &World, e: Entity) -> Option<Location> {
    match w.spatial.get(e) {
        Some(Place::At(loc)) => Some(loc),
        Some(Place::In(container, _)) => location(w, container),
        _ => None,
    }
}

/// Look for targets to shoot in a direction.
pub fn find_target(w: &World, shooter: Entity, dir: Dir6, range: usize) -> Option<Entity> {
    let origin = location(w, shooter).unwrap();
    for i in 1..(range + 1) {
        let loc = origin + dir.to_v2() * (i as i32);
        if terrain(w, loc).blocks_shot() {
            break;
        }
        if let Some(e) = mob_at(w, loc) {
            if is_hostile_to(w, shooter, e) {
                return Some(e);
            }
        }
    }
    None
}

/// If location contains a portal, return the destination of the portal.
pub fn portal(w: &World, loc: Location) -> Option<Location> { w.portal(loc).map(|p| loc + p) }

/// Return a portal if it can be seen through.
pub fn visible_portal(w: &World, loc: Location) -> Option<Location> {
    // Only void-form is transparent to portals.
    if terrain(w, loc).form == terrain::Form::Void { portal(w, loc) } else { None }
}

pub fn terrain(w: &World, loc: Location) -> Rc<terrain::Tile> {
    let idx = w.terrain.get(loc);
    terrain::Tile::get_resource(&idx).unwrap()

    // TODO: Add open/closed door mapping to terrain data, closed door terrain should have a field
    // that contains the terrain index of the corresponding open door tile.

    // TODO: Support terrain with brush variant distributions, like the grass case below that
    // occasionlly emits a fancier brush. The distribution needs to be embedded in the Tile struct.
    // The sampling needs loc noise, but is probably better done at the point where terrain is
    // being drawn than here, since we'll want to still have just one immutable terrain id
    // corresponding to all the variants.
    // Mobs standing on doors make the doors open.

    // if ret == TerrainType::Door && has_mobs(w, loc) {
    //     ret = TerrainType::OpenDoor;
    // }
    // // Grass is only occasionally fancy.
    // if ret == TerrainType::Grass {
    //     if loc.noise() > 0.85 {
    //         ret = TerrainType::Grass2;
    //     }
    // }
}

pub fn blocks_sight(w: &World, loc: Location) -> bool { terrain(w, loc).blocks_sight() }

/// Return whether the location obstructs entity movement.
pub fn blocks_walk(w: &World, loc: Location) -> bool {
    if terrain(w, loc).blocks_walk() {
        return true;
    }
    if w.spatial
        .entities_at(loc)
        .into_iter()
        .any(|e| is_blocking_entity(w, e)) {
        return true;
    }
    false
}

/// Return whether the entity blocks movement of other entities.
pub fn is_blocking_entity(w: &World, e: Entity) -> bool { is_mob(w, e) }

pub fn has_mobs(w: &World, loc: Location) -> bool { mob_at(w, loc).is_some() }

pub fn mob_at(w: &World, loc: Location) -> Option<Entity> {
    w.spatial.entities_at(loc).into_iter().find(|&e| is_mob(w, e))
}

pub fn is_hostile_to(w: &World, e: Entity, other: Entity) -> bool {
    match (alignment(w, e), alignment(w, other)) {
        (Some(Alignment::Chaotic), Some(_)) => true,
        (Some(_), Some(Alignment::Chaotic)) => true,
        (Some(Alignment::Evil), Some(Alignment::Good)) => true,
        (Some(Alignment::Good), Some(Alignment::Evil)) => true,
        _ => false,
    }
}

pub fn alignment(w: &World, e: Entity) -> Option<Alignment> {
    if w.ecs.brain.contains(e) { Some(w.ecs.brain[e].alignment) } else { None }
}

/// Return whether the entity can occupy a location.
pub fn can_enter(w: &World, e: Entity, loc: Location) -> bool {
    if is_mob(w, e) && has_mobs(w, loc) {
        return false;
    }
    if terrain(w, loc).is_door() && !has_intrinsic(w, e, Intrinsic::Hands) {
        // Can't open doors without hands.
        return false;
    }
    if blocks_walk(w, loc) {
        return false;
    }
    true
}

/// Return whether the entity can move in a direction.
pub fn can_step(w: &World, e: Entity, dir: Dir6) -> bool {
    if let Some(Place::At(loc)) = w.spatial.get(e) {
        can_enter(w, e, loc + dir.to_v2())
    } else {
        false
    }
}

/// Return the first free storage bag inventory slot on this entity.
pub fn free_bag_slot(w: &World, e: Entity) -> Option<Slot> {
    for &slot in vec![Slot::InventoryJ,
                      Slot::InventoryK,
                      Slot::InventoryL,
                      Slot::InventoryM,
                      Slot::InventoryN,
                      Slot::InventoryO,
                      Slot::InventoryP,
                      Slot::InventoryQ,
                      Slot::InventoryR,
                      Slot::InventoryS,
                      Slot::InventoryT,
                      Slot::InventoryU,
                      Slot::InventoryV,
                      Slot::InventoryW,
                      Slot::InventoryX,
                      Slot::InventoryY,
                      Slot::InventoryZ]
                     .iter() {
        if equipped(w, e, slot).is_none() {
            return Some(slot);
        }
    }
    None
}

/// Return the item equipped by this entity in the given inventory slot.
pub fn equipped(w: &World, e: Entity, slot: Slot) -> Option<Entity> {
    w.spatial.entity_equipped(e, slot)
}

pub fn can_be_picked_up(w: &World, e: Entity) -> bool {
    if w.ecs.item.contains(e) { w.ecs.item[e].item_type != ItemType::Instant } else { false }
}

/// Return an item at the location that can be interacted with.
pub fn top_item(w: &World, loc: Location) -> Option<Entity> {
    w.spatial.entities_at(loc).into_iter().find(|&e| can_be_picked_up(w, e))
}

pub fn is_item(w: &World, e: Entity) -> bool { w.ecs.item.contains(e) }

pub fn area_name(w: &World, _loc: Location) -> String {
    match current_depth(w) {
        0 => "Limbo".to_string(),
        1 => "Overworld".to_string(),
        n => format!("Dungeon {}", n - 1),
    }
}

/// Return the current floor depth. Greater depths mean more powerful monsters
/// and stranger terrain.
pub fn current_depth(w: &World) -> i32 { w.flags.depth }

pub fn hp(w: &World, e: Entity) -> i32 {
    max_hp(w, e) - if w.ecs.health.contains(e) { w.ecs.health[e].wounds } else { 0 }
}

pub fn max_hp(w: &World, e: Entity) -> i32 { stats(w, e).power }

pub fn fov_status(w: &World, loc: Location) -> Option<FovStatus> {
    if let Some(p) = player(w) {
        if w.ecs.map_memory.contains(p) {
            if w.ecs.map_memory[p].seen.contains(&loc) {
                return Some(FovStatus::Seen);
            }
            if w.ecs.map_memory[p].remembered.contains(&loc) {
                return Some(FovStatus::Remembered);
            }
            return None;
        }
    }
    // Just show everything by default.
    Some(::FovStatus::Seen)
}

/// Light level for the location.
pub fn light_at(w: &World, loc: Location) -> Light {
    if current_depth(w) == 1 {
        // Topside, full light.
        return Light::new(1.0);
    }
    if terrain(w, loc).is_luminous() {
        return Light::new(1.0);
    }

    if let Some(d) = loc.distance_from(w.flags.camera) {
        let lum = 0.8 - d as f32 / 10.0;
        return Light::new(if lum >= 0.0 { lum } else { 0.0 });
    }
    return Light::new(1.0);
}

/// Return whether the entity is an awake non-player mob and should be
/// animated with a bob.
pub fn is_bobbing(w: &World, e: Entity) -> bool { is_active(w, e) && !is_player(w, e) }

pub fn entity_brush(w: &World, e: Entity) -> Option<Resource<Brush>> {
    if w.ecs.desc.contains(e) { Some(w.ecs.desc[e].brush.clone()) } else { None }
}

pub fn is_instant_item(w: &World, e: Entity) -> bool {
    w.ecs.item.get(e).map_or(false, |item| item.item_type == ItemType::Instant)
}

pub fn can_enter_portals(w: &World, e: Entity) -> bool { is_player(w, e) }
<commit_msg>Random test terrain<commit_after>//! Non-mutating world and entity state querying functions.

use std::rc::Rc;
use calx_grid::Dir6;
use calx_ecs::Entity;
use calx_resource::{Resource, ResourceStore};
use world::World;
use components::{Alignment, BrainState};
use stats::{Intrinsic, Stats};
use location::Location;
use spatial::Place;
use item::{ItemType, Slot};
use brush::Brush;
use terrain;
use {FovStatus, Light};

/// Game update control.
#[derive(Copy, Clone, PartialEq)]
pub enum ControlState {
    AwaitingInput,
    ReadyToUpdate,
}

/// Return whether the entity is dead and should be removed from the world.
pub fn is_alive(w: &World, e: Entity) -> bool { location(w, e).is_some() }

/// Return the player entity if one exists.
pub fn player(w: &World) -> Option<Entity> {
    if let Some(p) = w.flags.player {
        if is_alive(w, p) {
            return Some(p);
        }
    }

    None
}

/// Return true if the game has ended and the player can make no further
/// actions.
pub fn game_over(w: &World) -> bool { player(w).is_none() }

/// Get the current control state.
pub fn control_state(w: &World) -> ControlState {
    if w.flags.player_acted {
        return ControlState::ReadyToUpdate;
    }

    let p = player(w);
    if let Some(p) = p {
        if acts_this_frame(w, p) {
            return ControlState::AwaitingInput;
        }
    }

    return ControlState::ReadyToUpdate;
}

/// Return whether this mob is the player avatar.
pub fn is_player(w: &World, e: Entity) -> bool {
    // TODO: Should this just check w.flags.player?
    brain_state(w, e) == Some(BrainState::PlayerControl) && is_alive(w, e)
}

/// Return whether the entity is an awake mob.
pub fn is_active(w: &World, e: Entity) -> bool {
    match brain_state(w, e) {
        Some(BrainState::Asleep) => false,
        Some(_) => true,
        _ => false,
    }
}

/// Return whether the entity is a mob that will act this frame.
pub fn acts_this_frame(w: &World, e: Entity) -> bool {
    if !is_active(w, e) {
        return false;
    }
    return ticks_this_frame(w, e);
}

pub fn brain_state(w: &World, e: Entity) -> Option<BrainState> {
    w.ecs.brain.get(e).map_or(None, |brain| Some(brain.state))
}

/// Return if the entity is a mob that should get an update this frame
/// based on its speed properties. Does not check for status effects like
/// sleep that might prevent actual action.
pub fn ticks_this_frame(w: &World, e: Entity) -> bool {
    if !is_mob(w, e) || !is_alive(w, e) {
        return false;
    }

    let tick = w.flags.tick;
    // Go through a cycle of 5 phases to get 4 possible speeds.
    // System idea from Jeff Lait.
    let phase = tick % 5;
    match phase {
        0 => return true,
        1 => return has_intrinsic(w, e, Intrinsic::Fast),
        2 => return true,
        3 => return has_intrinsic(w, e, Intrinsic::Quick),
        4 => return !has_intrinsic(w, e, Intrinsic::Slow),
        _ => panic!("Invalid action phase"),
    }
}

pub fn has_intrinsic(w: &World, e: Entity, intrinsic: Intrinsic) -> bool {
    stats(w, e).intrinsics & intrinsic as u32 != 0
}

/// Return the (composite) stats for an entity.
pub fn stats(w: &World, e: Entity) -> Stats {
    if w.ecs.composite_stats.contains(e) { w.ecs.composite_stats[e].0 } else { base_stats(w, e) }
}

/// Return the base stats of the entity. Does not include any added effects.
/// You almost always want to use the stats function instead of this one.
pub fn base_stats(w: &World, e: Entity) -> Stats {
    if w.ecs.stats.contains(e) { w.ecs.stats[e] } else { Default::default() }
}

pub fn is_mob(w: &World, e: Entity) -> bool { w.ecs.brain.contains(e) }

/// Return the location of an entity.
///
/// Returns the location of the containing entity for entities inside
/// containers. It is possible for entities to not have a location.
pub fn location(w: &World, e: Entity) -> Option<Location> {
    match w.spatial.get(e) {
        Some(Place::At(loc)) => Some(loc),
        Some(Place::In(container, _)) => location(w, container),
        _ => None,
    }
}

/// Look for targets to shoot in a direction.
pub fn find_target(w: &World, shooter: Entity, dir: Dir6, range: usize) -> Option<Entity> {
    let origin = location(w, shooter).unwrap();
    for i in 1..(range + 1) {
        let loc = origin + dir.to_v2() * (i as i32);
        if terrain(w, loc).blocks_shot() {
            break;
        }
        if let Some(e) = mob_at(w, loc) {
            if is_hostile_to(w, shooter, e) {
                return Some(e);
            }
        }
    }
    None
}

/// If location contains a portal, return the destination of the portal.
pub fn portal(w: &World, loc: Location) -> Option<Location> { w.portal(loc).map(|p| loc + p) }

/// Return a portal if it can be seen through.
pub fn visible_portal(w: &World, loc: Location) -> Option<Location> {
    // Only void-form is transparent to portals.
    if terrain(w, loc).form == terrain::Form::Void { portal(w, loc) } else { None }
}

pub fn terrain(w: &World, loc: Location) -> Rc<terrain::Tile> {
    let mut idx = w.terrain.get(loc);

    if idx == 0 {
        use terrain::Id;
        // Empty terrain, inject custom stuff.
        match loc.noise() {
            x if x < 0.5 => idx = Id::Ground as u8,
            x if x < 0.75 => idx = Id::Grass as u8,
            x if x < 0.95 => idx = Id::Water as u8,
            _ => idx = Id::Tree as u8
        }
    }

    terrain::Tile::get_resource(&idx).unwrap()

    // TODO: Add open/closed door mapping to terrain data, closed door terrain should have a field
    // that contains the terrain index of the corresponding open door tile.

    // TODO: Support terrain with brush variant distributions, like the grass case below that
    // occasionlly emits a fancier brush. The distribution needs to be embedded in the Tile struct.
    // The sampling needs loc noise, but is probably better done at the point where terrain is
    // being drawn than here, since we'll want to still have just one immutable terrain id
    // corresponding to all the variants.
    // Mobs standing on doors make the doors open.

    // if ret == TerrainType::Door && has_mobs(w, loc) {
    //     ret = TerrainType::OpenDoor;
    // }
    // // Grass is only occasionally fancy.
    // if ret == TerrainType::Grass {
    //     if loc.noise() > 0.85 {
    //         ret = TerrainType::Grass2;
    //     }
    // }
}

pub fn blocks_sight(w: &World, loc: Location) -> bool { terrain(w, loc).blocks_sight() }

/// Return whether the location obstructs entity movement.
pub fn blocks_walk(w: &World, loc: Location) -> bool {
    if terrain(w, loc).blocks_walk() {
        return true;
    }
    if w.spatial
        .entities_at(loc)
        .into_iter()
        .any(|e| is_blocking_entity(w, e)) {
        return true;
    }
    false
}

/// Return whether the entity blocks movement of other entities.
pub fn is_blocking_entity(w: &World, e: Entity) -> bool { is_mob(w, e) }

pub fn has_mobs(w: &World, loc: Location) -> bool { mob_at(w, loc).is_some() }

pub fn mob_at(w: &World, loc: Location) -> Option<Entity> {
    w.spatial.entities_at(loc).into_iter().find(|&e| is_mob(w, e))
}

pub fn is_hostile_to(w: &World, e: Entity, other: Entity) -> bool {
    match (alignment(w, e), alignment(w, other)) {
        (Some(Alignment::Chaotic), Some(_)) => true,
        (Some(_), Some(Alignment::Chaotic)) => true,
        (Some(Alignment::Evil), Some(Alignment::Good)) => true,
        (Some(Alignment::Good), Some(Alignment::Evil)) => true,
        _ => false,
    }
}

pub fn alignment(w: &World, e: Entity) -> Option<Alignment> {
    if w.ecs.brain.contains(e) { Some(w.ecs.brain[e].alignment) } else { None }
}

/// Return whether the entity can occupy a location.
pub fn can_enter(w: &World, e: Entity, loc: Location) -> bool {
    if is_mob(w, e) && has_mobs(w, loc) {
        return false;
    }
    if terrain(w, loc).is_door() && !has_intrinsic(w, e, Intrinsic::Hands) {
        // Can't open doors without hands.
        return false;
    }
    if blocks_walk(w, loc) {
        return false;
    }
    true
}

/// Return whether the entity can move in a direction.
pub fn can_step(w: &World, e: Entity, dir: Dir6) -> bool {
    if let Some(Place::At(loc)) = w.spatial.get(e) {
        can_enter(w, e, loc + dir.to_v2())
    } else {
        false
    }
}

/// Return the first free storage bag inventory slot on this entity.
pub fn free_bag_slot(w: &World, e: Entity) -> Option<Slot> {
    for &slot in vec![Slot::InventoryJ,
                      Slot::InventoryK,
                      Slot::InventoryL,
                      Slot::InventoryM,
                      Slot::InventoryN,
                      Slot::InventoryO,
                      Slot::InventoryP,
                      Slot::InventoryQ,
                      Slot::InventoryR,
                      Slot::InventoryS,
                      Slot::InventoryT,
                      Slot::InventoryU,
                      Slot::InventoryV,
                      Slot::InventoryW,
                      Slot::InventoryX,
                      Slot::InventoryY,
                      Slot::InventoryZ]
                     .iter() {
        if equipped(w, e, slot).is_none() {
            return Some(slot);
        }
    }
    None
}

/// Return the item equipped by this entity in the given inventory slot.
pub fn equipped(w: &World, e: Entity, slot: Slot) -> Option<Entity> {
    w.spatial.entity_equipped(e, slot)
}

pub fn can_be_picked_up(w: &World, e: Entity) -> bool {
    if w.ecs.item.contains(e) { w.ecs.item[e].item_type != ItemType::Instant } else { false }
}

/// Return an item at the location that can be interacted with.
pub fn top_item(w: &World, loc: Location) -> Option<Entity> {
    w.spatial.entities_at(loc).into_iter().find(|&e| can_be_picked_up(w, e))
}

pub fn is_item(w: &World, e: Entity) -> bool { w.ecs.item.contains(e) }

pub fn area_name(w: &World, _loc: Location) -> String {
    match current_depth(w) {
        0 => "Limbo".to_string(),
        1 => "Overworld".to_string(),
        n => format!("Dungeon {}", n - 1),
    }
}

/// Return the current floor depth. Greater depths mean more powerful monsters
/// and stranger terrain.
pub fn current_depth(w: &World) -> i32 { w.flags.depth }

pub fn hp(w: &World, e: Entity) -> i32 {
    max_hp(w, e) - if w.ecs.health.contains(e) { w.ecs.health[e].wounds } else { 0 }
}

pub fn max_hp(w: &World, e: Entity) -> i32 { stats(w, e).power }

pub fn fov_status(w: &World, loc: Location) -> Option<FovStatus> {
    if let Some(p) = player(w) {
        if w.ecs.map_memory.contains(p) {
            if w.ecs.map_memory[p].seen.contains(&loc) {
                return Some(FovStatus::Seen);
            }
            if w.ecs.map_memory[p].remembered.contains(&loc) {
                return Some(FovStatus::Remembered);
            }
            return None;
        }
    }
    // Just show everything by default.
    Some(::FovStatus::Seen)
}

/// Light level for the location.
pub fn light_at(w: &World, loc: Location) -> Light {
    if current_depth(w) == 1 {
        // Topside, full light.
        return Light::new(1.0);
    }
    if terrain(w, loc).is_luminous() {
        return Light::new(1.0);
    }

    if let Some(d) = loc.distance_from(w.flags.camera) {
        let lum = 0.8 - d as f32 / 10.0;
        return Light::new(if lum >= 0.0 { lum } else { 0.0 });
    }
    return Light::new(1.0);
}

/// Return whether the entity is an awake non-player mob and should be
/// animated with a bob.
pub fn is_bobbing(w: &World, e: Entity) -> bool { is_active(w, e) && !is_player(w, e) }

pub fn entity_brush(w: &World, e: Entity) -> Option<Resource<Brush>> {
    if w.ecs.desc.contains(e) { Some(w.ecs.desc[e].brush.clone()) } else { None }
}

pub fn is_instant_item(w: &World, e: Entity) -> bool {
    w.ecs.item.get(e).map_or(false, |item| item.item_type == ItemType::Instant)
}

pub fn can_enter_portals(w: &World, e: Entity) -> bool { is_player(w, e) }
<|endoftext|>


<commit_before>use crate::{
    prelude::{Properties, SignedBlock, TxVariant},
    serializer::*,
};
use std::convert::{TryFrom, TryInto};
use std::io::{self, Cursor, Error};

#[repr(u8)]
pub enum MsgType {
    Error = 0,
    GetProperties = 1,
    GetBlock = 2,
}

pub enum MsgRequest {
    GetProperties,
    GetBlock(u64), // height
}

impl MsgRequest {
    pub fn serialize(self) -> Vec<u8> {
        match self {
            MsgRequest::GetProperties => vec![MsgType::GetProperties as u8],
            MsgRequest::GetBlock(height) => {
                let mut buf = Vec::with_capacity(9);
                buf.push(MsgType::GetBlock as u8);
                buf.push_u64(height);
                buf
            }
        }
    }

    pub fn deserialize(cursor: &mut Cursor<&[u8]>) -> io::Result<Self> {
        let tag = cursor.take_u8()?;
        match tag {
            t if t == MsgType::GetProperties as u8 => Ok(MsgRequest::GetProperties),
            t if t == MsgType::GetBlock as u8 => {
                let height = cursor.take_u64()?;
                Ok(MsgRequest::GetBlock(height))
            }
            _ => Err(Error::new(
                io::ErrorKind::InvalidData,
                "invalid msg request",
            )),
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(u16)]
pub enum ErrorKind {
    UnknownError = 0,
    InvalidHeight = 1,
}

impl TryFrom<u16> for ErrorKind {
    type Error = Error;

    fn try_from(value: u16) -> Result<ErrorKind, Error> {
        match value {
            e if e == ErrorKind::UnknownError as u16 => Ok(ErrorKind::UnknownError),
            e if e == ErrorKind::InvalidHeight as u16 => Ok(ErrorKind::InvalidHeight),
            _ => Err(Error::new(io::ErrorKind::InvalidData, "unknown error kind")),
        }
    }
}

#[derive(Clone, Debug)]
pub enum MsgResponse {
    Error(ErrorKind, Option<String>), // code, message
    GetProperties(Properties),
    GetBlock(SignedBlock),
}

impl MsgResponse {
    pub fn serialize(self) -> Vec<u8> {
        use std::mem;

        match self {
            MsgResponse::Error(code, msg) => match msg {
                Some(msg) => {
                    let mut buf = Vec::with_capacity(3 + msg.len());
                    buf.push(MsgType::Error as u8);
                    buf.push_u16(code as u16);
                    buf.push_bytes(msg.as_bytes());
                    buf
                }
                None => {
                    let mut buf = Vec::with_capacity(7);
                    buf.push(MsgType::Error as u8);
                    buf.push_u16(code as u16);
                    buf.push_bytes(&[]);
                    buf
                }
            },
            MsgResponse::GetProperties(props) => {
                let mut buf = Vec::with_capacity(1 + mem::size_of::<Properties>());
                buf.push(MsgType::GetProperties as u8);
                buf.push_u64(props.height);
                {
                    let mut tx_buf = Vec::with_capacity(4096);
                    TxVariant::OwnerTx(props.owner).encode_with_sigs(&mut tx_buf);
                    buf.push_bytes(&tx_buf);
                }
                buf.push_balance(&props.network_fee);
                buf.push_balance(&props.token_supply);
                buf
            }
            MsgResponse::GetBlock(block) => {
                let mut buf = Vec::with_capacity(1_048_576);
                buf.push(MsgType::GetBlock as u8);
                block.encode_with_tx(&mut buf);
                buf
            }
        }
    }

    pub fn deserialize(cursor: &mut Cursor<&[u8]>) -> io::Result<Self> {
        let tag = cursor.take_u8()?;
        match tag {
            t if t == MsgType::Error as u8 => {
                let kind = cursor.take_u16()?.try_into()?;
                let msg = {
                    let buf = cursor.take_bytes()?;
                    if buf.is_empty() {
                        None
                    } else {
                        Some(String::from_utf8_lossy(&buf).into_owned())
                    }
                };
                Ok(MsgResponse::Error(kind, msg))
            }
            t if t == MsgType::GetProperties as u8 => {
                let height = cursor.take_u64()?;
                let owner = {
                    let var = TxVariant::decode_with_sigs(cursor).ok_or_else(|| {
                        Error::new(io::ErrorKind::InvalidData, "failed to deserialize owner tx")
                    })?;
                    match var {
                        TxVariant::OwnerTx(tx) => tx,
                        _ => {
                            return Err(Error::new(io::ErrorKind::InvalidData, "expected owner tx"))
                        }
                    }
                };
                let network_fee = cursor.take_balance()?;
                let token_supply = cursor.take_balance()?;
                Ok(MsgResponse::GetProperties(Properties {
                    height,
                    owner,
                    network_fee,
                    token_supply,
                }))
            }
            t if t == MsgType::GetBlock as u8 => {
                let block = SignedBlock::decode_with_tx(cursor)
                    .ok_or_else(|| Error::from(io::ErrorKind::UnexpectedEof))?;
                Ok(MsgResponse::GetBlock(block))
            }
            _ => Err(Error::new(
                io::ErrorKind::InvalidData,
                "invalid msg response",
            )),
        }
    }
}
<commit_msg>Fix owner encoding in properties<commit_after>use crate::{
    prelude::{Properties, SignedBlock, TxVariant},
    serializer::*,
};
use std::convert::{TryFrom, TryInto};
use std::io::{self, Cursor, Error};

#[repr(u8)]
pub enum MsgType {
    Error = 0,
    GetProperties = 1,
    GetBlock = 2,
}

pub enum MsgRequest {
    GetProperties,
    GetBlock(u64), // height
}

impl MsgRequest {
    pub fn serialize(self) -> Vec<u8> {
        match self {
            MsgRequest::GetProperties => vec![MsgType::GetProperties as u8],
            MsgRequest::GetBlock(height) => {
                let mut buf = Vec::with_capacity(9);
                buf.push(MsgType::GetBlock as u8);
                buf.push_u64(height);
                buf
            }
        }
    }

    pub fn deserialize(cursor: &mut Cursor<&[u8]>) -> io::Result<Self> {
        let tag = cursor.take_u8()?;
        match tag {
            t if t == MsgType::GetProperties as u8 => Ok(MsgRequest::GetProperties),
            t if t == MsgType::GetBlock as u8 => {
                let height = cursor.take_u64()?;
                Ok(MsgRequest::GetBlock(height))
            }
            _ => Err(Error::new(
                io::ErrorKind::InvalidData,
                "invalid msg request",
            )),
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(u16)]
pub enum ErrorKind {
    UnknownError = 0,
    InvalidHeight = 1,
}

impl TryFrom<u16> for ErrorKind {
    type Error = Error;

    fn try_from(value: u16) -> Result<ErrorKind, Error> {
        match value {
            e if e == ErrorKind::UnknownError as u16 => Ok(ErrorKind::UnknownError),
            e if e == ErrorKind::InvalidHeight as u16 => Ok(ErrorKind::InvalidHeight),
            _ => Err(Error::new(io::ErrorKind::InvalidData, "unknown error kind")),
        }
    }
}

#[derive(Clone, Debug)]
pub enum MsgResponse {
    Error(ErrorKind, Option<String>), // code, message
    GetProperties(Properties),
    GetBlock(SignedBlock),
}

impl MsgResponse {
    pub fn serialize(self) -> Vec<u8> {
        use std::mem;

        match self {
            MsgResponse::Error(code, msg) => match msg {
                Some(msg) => {
                    let mut buf = Vec::with_capacity(3 + msg.len());
                    buf.push(MsgType::Error as u8);
                    buf.push_u16(code as u16);
                    buf.push_bytes(msg.as_bytes());
                    buf
                }
                None => {
                    let mut buf = Vec::with_capacity(7);
                    buf.push(MsgType::Error as u8);
                    buf.push_u16(code as u16);
                    buf.push_bytes(&[]);
                    buf
                }
            },
            MsgResponse::GetProperties(props) => {
                let mut buf = Vec::with_capacity(1 + mem::size_of::<Properties>());
                buf.push(MsgType::GetProperties as u8);
                buf.push_u64(props.height);
                {
                    let mut tx_buf = Vec::with_capacity(4096);
                    TxVariant::OwnerTx(props.owner).encode_with_sigs(&mut tx_buf);
                    buf.extend_from_slice(&tx_buf);
                }
                buf.push_balance(&props.network_fee);
                buf.push_balance(&props.token_supply);
                buf
            }
            MsgResponse::GetBlock(block) => {
                let mut buf = Vec::with_capacity(1_048_576);
                buf.push(MsgType::GetBlock as u8);
                block.encode_with_tx(&mut buf);
                buf
            }
        }
    }

    pub fn deserialize(cursor: &mut Cursor<&[u8]>) -> io::Result<Self> {
        let tag = cursor.take_u8()?;
        match tag {
            t if t == MsgType::Error as u8 => {
                let kind = cursor.take_u16()?.try_into()?;
                let msg = {
                    let buf = cursor.take_bytes()?;
                    if buf.is_empty() {
                        None
                    } else {
                        Some(String::from_utf8_lossy(&buf).into_owned())
                    }
                };
                Ok(MsgResponse::Error(kind, msg))
            }
            t if t == MsgType::GetProperties as u8 => {
                let height = cursor.take_u64()?;
                let owner = {
                    let var = TxVariant::decode_with_sigs(cursor).ok_or_else(|| {
                        Error::new(io::ErrorKind::InvalidData, "failed to deserialize owner tx")
                    })?;
                    match var {
                        TxVariant::OwnerTx(tx) => tx,
                        _ => {
                            return Err(Error::new(io::ErrorKind::InvalidData, "expected owner tx"))
                        }
                    }
                };
                let network_fee = cursor.take_balance()?;
                let token_supply = cursor.take_balance()?;
                Ok(MsgResponse::GetProperties(Properties {
                    height,
                    owner,
                    network_fee,
                    token_supply,
                }))
            }
            t if t == MsgType::GetBlock as u8 => {
                let block = SignedBlock::decode_with_tx(cursor)
                    .ok_or_else(|| Error::from(io::ErrorKind::UnexpectedEof))?;
                Ok(MsgResponse::GetBlock(block))
            }
            _ => Err(Error::new(
                io::ErrorKind::InvalidData,
                "invalid msg response",
            )),
        }
    }
}
<|endoftext|>


<commit_before>//! Common types for receipts.

use ruma_serde::StringEnum;

/// The type of receipt.
#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq, StringEnum)]
pub enum ReceiptType {
    /// m.read
    #[ruma_enum(rename = "m.read")]
    Read,

    #[doc(hidden)]
    _Custom(String),
}
<commit_msg>common: Use ruma-serde derives for ReceiptType<commit_after>//! Common types for receipts.

use ruma_serde::{OrdAsRefStr, PartialEqAsRefStr, PartialOrdAsRefStr, StringEnum};

/// The type of receipt.
#[derive(Clone, Debug, PartialOrdAsRefStr, OrdAsRefStr, PartialEqAsRefStr, Eq, StringEnum)]
pub enum ReceiptType {
    /// m.read
    #[ruma_enum(rename = "m.read")]
    Read,

    #[doc(hidden)]
    _Custom(String),
}
<|endoftext|>


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

use diesel::{QueryDsl, RunQueryDsl};
use juniper::{FieldError, FieldResult};

use file::FileContentInput;
use problem::material::Material;
use problem::ProblemName;
use root::ApiContext;
use schema::problems;
use user::UserId;

use crate::api::award::{AwardView, ScoreAwardGrading};
use crate::data::award::{
    AwardDomain, AwardValue, ScoreAwardDomain, ScoreAwardGrade, ScoreAwardValue, ScoreRange,
};

use super::*;
use crate::api::contest::{Contest, ContestStatus};
use crate::api::contest_evaluation::Evaluation;
use crate::api::contest_submission::{FileInput, Submission};

#[derive(juniper::GraphQLInputObject)]
pub struct ProblemInput {
    name: String,
    archive_content: FileContentInput,
}

#[derive(Insertable)]
#[table_name = "problems"]
pub struct ProblemInsertable {
    name: String,
    archive_integrity: String,
}

#[derive(juniper::GraphQLInputObject)]
pub struct ProblemUpdateInput {
    name: String,
    archive_content: Option<FileContentInput>,
}

#[derive(AsChangeset)]
#[table_name = "problems"]
pub struct ProblemChangeset {
    archive_integrity: Option<String>,
}

#[derive(Queryable, Clone, Debug)]
struct ProblemData {
    name: String,
    archive_integrity: String,
}

/// A problem in the contest
pub struct Problem {
    contest: Contest,
    data: ProblemData,
    material: Material,
}

#[juniper_ext::graphql(Context = ApiContext)]
impl Problem {
    /// The contest this problem belongs to.
    pub fn contest(&self) -> &Contest {
        &self.contest
    }

    /// Name of this problem. Unique in the current contest.
    pub fn name(&self) -> ProblemName {
        ProblemName(self.data.name.clone())
    }

    /// Material of this problem
    pub fn material(&self) -> &Material {
        &self.material
    }

    /// Range of the total score, obtained as the sum of all the score awards
    pub fn score_range(&self) -> ScoreRange {
        ScoreRange::total(self.material.awards.iter().filter_map(
            |award| match award.material.domain {
                AwardDomain::Score(ScoreAwardDomain { range }) => Some(range),
                _ => None,
            },
        ))
    }

    pub fn score_domain(&self) -> ScoreAwardDomain {
        ScoreAwardDomain {
            range: self.score_range(),
        }
    }

    pub fn view(&self, context: &ApiContext, user_id: Option<UserId>) -> FieldResult<ProblemView> {
        context.authorize_user(&user_id)?;
        Ok(ProblemView {
            problem: &self,
            user_id,
        })
    }
}

impl Problem {
    fn new(context: &ApiContext, data: ProblemData) -> FieldResult<Self> {
        let material = Self::get_problem_material(&data, context)?;
        let contest = Contest::current(context)?;
        Ok(Problem {
            contest,
            data,
            material,
        })
    }

    /// Get a problem data by its name
    pub fn by_name(context: &ApiContext, name: &str) -> FieldResult<Self> {
        let data = problems::table.find(name).first(&context.database)?;
        Ok(Self::new(context, data)?)
    }

    /// Get all the problems data in the database
    pub fn all(context: &ApiContext) -> FieldResult<Vec<Self>> {
        Ok(problems::table
            .load::<ProblemData>(&context.database)?
            .into_iter()
            .map(|data| Self::new(context, data))
            .collect::<Result<Vec<_>, _>>()?)
    }

    /// Insert a problem in the database
    pub fn insert(context: &ApiContext, inputs: Vec<ProblemInput>) -> FieldResult<()> {
        for ProblemInput {
            name,
            archive_content,
        } in inputs
        {
            diesel::insert_into(problems::table)
                .values(ProblemInsertable {
                    name,
                    archive_integrity: context.create_blob(&archive_content.decode()?)?,
                })
                .execute(&context.database)?;
        }
        Ok(())
    }

    /// Delete a problem from the database
    pub fn delete(context: &ApiContext, names: Vec<String>) -> FieldResult<()> {
        use crate::diesel::ExpressionMethods;
        diesel::delete(problems::table)
            .filter(problems::dsl::name.eq_any(names))
            .execute(&context.database)?;
        Ok(())
    }

    /// Update a problem in the database
    pub fn update(context: &ApiContext, inputs: Vec<ProblemUpdateInput>) -> FieldResult<()> {
        use crate::diesel::ExpressionMethods;
        for input in inputs {
            diesel::update(problems::table)
                .filter(problems::dsl::name.eq(&input.name))
                .set(&ProblemChangeset {
                    archive_integrity: if let Some(content) = input.archive_content {
                        Some(context.create_blob(&content.decode()?)?)
                    } else {
                        None
                    },
                })
                .execute(&context.database)?;
        }
        Ok(())
    }

    pub fn unpack(&self, context: &ApiContext) -> FieldResult<PathBuf> {
        Self::unpack_data(&self.data, context)
    }

    fn unpack_data(data: &ProblemData, context: &ApiContext) -> FieldResult<PathBuf> {
        context.unpack_archive(&data.archive_integrity, "problem")
    }

    /// Material of this problem
    fn get_problem_material(data: &ProblemData, context: &ApiContext) -> FieldResult<Material> {
        Ok(task_maker::generate_material(Self::unpack_data(
            data, context,
        )?)?)
    }
}

/// A problem in the contest as seen by contestants
pub struct ProblemView<'a> {
    problem: &'a Problem,
    user_id: Option<UserId>,
}

#[juniper_ext::graphql(Context = ApiContext)]
impl<'a> ProblemView<'a> {
    pub fn awards(&self) -> Vec<AwardView> {
        self.problem
            .material()
            .awards
            .iter()
            .map(|award| AwardView {
                user_id: self.user_id.clone(),
                award: (*award).clone(),
                problem: self.problem,
            })
            .collect()
    }

    pub fn grading(&self, context: &ApiContext) -> FieldResult<ScoreAwardGrading> {
        Ok(ScoreAwardGrading {
            domain: self.problem.score_domain(),
            grade: match self.tackling() {
                Some(t) => Some(t.grade(context)?),
                None => None,
            },
        })
    }

    pub fn tackling(&self) -> Option<ProblemTackling<'a>> {
        // TODO: return `None` if user is not participating in the contest
        self.user_id.as_ref().map(|user_id| ProblemTackling {
            problem: &self.problem,
            user_id: (*user_id).clone(),
        })
    }
}

/// Progress at solving a problem by a user in the contest
pub struct ProblemTackling<'a> {
    problem: &'a Problem,
    user_id: UserId,
}

impl ProblemTackling<'_> {
    pub fn submit(&self, context: &ApiContext, files: Vec<FileInput>) -> FieldResult<Submission> {
        if self.can_submit() {
            return Err("cannot submit now".into());
        }

        let submission =
            Submission::insert(&context, &self.user_id.0, &self.problem.name().0, files)?;
        Evaluation::start_new(&submission, context)?;
        Ok(submission)
    }
}

/// Attempts at solving a problem by a user in the contest
#[juniper_ext::graphql(Context = ApiContext)]
impl ProblemTackling<'_> {
    /// Sum of the score awards
    pub fn score(&self, context: &ApiContext) -> FieldResult<ScoreAwardValue> {
        Ok(ScoreAwardValue::total(
            self.problem
                .view(context, Some(self.user_id.clone()))?
                .awards()
                .into_iter()
                .map(|award_view: AwardView| -> FieldResult<_> {
                    Ok(
                        match award_view
                            .tackling()
                            .ok_or(FieldError::from("problem tackling without award tackling"))?
                            .best_achievement(context)?
                            .value()
                        {
                            AwardValue::Score(value) => Some(value),
                            _ => None,
                        },
                    )
                })
                .filter_map(|x| match x {
                    Ok(None) => None,
                    Ok(Some(x)) => Some(Ok(x)),
                    Err(x) => Some(Err(x)),
                })
                .collect::<FieldResult<Vec<_>>>()?,
        ))
    }

    fn grade(&self, context: &ApiContext) -> FieldResult<ScoreAwardGrade> {
        Ok(ScoreAwardGrade {
            domain: self.problem.score_domain(),
            value: self.score(context)?,
        })
    }

    /// Submissions of the current user (if to be shown)
    fn submissions(
        &self,
        context: &ApiContext,
    ) -> FieldResult<Vec<contest_submission::Submission>> {
        Ok(contest_submission::Submission::by_user_and_problem(
            context,
            &self.user_id.0,
            &self.problem.name().0,
        )?)
    }

    /// Indicates if the user can submit to this problem
    fn can_submit(&self) -> bool {
        match self.problem.contest.status() {
            ContestStatus::Running => true,
            _ => false,
        }
    }
}
<commit_msg>Fix inverted logic in `can_submit` check<commit_after>use std::path::PathBuf;

use diesel::{QueryDsl, RunQueryDsl};
use juniper::{FieldError, FieldResult};

use file::FileContentInput;
use problem::material::Material;
use problem::ProblemName;
use root::ApiContext;
use schema::problems;
use user::UserId;

use crate::api::award::{AwardView, ScoreAwardGrading};
use crate::data::award::{
    AwardDomain, AwardValue, ScoreAwardDomain, ScoreAwardGrade, ScoreAwardValue, ScoreRange,
};

use super::*;
use crate::api::contest::{Contest, ContestStatus};
use crate::api::contest_evaluation::Evaluation;
use crate::api::contest_submission::{FileInput, Submission};

#[derive(juniper::GraphQLInputObject)]
pub struct ProblemInput {
    name: String,
    archive_content: FileContentInput,
}

#[derive(Insertable)]
#[table_name = "problems"]
pub struct ProblemInsertable {
    name: String,
    archive_integrity: String,
}

#[derive(juniper::GraphQLInputObject)]
pub struct ProblemUpdateInput {
    name: String,
    archive_content: Option<FileContentInput>,
}

#[derive(AsChangeset)]
#[table_name = "problems"]
pub struct ProblemChangeset {
    archive_integrity: Option<String>,
}

#[derive(Queryable, Clone, Debug)]
struct ProblemData {
    name: String,
    archive_integrity: String,
}

/// A problem in the contest
pub struct Problem {
    contest: Contest,
    data: ProblemData,
    material: Material,
}

#[juniper_ext::graphql(Context = ApiContext)]
impl Problem {
    /// The contest this problem belongs to.
    pub fn contest(&self) -> &Contest {
        &self.contest
    }

    /// Name of this problem. Unique in the current contest.
    pub fn name(&self) -> ProblemName {
        ProblemName(self.data.name.clone())
    }

    /// Material of this problem
    pub fn material(&self) -> &Material {
        &self.material
    }

    /// Range of the total score, obtained as the sum of all the score awards
    pub fn score_range(&self) -> ScoreRange {
        ScoreRange::total(self.material.awards.iter().filter_map(
            |award| match award.material.domain {
                AwardDomain::Score(ScoreAwardDomain { range }) => Some(range),
                _ => None,
            },
        ))
    }

    pub fn score_domain(&self) -> ScoreAwardDomain {
        ScoreAwardDomain {
            range: self.score_range(),
        }
    }

    pub fn view(&self, context: &ApiContext, user_id: Option<UserId>) -> FieldResult<ProblemView> {
        context.authorize_user(&user_id)?;
        Ok(ProblemView {
            problem: &self,
            user_id,
        })
    }
}

impl Problem {
    fn new(context: &ApiContext, data: ProblemData) -> FieldResult<Self> {
        let material = Self::get_problem_material(&data, context)?;
        let contest = Contest::current(context)?;
        Ok(Problem {
            contest,
            data,
            material,
        })
    }

    /// Get a problem data by its name
    pub fn by_name(context: &ApiContext, name: &str) -> FieldResult<Self> {
        let data = problems::table.find(name).first(&context.database)?;
        Ok(Self::new(context, data)?)
    }

    /// Get all the problems data in the database
    pub fn all(context: &ApiContext) -> FieldResult<Vec<Self>> {
        Ok(problems::table
            .load::<ProblemData>(&context.database)?
            .into_iter()
            .map(|data| Self::new(context, data))
            .collect::<Result<Vec<_>, _>>()?)
    }

    /// Insert a problem in the database
    pub fn insert(context: &ApiContext, inputs: Vec<ProblemInput>) -> FieldResult<()> {
        for ProblemInput {
            name,
            archive_content,
        } in inputs
        {
            diesel::insert_into(problems::table)
                .values(ProblemInsertable {
                    name,
                    archive_integrity: context.create_blob(&archive_content.decode()?)?,
                })
                .execute(&context.database)?;
        }
        Ok(())
    }

    /// Delete a problem from the database
    pub fn delete(context: &ApiContext, names: Vec<String>) -> FieldResult<()> {
        use crate::diesel::ExpressionMethods;
        diesel::delete(problems::table)
            .filter(problems::dsl::name.eq_any(names))
            .execute(&context.database)?;
        Ok(())
    }

    /// Update a problem in the database
    pub fn update(context: &ApiContext, inputs: Vec<ProblemUpdateInput>) -> FieldResult<()> {
        use crate::diesel::ExpressionMethods;
        for input in inputs {
            diesel::update(problems::table)
                .filter(problems::dsl::name.eq(&input.name))
                .set(&ProblemChangeset {
                    archive_integrity: if let Some(content) = input.archive_content {
                        Some(context.create_blob(&content.decode()?)?)
                    } else {
                        None
                    },
                })
                .execute(&context.database)?;
        }
        Ok(())
    }

    pub fn unpack(&self, context: &ApiContext) -> FieldResult<PathBuf> {
        Self::unpack_data(&self.data, context)
    }

    fn unpack_data(data: &ProblemData, context: &ApiContext) -> FieldResult<PathBuf> {
        context.unpack_archive(&data.archive_integrity, "problem")
    }

    /// Material of this problem
    fn get_problem_material(data: &ProblemData, context: &ApiContext) -> FieldResult<Material> {
        Ok(task_maker::generate_material(Self::unpack_data(
            data, context,
        )?)?)
    }
}

/// A problem in the contest as seen by contestants
pub struct ProblemView<'a> {
    problem: &'a Problem,
    user_id: Option<UserId>,
}

#[juniper_ext::graphql(Context = ApiContext)]
impl<'a> ProblemView<'a> {
    pub fn awards(&self) -> Vec<AwardView> {
        self.problem
            .material()
            .awards
            .iter()
            .map(|award| AwardView {
                user_id: self.user_id.clone(),
                award: (*award).clone(),
                problem: self.problem,
            })
            .collect()
    }

    pub fn grading(&self, context: &ApiContext) -> FieldResult<ScoreAwardGrading> {
        Ok(ScoreAwardGrading {
            domain: self.problem.score_domain(),
            grade: match self.tackling() {
                Some(t) => Some(t.grade(context)?),
                None => None,
            },
        })
    }

    pub fn tackling(&self) -> Option<ProblemTackling<'a>> {
        // TODO: return `None` if user is not participating in the contest
        self.user_id.as_ref().map(|user_id| ProblemTackling {
            problem: &self.problem,
            user_id: (*user_id).clone(),
        })
    }
}

/// Progress at solving a problem by a user in the contest
pub struct ProblemTackling<'a> {
    problem: &'a Problem,
    user_id: UserId,
}

impl ProblemTackling<'_> {
    pub fn submit(&self, context: &ApiContext, files: Vec<FileInput>) -> FieldResult<Submission> {
        if !self.can_submit() {
            return Err("cannot submit now".into());
        }

        let submission =
            Submission::insert(&context, &self.user_id.0, &self.problem.name().0, files)?;
        Evaluation::start_new(&submission, context)?;
        Ok(submission)
    }
}

/// Attempts at solving a problem by a user in the contest
#[juniper_ext::graphql(Context = ApiContext)]
impl ProblemTackling<'_> {
    /// Sum of the score awards
    pub fn score(&self, context: &ApiContext) -> FieldResult<ScoreAwardValue> {
        Ok(ScoreAwardValue::total(
            self.problem
                .view(context, Some(self.user_id.clone()))?
                .awards()
                .into_iter()
                .map(|award_view: AwardView| -> FieldResult<_> {
                    Ok(
                        match award_view
                            .tackling()
                            .ok_or(FieldError::from("problem tackling without award tackling"))?
                            .best_achievement(context)?
                            .value()
                        {
                            AwardValue::Score(value) => Some(value),
                            _ => None,
                        },
                    )
                })
                .filter_map(|x| match x {
                    Ok(None) => None,
                    Ok(Some(x)) => Some(Ok(x)),
                    Err(x) => Some(Err(x)),
                })
                .collect::<FieldResult<Vec<_>>>()?,
        ))
    }

    fn grade(&self, context: &ApiContext) -> FieldResult<ScoreAwardGrade> {
        Ok(ScoreAwardGrade {
            domain: self.problem.score_domain(),
            value: self.score(context)?,
        })
    }

    /// Submissions of the current user (if to be shown)
    fn submissions(
        &self,
        context: &ApiContext,
    ) -> FieldResult<Vec<contest_submission::Submission>> {
        Ok(contest_submission::Submission::by_user_and_problem(
            context,
            &self.user_id.0,
            &self.problem.name().0,
        )?)
    }

    /// Indicates if the user can submit to this problem
    fn can_submit(&self) -> bool {
        match self.problem.contest.status() {
            ContestStatus::Running => true,
            _ => false,
        }
    }
}
<|endoftext|>