<commit_before>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::types::TypeName;
use indoc::indoc;
use lazy_static::lazy_static;
use proc_macro2::Span;
use std::collections::HashMap;
use syn::{parse_quote, Ident, TypePath};

/// Whether this type should be included in the 'prelude'
/// passed to bindgen, and if so, how.
#[derive(Debug)]
enum PreludePolicy {
    Exclude,
    IncludeNormal,
    IncludeTemplated,
}

/// Details about known special types, mostly primitives.
#[derive(Debug)]
struct TypeDetails {
    /// The name used by cxx (in Rust code) for this type.
    rs_name: String,
    /// C++ equivalent name for a Rust type.
    cpp_name: String,
    /// Whether this can be safely represented by value.
    by_value_safe: bool,
    /// Whether and how to include this in the prelude given to bindgen.
    prelude_policy: PreludePolicy,
    /// Whether this is a & on the Rust side but a value on the C++
    /// side. Only applies to &str.
    de_referencicate: bool,
}

impl TypeDetails {
    fn new(
        rs_name: String,
        cpp_name: String,
        by_value_safe: bool,
        prelude_policy: PreludePolicy,
        de_referencicate: bool,
    ) -> Self {
        TypeDetails {
            rs_name,
            cpp_name,
            by_value_safe,
            prelude_policy,
            de_referencicate,
        }
    }

    fn get_prelude_entry(&self) -> Option<String> {
        match self.prelude_policy {
            PreludePolicy::Exclude => None,
            PreludePolicy::IncludeNormal | PreludePolicy::IncludeTemplated => {
                let cxx_name = &self.rs_name;
                let (templating, payload) = match self.prelude_policy {
                    PreludePolicy::IncludeNormal => ("", "char* ptr"),
                    PreludePolicy::IncludeTemplated => ("template<typename T> ", "T* ptr"),
                    _ => unreachable!(),
                };
                Some(format!(
                    indoc! {"
                    /**
                    *

                    */
                    {}class {} {{
                        {};
                    }};

                    "},
                    self.cpp_name, templating, cxx_name, payload
                ))
            }
        }
    }
}

/// Database of known types.
pub(crate) struct TypeDatabase {
    by_rs_name: HashMap<TypeName, TypeDetails>,
    by_cppname: HashMap<String, String>,
}

lazy_static! {
    /// Database of known types.
    static ref KNOWN_TYPES: TypeDatabase = create_type_database();
}

fn create_type_database() -> TypeDatabase {
    let mut by_rs_name = HashMap::new();

    let mut do_insert =
        |td: TypeDetails| by_rs_name.insert(TypeName::new_from_user_input(&td.rs_name), td);

    do_insert(TypeDetails::new(
        "UniquePtr".into(),
        "std::unique_ptr".into(),
        true,
        PreludePolicy::IncludeTemplated,
        false,
    ));
    do_insert(TypeDetails::new(
        "CxxString".into(),
        "std::string".into(),
        false,
        PreludePolicy::IncludeNormal,
        false,
    ));
    do_insert(TypeDetails::new(
        "str".into(),
        "rust::Str".into(),
        true,
        PreludePolicy::IncludeNormal,
        true,
    ));
    do_insert(TypeDetails::new(
        "String".into(),
        "rust::String".into(),
        true,
        PreludePolicy::IncludeNormal,
        false,
    ));
    for (cpp_type, rust_type) in (3..7)
        .map(|x| 2i32.pow(x))
        .map(|x| {
            vec![
                (format!("uint{}_t", x), format!("u{}", x)),
                (format!("int{}_t", x), format!("i{}", x)),
            ]
        })
        .flatten()
    {
        do_insert(TypeDetails::new(
            rust_type,
            cpp_type,
            true,
            PreludePolicy::Exclude,
            false,
        ));
    }
    do_insert(TypeDetails::new(
        "bool".into(),
        "bool".into(),
        true,
        PreludePolicy::Exclude,
        false,
    ));

    let mut insert_ctype = |cname: &str| {
        let td = TypeDetails::new(
            format!("std::os::raw::c_{}", cname),
            cname.into(),
            true,
            PreludePolicy::Exclude,
            false,
        );
        by_rs_name.insert(TypeName::new_from_user_input(&td.rs_name), td);
    };

    insert_ctype("ulong");
    insert_ctype("uint");
    insert_ctype("long");
    insert_ctype("int");

    let mut by_cppname = HashMap::new();
    for td in by_rs_name.values() {
        by_cppname.insert(td.cpp_name.clone(), td.rs_name.clone());
    }

    TypeDatabase {
        by_rs_name,
        by_cppname,
    }
}

/// This is worked out basically using trial and error.
/// Excluding std* and rust* is obvious, but the other items...
/// in theory bindgen ought to be smart enough to work out that
/// they're not used and therefore not generate code for them.
/// But it doesm unless we blocklist them. This is obviously
/// a bit sensitive to the particular STL in use so one day
/// it would be good to dig into bindgen's behavior here - TODO.
const BINDGEN_BLOCKLIST: &[&str] = &["std.*", "__gnu.*", ".*mbstate_t.*", "rust.*"];

/// Prelude of C++ for squirting into bindgen. This configures
/// bindgen to output simpler types to replace some STL types
/// that bindgen just can't cope with. Although we then replace
/// those types with cxx types (e.g. UniquePtr), this intermediate
/// step is still necessary because bindgen can't otherwise
/// give us the templated types (e.g. when faced with the STL
/// unique_ptr, bindgen would normally give us std_unique_ptr
/// as opposed to std_unique_ptr<T>.)
pub(crate) fn get_prelude() -> String {
    itertools::join(
        KNOWN_TYPES
            .by_rs_name
            .values()
            .filter_map(|t| t.get_prelude_entry()),
        "\n",
    )
}

/// Types which are known to be safe (or unsafe) to hold and pass by
/// value in Rust.
pub(crate) fn get_pod_safe_types() -> Vec<(TypeName, bool)> {
    KNOWN_TYPES
        .by_rs_name
        .iter()
        .map(|(_, td)| (TypeName::new_from_user_input(&td.rs_name), td.by_value_safe))
        .collect()
}

/// Get the list of types to give to bindgen to ask it _not_ to
/// generate code for.
pub(crate) fn get_initial_blocklist() -> Vec<String> {
    BINDGEN_BLOCKLIST.iter().map(|s| s.to_string()).collect()
}

/// Whether this TypePath should be treated as a value in C++
/// but a reference in Rust. This only applies to rust::Str
/// (C++ name) which is &str in Rust.
pub(crate) fn should_dereference_in_cpp(typ: &TypePath) -> bool {
    let tn = TypeName::from_cxx_type_path(typ);
    let td = KNOWN_TYPES.by_rs_name.get(&tn);
    if let Some(td) = td {
        td.de_referencicate
    } else {
        false
    }
}

/// Here we substitute any names which we know are Special from
/// our type database, e.g. std::unique_ptr -> UniquePtr.
/// The 'without_arguments' bit means we strip off and ignore
/// any PathArguments within this TypePath - callers should
/// put them back again if needs be.
pub(crate) fn replace_type_path_without_arguments(typ: TypePath) -> TypePath {
    let name = TypeName::from_cxx_type_path(&typ).to_cpp_name();
    match KNOWN_TYPES.by_cppname.get(&name) {
        Some(replacement_name) => {
            let id = make_ident(replacement_name);
            parse_quote! {
                #id
            }
        }
        None => typ,
    }
}

pub(crate) fn special_cpp_name(rs: &TypeName) -> Option<String> {
    KNOWN_TYPES
        .by_rs_name
        .get(rs)
        .map(|x| x.cpp_name.to_string())
}

fn make_ident(id: &str) -> Ident {
    Ident::new(id, Span::call_site())
}
<commit_msg>Remove make_ident duplication.<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::types::{make_ident, TypeName};
use indoc::indoc;
use lazy_static::lazy_static;
use std::collections::HashMap;
use syn::{parse_quote, TypePath};

/// Whether this type should be included in the 'prelude'
/// passed to bindgen, and if so, how.
#[derive(Debug)]
enum PreludePolicy {
    Exclude,
    IncludeNormal,
    IncludeTemplated,
}

/// Details about known special types, mostly primitives.
#[derive(Debug)]
struct TypeDetails {
    /// The name used by cxx (in Rust code) for this type.
    rs_name: String,
    /// C++ equivalent name for a Rust type.
    cpp_name: String,
    /// Whether this can be safely represented by value.
    by_value_safe: bool,
    /// Whether and how to include this in the prelude given to bindgen.
    prelude_policy: PreludePolicy,
    /// Whether this is a & on the Rust side but a value on the C++
    /// side. Only applies to &str.
    de_referencicate: bool,
}

impl TypeDetails {
    fn new(
        rs_name: String,
        cpp_name: String,
        by_value_safe: bool,
        prelude_policy: PreludePolicy,
        de_referencicate: bool,
    ) -> Self {
        TypeDetails {
            rs_name,
            cpp_name,
            by_value_safe,
            prelude_policy,
            de_referencicate,
        }
    }

    fn get_prelude_entry(&self) -> Option<String> {
        match self.prelude_policy {
            PreludePolicy::Exclude => None,
            PreludePolicy::IncludeNormal | PreludePolicy::IncludeTemplated => {
                let cxx_name = &self.rs_name;
                let (templating, payload) = match self.prelude_policy {
                    PreludePolicy::IncludeNormal => ("", "char* ptr"),
                    PreludePolicy::IncludeTemplated => ("template<typename T> ", "T* ptr"),
                    _ => unreachable!(),
                };
                Some(format!(
                    indoc! {"
                    /**
                    *

                    */
                    {}class {} {{
                        {};
                    }};

                    "},
                    self.cpp_name, templating, cxx_name, payload
                ))
            }
        }
    }
}

/// Database of known types.
pub(crate) struct TypeDatabase {
    by_rs_name: HashMap<TypeName, TypeDetails>,
    by_cppname: HashMap<String, String>,
}

lazy_static! {
    /// Database of known types.
    static ref KNOWN_TYPES: TypeDatabase = create_type_database();
}

fn create_type_database() -> TypeDatabase {
    let mut by_rs_name = HashMap::new();

    let mut do_insert =
        |td: TypeDetails| by_rs_name.insert(TypeName::new_from_user_input(&td.rs_name), td);

    do_insert(TypeDetails::new(
        "UniquePtr".into(),
        "std::unique_ptr".into(),
        true,
        PreludePolicy::IncludeTemplated,
        false,
    ));
    do_insert(TypeDetails::new(
        "CxxString".into(),
        "std::string".into(),
        false,
        PreludePolicy::IncludeNormal,
        false,
    ));
    do_insert(TypeDetails::new(
        "str".into(),
        "rust::Str".into(),
        true,
        PreludePolicy::IncludeNormal,
        true,
    ));
    do_insert(TypeDetails::new(
        "String".into(),
        "rust::String".into(),
        true,
        PreludePolicy::IncludeNormal,
        false,
    ));
    for (cpp_type, rust_type) in (3..7)
        .map(|x| 2i32.pow(x))
        .map(|x| {
            vec![
                (format!("uint{}_t", x), format!("u{}", x)),
                (format!("int{}_t", x), format!("i{}", x)),
            ]
        })
        .flatten()
    {
        do_insert(TypeDetails::new(
            rust_type,
            cpp_type,
            true,
            PreludePolicy::Exclude,
            false,
        ));
    }
    do_insert(TypeDetails::new(
        "bool".into(),
        "bool".into(),
        true,
        PreludePolicy::Exclude,
        false,
    ));

    let mut insert_ctype = |cname: &str| {
        let td = TypeDetails::new(
            format!("std::os::raw::c_{}", cname),
            cname.into(),
            true,
            PreludePolicy::Exclude,
            false,
        );
        by_rs_name.insert(TypeName::new_from_user_input(&td.rs_name), td);
    };

    insert_ctype("ulong");
    insert_ctype("uint");
    insert_ctype("long");
    insert_ctype("int");

    let mut by_cppname = HashMap::new();
    for td in by_rs_name.values() {
        by_cppname.insert(td.cpp_name.clone(), td.rs_name.clone());
    }

    TypeDatabase {
        by_rs_name,
        by_cppname,
    }
}

/// This is worked out basically using trial and error.
/// Excluding std* and rust* is obvious, but the other items...
/// in theory bindgen ought to be smart enough to work out that
/// they're not used and therefore not generate code for them.
/// But it doesm unless we blocklist them. This is obviously
/// a bit sensitive to the particular STL in use so one day
/// it would be good to dig into bindgen's behavior here - TODO.
const BINDGEN_BLOCKLIST: &[&str] = &["std.*", "__gnu.*", ".*mbstate_t.*", "rust.*"];

/// Prelude of C++ for squirting into bindgen. This configures
/// bindgen to output simpler types to replace some STL types
/// that bindgen just can't cope with. Although we then replace
/// those types with cxx types (e.g. UniquePtr), this intermediate
/// step is still necessary because bindgen can't otherwise
/// give us the templated types (e.g. when faced with the STL
/// unique_ptr, bindgen would normally give us std_unique_ptr
/// as opposed to std_unique_ptr<T>.)
pub(crate) fn get_prelude() -> String {
    itertools::join(
        KNOWN_TYPES
            .by_rs_name
            .values()
            .filter_map(|t| t.get_prelude_entry()),
        "\n",
    )
}

/// Types which are known to be safe (or unsafe) to hold and pass by
/// value in Rust.
pub(crate) fn get_pod_safe_types() -> Vec<(TypeName, bool)> {
    KNOWN_TYPES
        .by_rs_name
        .iter()
        .map(|(_, td)| (TypeName::new_from_user_input(&td.rs_name), td.by_value_safe))
        .collect()
}

/// Get the list of types to give to bindgen to ask it _not_ to
/// generate code for.
pub(crate) fn get_initial_blocklist() -> Vec<String> {
    BINDGEN_BLOCKLIST.iter().map(|s| s.to_string()).collect()
}

/// Whether this TypePath should be treated as a value in C++
/// but a reference in Rust. This only applies to rust::Str
/// (C++ name) which is &str in Rust.
pub(crate) fn should_dereference_in_cpp(typ: &TypePath) -> bool {
    let tn = TypeName::from_cxx_type_path(typ);
    let td = KNOWN_TYPES.by_rs_name.get(&tn);
    if let Some(td) = td {
        td.de_referencicate
    } else {
        false
    }
}

/// Here we substitute any names which we know are Special from
/// our type database, e.g. std::unique_ptr -> UniquePtr.
/// The 'without_arguments' bit means we strip off and ignore
/// any PathArguments within this TypePath - callers should
/// put them back again if needs be.
pub(crate) fn replace_type_path_without_arguments(typ: TypePath) -> TypePath {
    let name = TypeName::from_cxx_type_path(&typ).to_cpp_name();
    match KNOWN_TYPES.by_cppname.get(&name) {
        Some(replacement_name) => {
            let id = make_ident(replacement_name);
            parse_quote! {
                #id
            }
        }
        None => typ,
    }
}

pub(crate) fn special_cpp_name(rs: &TypeName) -> Option<String> {
    KNOWN_TYPES
        .by_rs_name
        .get(rs)
        .map(|x| x.cpp_name.to_string())
}
<|endoftext|>


<commit_before>use super::Parser;
use ::errors::{self, ResultExt};
use ::GameDescriptor;
use ::default_game_descriptor::DefaultGameDescriptor;
use std::io::{Read, BufRead, BufReader};
use regex::Regex;

/// Parser for files in the life 1.05 format.
pub struct Life106Parser {}

impl Life106Parser {
	/// Construct a new instance.
	pub fn new() -> Self {
		Life106Parser{}
	}
}

impl Parser for Life106Parser {
	fn parse(&mut self, input: Box<Read>) -> errors::Result<Box<GameDescriptor>> {
		let reader = BufReader::new(input);
		let mut ret = DefaultGameDescriptor::new();
		let regex = Regex::new("(\\d+)\\s+(\\d+)")
			.expect("invalid regex!");

		for line in reader.lines() {
			let line = line.chain_err(|| "failed to read line")?;
			let line = line.trim();

			if regex.is_match(line) {
				let coords: Vec<i16> = line.split_whitespace()
					.map(|it| it.parse::<i16>().expect("Error parsing int!"))
					.collect();
				ret.add_live_cell(coords[0], coords[1]);
			}
		}

		Ok(Box::new(ret))
	}
}
<commit_msg>now correctly handling IO errors in 1.06 parser<commit_after>use super::Parser;
use ::errors;
use ::GameDescriptor;
use ::default_game_descriptor::DefaultGameDescriptor;
use std::io::{Read, BufRead, BufReader};
use regex::Regex;

/// Parser for files in the life 1.05 format.
pub struct Life106Parser {}

impl Life106Parser {
	/// Construct a new instance.
	pub fn new() -> Self {
		Life106Parser{}
	}
}

impl Parser for Life106Parser {
	fn parse(&mut self, input: Box<Read>) -> errors::Result<Box<GameDescriptor>> {
		let reader = BufReader::new(input);
		let mut ret = DefaultGameDescriptor::new();
		let regex = Regex::new("(\\d+)\\s+(\\d+)")
			.expect("invalid regex!");

		for line in reader.lines() {
			let line = line.
				map_err(|err| errors::ErrorKind::IOError(err.kind()))?;
			let line = line.trim();

			if regex.is_match(line) {
				let coords: Vec<i16> = line.split_whitespace()
					.map(|it| it.parse::<i16>().expect("Error parsing int!"))
					.collect();
				ret.add_live_cell(coords[0], coords[1]);
			}
		}

		Ok(Box::new(ret))
	}
}

#[cfg(test)]
mod test {
	extern crate io_test_util;

	use super::*;
	use std::io;

	#[test]
	fn should_correctly_handle_io_errors() {
		use self::io_test_util;

		let mut parser = Life106Parser::new();
		let reader = io_test_util::ErrReader::new(io::ErrorKind::NotFound);

		if let Err(parser_error) = parser.parse(Box::new(reader)) {
			match *parser_error.kind() {
				errors::ErrorKind::IOError(inner_error) => {
					assert_eq!(io::ErrorKind::NotFound, inner_error);
				},
				_ => panic!("wrong error kind!")
			}
		} else {
			panic!("No error returned!")
		}
	}
}
<|endoftext|>


<commit_before>use store::FileLockEntry;
use storeid::StoreId;
use hook::Hook;
use hook::result::HookResult;
use hook::accessor::{StoreIdAccessor, MutableHookDataAccessor, NonMutableHookDataAccessor};
use hook::accessor::HookDataAccessor as HDA;

#[derive(Debug)]
pub struct Aspect {
    name: String,
    hooks: Vec<Box<Hook>>,
}

impl Aspect {

    pub fn new(name: String) -> Aspect {
        Aspect {
            name: name,
            hooks: vec![],
        }
    }

    pub fn name(&self) -> &String {
        &self.name
    }

    pub fn register_hook(&mut self, h: Box<Hook>) {
        self.hooks.push(h);
    }

}

impl StoreIdAccessor for Aspect {
    fn access(&self, id: &StoreId) -> HookResult<()> {
        let accessors : Vec<HDA> = self.hooks.iter().map(|h| h.accessor()).collect();
        if !accessors.iter().all(|a| match a { &HDA::StoreIdAccess(_)  => true, _ => false }) {
            unimplemented!()
        }

        for accessor in accessors {
            match accessor {
                HDA::StoreIdAccess(accessor) => try!(accessor.access(id)),
                _ => unreachable!(),
            }
        }
        Ok(())
    }
}

impl MutableHookDataAccessor for Aspect {
    fn access_mut(&self, fle: &mut FileLockEntry) -> HookResult<()> {
        let accessors : Vec<HDA> = self.hooks.iter().map(|h| h.accessor()).collect();
        if !accessors.iter().all(|a| match a { &HDA::MutableAccess(_)  => true, _ => false }) {
            unimplemented!()
        }

        for accessor in accessors {
            match accessor {
                HDA::MutableAccess(accessor) => try!(accessor.access_mut(fle)),
                _ => unreachable!(),
            }
        }
        Ok(())
    }
}

impl NonMutableHookDataAccessor for Aspect {
    fn access(&self, fle: &FileLockEntry) -> HookResult<()> {
        unimplemented!()
    }
}

<commit_msg>Impl NonMutableHookDataAccessor::access() for Aspect<commit_after>use store::FileLockEntry;
use storeid::StoreId;
use hook::Hook;
use hook::result::HookResult;
use hook::accessor::{StoreIdAccessor, MutableHookDataAccessor, NonMutableHookDataAccessor};
use hook::accessor::HookDataAccessor as HDA;

#[derive(Debug)]
pub struct Aspect {
    name: String,
    hooks: Vec<Box<Hook>>,
}

impl Aspect {

    pub fn new(name: String) -> Aspect {
        Aspect {
            name: name,
            hooks: vec![],
        }
    }

    pub fn name(&self) -> &String {
        &self.name
    }

    pub fn register_hook(&mut self, h: Box<Hook>) {
        self.hooks.push(h);
    }

}

impl StoreIdAccessor for Aspect {
    fn access(&self, id: &StoreId) -> HookResult<()> {
        let accessors : Vec<HDA> = self.hooks.iter().map(|h| h.accessor()).collect();
        if !accessors.iter().all(|a| match a { &HDA::StoreIdAccess(_)  => true, _ => false }) {
            unimplemented!()
        }

        for accessor in accessors {
            match accessor {
                HDA::StoreIdAccess(accessor) => try!(accessor.access(id)),
                _ => unreachable!(),
            }
        }
        Ok(())
    }
}

impl MutableHookDataAccessor for Aspect {
    fn access_mut(&self, fle: &mut FileLockEntry) -> HookResult<()> {
        let accessors : Vec<HDA> = self.hooks.iter().map(|h| h.accessor()).collect();
        if !accessors.iter().all(|a| match a { &HDA::MutableAccess(_)  => true, _ => false }) {
            unimplemented!()
        }

        for accessor in accessors {
            match accessor {
                HDA::MutableAccess(accessor) => try!(accessor.access_mut(fle)),
                _ => unreachable!(),
            }
        }
        Ok(())
    }
}

impl NonMutableHookDataAccessor for Aspect {
    fn access(&self, fle: &FileLockEntry) -> HookResult<()> {
        let accessors : Vec<HDA> = self.hooks.iter().map(|h| h.accessor()).collect();
        if !accessors.iter().all(|a| match a { &HDA::NonMutableAccess(_)  => true, _ => false }) {
            unimplemented!()
        }

        for accessor in accessors {
            match accessor {
                HDA::NonMutableAccess(accessor) => try!(accessor.access(fle)),
                _ => unreachable!(),
            }
        }
        Ok(())
    }
}

<|endoftext|>


<commit_before>/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::build_project::{build_project, commit_project, BuildProjectFailure};
use crate::config::Config;
use crate::errors::{Error, Result};
use crate::file_source::FileSource;
use crate::graphql_asts::GraphQLAsts;
use crate::red_to_green::RedToGreen;
use crate::{
    compiler_state::{ArtifactMapKind, CompilerState},
    file_source::FileSourceSubscriptionNextChange,
    FileSourceResult,
};
use common::{PerfLogEvent, PerfLogger};
use futures::future::join_all;
use graphql_watchman::WatchmanFileSourceSubscriptionNextChange;
use log::{debug, info};
use rayon::prelude::*;
use std::sync::Arc;
use tokio::{
    sync::Notify,
    task::{self, JoinHandle},
};

pub struct Compiler<TPerfLogger>
where
    TPerfLogger: PerfLogger + 'static,
{
    config: Arc<Config>,
    perf_logger: Arc<TPerfLogger>,
}

impl<TPerfLogger: PerfLogger> Compiler<TPerfLogger> {
    pub fn new(config: Arc<Config>, perf_logger: Arc<TPerfLogger>) -> Self {
        Self {
            config,
            perf_logger,
        }
    }

    pub async fn compile(&self) -> Result<CompilerState> {
        let setup_event = self.perf_logger.create_event("compiler_setup");
        self.config.status_reporter.build_starts();
        let result: Result<CompilerState> = async {
            let file_source = FileSource::connect(&self.config, &setup_event).await?;
            let mut compiler_state = file_source
                .query(&setup_event, self.perf_logger.as_ref())
                .await?;

            self.build_projects(&mut compiler_state, &setup_event)
                .await?;

            Ok(compiler_state)
        }
        .await;
        setup_event.complete();

        match result {
            Ok(compiler_state) => {
                self.config.status_reporter.build_completes();
                Ok(compiler_state)
            }
            Err(error) => {
                self.config.status_reporter.build_errors(&error);
                Err(error)
            }
        }
    }

    pub async fn watch(&self) -> Result<()> {
        'watch: loop {
            let setup_event = self.perf_logger.create_event("compiler_setup");
            self.config.status_reporter.build_starts();
            let result: Result<(CompilerState, Arc<Notify>, JoinHandle<()>)> = async {
                let file_source = FileSource::connect(&self.config, &setup_event).await?;

                let (compiler_state, mut subscription) = file_source
                    .subscribe(&setup_event, self.perf_logger.as_ref())
                    .await?;

                let pending_file_source_changes =
                    compiler_state.pending_file_source_changes.clone();
                let source_control_update_status =
                    Arc::clone(&compiler_state.source_control_update_status);

                let notify_sender = Arc::new(Notify::new());
                let notify_receiver = notify_sender.clone();

                // First, set up watchman subscription
                let subscription_handle = task::spawn(async move {
                    loop {
                        let next_change = subscription.next_change().await;
                        match next_change {
                            Ok(FileSourceSubscriptionNextChange::Watchman(watchman_next_change)) => {
                                match watchman_next_change {
                                    WatchmanFileSourceSubscriptionNextChange::Result(file_source_changes) => {
                                        pending_file_source_changes
                                            .write()
                                            .unwrap()
                                            .push(FileSourceResult::Watchman(file_source_changes));
                                        notify_sender.notify_one();
                                    }
                                    WatchmanFileSourceSubscriptionNextChange::SourceControlUpdateEnter => {
                                        info!("hg.update started...");
                                        source_control_update_status.mark_as_started();
                                    }
                                    WatchmanFileSourceSubscriptionNextChange::SourceControlUpdateLeave => {
                                        info!("hg.update completed.");
                                        source_control_update_status.set_to_default();
                                    }
                                    WatchmanFileSourceSubscriptionNextChange::SourceControlUpdate => {
                                        info!("hg.update completed. Detected new base revision...");
                                        source_control_update_status.mark_as_completed();
                                        notify_sender.notify_one();
                                        break;
                                    }
                                    WatchmanFileSourceSubscriptionNextChange::None => {}
                                }
                            }
                            Err(err) => {
                                panic!("Watchman subscription error: {}", err);
                            }
                        }
                    }
                });

                Ok((compiler_state, notify_receiver, subscription_handle))

            }
            .await;

            match result {
                Ok((mut compiler_state, notify_receiver, subscription_handle)) => {
                    let mut red_to_green = RedToGreen::new();
                    if let Err(err) = self.build_projects(&mut compiler_state, &setup_event).await {
                        red_to_green.log_error();
                        self.config.status_reporter.build_errors(&err);
                    } else {
                        info!("Compilation completed.");
                        self.config.status_reporter.build_completes();
                    }
                    setup_event.complete();
                    info!("Waiting for changes...");

                    self.incremental_build_loop(
                        compiler_state,
                        notify_receiver,
                        &subscription_handle,
                    )
                    .await;
                }
                Err(err) => {
                    self.config.status_reporter.build_errors(&err);
                    break 'watch Err(err);
                }
            }
        }
    }

    async fn incremental_build_loop(
        &self,
        mut compiler_state: CompilerState,
        notify_receiver: Arc<Notify>,
        subscription_handle: &JoinHandle<()>,
    ) {
        let mut red_to_green = RedToGreen::new();

        loop {
            notify_receiver.notified().await;

            if compiler_state.source_control_update_status.is_started() {
                continue;
            }

            if compiler_state.source_control_update_status.is_completed() {
                subscription_handle.abort();
                return;
            }

            // Single change to file sometimes produces 2 watchman change events for the same file
            // wait for 50ms in case there is a subsequent request
            tokio::time::sleep(std::time::Duration::from_millis(50)).await;

            if compiler_state.has_pending_file_source_changes() {
                let incremental_build_event =
                    self.perf_logger.create_event("incremental_build_event");
                let incremental_build_time =
                    incremental_build_event.start("incremental_build_time");

                let had_new_changes = match compiler_state.merge_file_source_changes(
                    &self.config,
                    self.perf_logger.as_ref(),
                    false,
                ) {
                    Ok(b) => b,
                    Err(err) => {
                        let error_event = self.perf_logger.create_event("watch_build_error");
                        error_event.string("error", format!("Ignored Compilation Error: {}", err));
                        error_event.complete();
                        return;
                    }
                };

                if had_new_changes {
                    self.config.status_reporter.build_starts();
                    info!("Change detected, start compiling...");
                    if let Err(err) = self
                        .build_projects(&mut compiler_state, &incremental_build_event)
                        .await
                    {
                        red_to_green.log_error();
                        self.config.status_reporter.build_errors(&err);
                    } else {
                        info!("Compilation completed.");
                        self.config.status_reporter.build_completes();
                        red_to_green.clear_error_and_log(self.perf_logger.as_ref());
                    }
                    incremental_build_event.stop(incremental_build_time);
                    info!("Compilation completed.");
                    info!("Watching for new changes...");
                } else {
                    debug!("No new changes detected.");
                    incremental_build_event.stop(incremental_build_time);
                }
                incremental_build_event.complete();
            }
        }
    }

    async fn build_projects(
        &self,
        compiler_state: &mut CompilerState,
        setup_event: &impl PerfLogEvent,
    ) -> Result<()> {
        let build_projects_time = setup_event.start("build_projects_time");
        let result = build_projects(
            Arc::clone(&self.config),
            Arc::clone(&self.perf_logger),
            setup_event,
            compiler_state,
        )
        .await;
        setup_event.stop(build_projects_time);
        setup_event.time("post_build_projects_time", || {
            result.and_then(|_| {
                compiler_state.complete_compilation();
                self.config.artifact_writer.finalize()?;
                if let Some(post_artifacts_write) = &self.config.post_artifacts_write {
                    post_artifacts_write(&self.config)
                        .map_err(|error| Error::PostArtifactsError { error })?;
                }

                Ok(())
            })
        })
    }
}

async fn build_projects<TPerfLogger: PerfLogger + 'static>(
    config: Arc<Config>,
    perf_logger: Arc<TPerfLogger>,
    setup_event: &impl PerfLogEvent,
    compiler_state: &mut CompilerState,
) -> Result<()> {
    let mut graphql_asts = setup_event.time("parse_sources_time", || {
        GraphQLAsts::from_graphql_sources_map(
            &compiler_state.graphql_sources,
            &compiler_state.get_dirty_definitions(&config),
        )
    })?;

    if compiler_state.should_cancel_current_build() {
        debug!("Build is cancelled: new file changes are pending.");
        return Err(Error::Cancelled);
    }

    let build_results: Vec<_> = config
        .par_enabled_projects()
        .filter(|project_config| {
            if let Some(base) = project_config.base {
                if compiler_state.project_has_pending_changes(base) {
                    return true;
                }
            }
            compiler_state.project_has_pending_changes(project_config.name)
        })
        .map(|project_config| {
            build_project(
                &config,
                project_config,
                compiler_state,
                &graphql_asts,
                Arc::clone(&perf_logger),
            )
        })
        .collect();
    let mut results = Vec::new();
    let mut errors = Vec::new();
    for result in build_results {
        match result {
            Ok(result) => results.push(result),
            Err(error) => match error {
                BuildProjectFailure::Error(error) => errors.push(error),
                BuildProjectFailure::Cancelled => {
                    return Err(Error::Cancelled);
                }
            },
        }
    }

    let mut build_cancelled_during_commit = false;
    if errors.is_empty() {
        if compiler_state.should_cancel_current_build() {
            debug!("Build is cancelled: updates in source code/or new file changes are pending.");
            return Err(Error::Cancelled);
        }

        let mut handles: Vec<JoinHandle<std::result::Result<_, BuildProjectFailure>>> = Vec::new();
        for (project_name, schema, programs, artifacts) in results {
            let config = Arc::clone(&config);
            let perf_logger = Arc::clone(&perf_logger);
            let artifact_map = compiler_state
                .artifacts
                .get(&project_name)
                .cloned()
                .unwrap_or_else(|| Arc::new(ArtifactMapKind::Unconnected(Default::default())));
            let removed_definition_names = graphql_asts
                .remove(&project_name)
                .expect("Expect GraphQLAsts to exist.")
                .removed_definition_names;
            let dirty_artifact_paths = compiler_state
                .dirty_artifact_paths
                .get(&project_name)
                .cloned()
                .unwrap_or_default();

            let source_control_update_status =
                Arc::clone(&compiler_state.source_control_update_status);
            handles.push(task::spawn(async move {
                let project_config = &config.projects[&project_name];
                Ok((
                    project_name,
                    commit_project(
                        &config,
                        project_config,
                        perf_logger,
                        &schema,
                        programs,
                        artifacts,
                        artifact_map,
                        removed_definition_names,
                        dirty_artifact_paths,
                        source_control_update_status,
                    )
                    .await?,
                    schema,
                ))
            }));
        }

        for commit_result in join_all(handles).await {
            let commit_result: std::result::Result<std::result::Result<_, _>, _> = commit_result;
            let inner_result = commit_result.map_err(|e| Error::JoinError {
                error: e.to_string(),
            })?;
            match inner_result {
                Ok((project_name, next_artifact_map, schema)) => {
                    let next_artifact_map = Arc::new(ArtifactMapKind::Mapping(next_artifact_map));
                    compiler_state
                        .artifacts
                        .insert(project_name, next_artifact_map);
                    compiler_state.schema_cache.insert(project_name, schema);
                }
                Err(BuildProjectFailure::Error(error)) => {
                    errors.push(error);
                }
                Err(BuildProjectFailure::Cancelled) => {
                    build_cancelled_during_commit = true;
                }
            }
        }
    }

    if errors.is_empty() {
        match build_cancelled_during_commit {
            true => Err(Error::Cancelled),
            false => Ok(()),
        }
    } else {
        Err(Error::BuildProjectsErrors { errors })
    }
}
<commit_msg>Avoid duplicate output<commit_after>/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::build_project::{build_project, commit_project, BuildProjectFailure};
use crate::config::Config;
use crate::errors::{Error, Result};
use crate::file_source::FileSource;
use crate::graphql_asts::GraphQLAsts;
use crate::red_to_green::RedToGreen;
use crate::{
    compiler_state::{ArtifactMapKind, CompilerState},
    file_source::FileSourceSubscriptionNextChange,
    FileSourceResult,
};
use common::{PerfLogEvent, PerfLogger};
use futures::future::join_all;
use graphql_watchman::WatchmanFileSourceSubscriptionNextChange;
use log::{debug, info};
use rayon::prelude::*;
use std::sync::Arc;
use tokio::{
    sync::Notify,
    task::{self, JoinHandle},
};

pub struct Compiler<TPerfLogger>
where
    TPerfLogger: PerfLogger + 'static,
{
    config: Arc<Config>,
    perf_logger: Arc<TPerfLogger>,
}

impl<TPerfLogger: PerfLogger> Compiler<TPerfLogger> {
    pub fn new(config: Arc<Config>, perf_logger: Arc<TPerfLogger>) -> Self {
        Self {
            config,
            perf_logger,
        }
    }

    pub async fn compile(&self) -> Result<CompilerState> {
        let setup_event = self.perf_logger.create_event("compiler_setup");
        self.config.status_reporter.build_starts();
        let result: Result<CompilerState> = async {
            let file_source = FileSource::connect(&self.config, &setup_event).await?;
            let mut compiler_state = file_source
                .query(&setup_event, self.perf_logger.as_ref())
                .await?;

            self.build_projects(&mut compiler_state, &setup_event)
                .await?;

            Ok(compiler_state)
        }
        .await;
        setup_event.complete();

        match result {
            Ok(compiler_state) => {
                self.config.status_reporter.build_completes();
                Ok(compiler_state)
            }
            Err(error) => {
                self.config.status_reporter.build_errors(&error);
                Err(error)
            }
        }
    }

    pub async fn watch(&self) -> Result<()> {
        'watch: loop {
            let setup_event = self.perf_logger.create_event("compiler_setup");
            self.config.status_reporter.build_starts();
            let result: Result<(CompilerState, Arc<Notify>, JoinHandle<()>)> = async {
                let file_source = FileSource::connect(&self.config, &setup_event).await?;

                let (compiler_state, mut subscription) = file_source
                    .subscribe(&setup_event, self.perf_logger.as_ref())
                    .await?;

                let pending_file_source_changes =
                    compiler_state.pending_file_source_changes.clone();
                let source_control_update_status =
                    Arc::clone(&compiler_state.source_control_update_status);

                let notify_sender = Arc::new(Notify::new());
                let notify_receiver = notify_sender.clone();

                // First, set up watchman subscription
                let subscription_handle = task::spawn(async move {
                    loop {
                        let next_change = subscription.next_change().await;
                        match next_change {
                            Ok(FileSourceSubscriptionNextChange::Watchman(watchman_next_change)) => {
                                match watchman_next_change {
                                    WatchmanFileSourceSubscriptionNextChange::Result(file_source_changes) => {
                                        pending_file_source_changes
                                            .write()
                                            .unwrap()
                                            .push(FileSourceResult::Watchman(file_source_changes));
                                        notify_sender.notify_one();
                                    }
                                    WatchmanFileSourceSubscriptionNextChange::SourceControlUpdateEnter => {
                                        info!("hg.update started...");
                                        source_control_update_status.mark_as_started();
                                    }
                                    WatchmanFileSourceSubscriptionNextChange::SourceControlUpdateLeave => {
                                        info!("hg.update completed.");
                                        source_control_update_status.set_to_default();
                                    }
                                    WatchmanFileSourceSubscriptionNextChange::SourceControlUpdate => {
                                        info!("hg.update completed. Detected new base revision...");
                                        source_control_update_status.mark_as_completed();
                                        notify_sender.notify_one();
                                        break;
                                    }
                                    WatchmanFileSourceSubscriptionNextChange::None => {}
                                }
                            }
                            Err(err) => {
                                panic!("Watchman subscription error: {}", err);
                            }
                        }
                    }
                });

                Ok((compiler_state, notify_receiver, subscription_handle))

            }
            .await;

            match result {
                Ok((mut compiler_state, notify_receiver, subscription_handle)) => {
                    let mut red_to_green = RedToGreen::new();
                    if let Err(err) = self.build_projects(&mut compiler_state, &setup_event).await {
                        red_to_green.log_error();
                        self.config.status_reporter.build_errors(&err);
                    } else {
                        info!("Compilation completed.");
                        self.config.status_reporter.build_completes();
                    }
                    setup_event.complete();
                    info!("Watching for new changes...");

                    self.incremental_build_loop(
                        compiler_state,
                        notify_receiver,
                        &subscription_handle,
                    )
                    .await;
                }
                Err(err) => {
                    self.config.status_reporter.build_errors(&err);
                    break 'watch Err(err);
                }
            }
        }
    }

    async fn incremental_build_loop(
        &self,
        mut compiler_state: CompilerState,
        notify_receiver: Arc<Notify>,
        subscription_handle: &JoinHandle<()>,
    ) {
        let mut red_to_green = RedToGreen::new();

        loop {
            notify_receiver.notified().await;

            if compiler_state.source_control_update_status.is_started() {
                continue;
            }

            if compiler_state.source_control_update_status.is_completed() {
                subscription_handle.abort();
                return;
            }

            // Single change to file sometimes produces 2 watchman change events for the same file
            // wait for 50ms in case there is a subsequent request
            tokio::time::sleep(std::time::Duration::from_millis(50)).await;

            if compiler_state.has_pending_file_source_changes() {
                let incremental_build_event =
                    self.perf_logger.create_event("incremental_build_event");
                let incremental_build_time =
                    incremental_build_event.start("incremental_build_time");

                let had_new_changes = match compiler_state.merge_file_source_changes(
                    &self.config,
                    self.perf_logger.as_ref(),
                    false,
                ) {
                    Ok(b) => b,
                    Err(err) => {
                        let error_event = self.perf_logger.create_event("watch_build_error");
                        error_event.string("error", format!("Ignored Compilation Error: {}", err));
                        error_event.complete();
                        return;
                    }
                };

                if had_new_changes {
                    self.config.status_reporter.build_starts();
                    info!("Change detected, start compiling...");
                    if let Err(err) = self
                        .build_projects(&mut compiler_state, &incremental_build_event)
                        .await
                    {
                        red_to_green.log_error();
                        self.config.status_reporter.build_errors(&err);
                    } else {
                        info!("Compilation completed.");
                        self.config.status_reporter.build_completes();
                        red_to_green.clear_error_and_log(self.perf_logger.as_ref());
                    }
                    incremental_build_event.stop(incremental_build_time);
                    info!("Watching for new changes...");
                } else {
                    debug!("No new changes detected.");
                    incremental_build_event.stop(incremental_build_time);
                }
                incremental_build_event.complete();
            }
        }
    }

    async fn build_projects(
        &self,
        compiler_state: &mut CompilerState,
        setup_event: &impl PerfLogEvent,
    ) -> Result<()> {
        let build_projects_time = setup_event.start("build_projects_time");
        let result = build_projects(
            Arc::clone(&self.config),
            Arc::clone(&self.perf_logger),
            setup_event,
            compiler_state,
        )
        .await;
        setup_event.stop(build_projects_time);
        setup_event.time("post_build_projects_time", || {
            result.and_then(|_| {
                compiler_state.complete_compilation();
                self.config.artifact_writer.finalize()?;
                if let Some(post_artifacts_write) = &self.config.post_artifacts_write {
                    post_artifacts_write(&self.config)
                        .map_err(|error| Error::PostArtifactsError { error })?;
                }

                Ok(())
            })
        })
    }
}

async fn build_projects<TPerfLogger: PerfLogger + 'static>(
    config: Arc<Config>,
    perf_logger: Arc<TPerfLogger>,
    setup_event: &impl PerfLogEvent,
    compiler_state: &mut CompilerState,
) -> Result<()> {
    let mut graphql_asts = setup_event.time("parse_sources_time", || {
        GraphQLAsts::from_graphql_sources_map(
            &compiler_state.graphql_sources,
            &compiler_state.get_dirty_definitions(&config),
        )
    })?;

    if compiler_state.should_cancel_current_build() {
        debug!("Build is cancelled: new file changes are pending.");
        return Err(Error::Cancelled);
    }

    let build_results: Vec<_> = config
        .par_enabled_projects()
        .filter(|project_config| {
            if let Some(base) = project_config.base {
                if compiler_state.project_has_pending_changes(base) {
                    return true;
                }
            }
            compiler_state.project_has_pending_changes(project_config.name)
        })
        .map(|project_config| {
            build_project(
                &config,
                project_config,
                compiler_state,
                &graphql_asts,
                Arc::clone(&perf_logger),
            )
        })
        .collect();
    let mut results = Vec::new();
    let mut errors = Vec::new();
    for result in build_results {
        match result {
            Ok(result) => results.push(result),
            Err(error) => match error {
                BuildProjectFailure::Error(error) => errors.push(error),
                BuildProjectFailure::Cancelled => {
                    return Err(Error::Cancelled);
                }
            },
        }
    }

    let mut build_cancelled_during_commit = false;
    if errors.is_empty() {
        if compiler_state.should_cancel_current_build() {
            debug!("Build is cancelled: updates in source code/or new file changes are pending.");
            return Err(Error::Cancelled);
        }

        let mut handles: Vec<JoinHandle<std::result::Result<_, BuildProjectFailure>>> = Vec::new();
        for (project_name, schema, programs, artifacts) in results {
            let config = Arc::clone(&config);
            let perf_logger = Arc::clone(&perf_logger);
            let artifact_map = compiler_state
                .artifacts
                .get(&project_name)
                .cloned()
                .unwrap_or_else(|| Arc::new(ArtifactMapKind::Unconnected(Default::default())));
            let removed_definition_names = graphql_asts
                .remove(&project_name)
                .expect("Expect GraphQLAsts to exist.")
                .removed_definition_names;
            let dirty_artifact_paths = compiler_state
                .dirty_artifact_paths
                .get(&project_name)
                .cloned()
                .unwrap_or_default();

            let source_control_update_status =
                Arc::clone(&compiler_state.source_control_update_status);
            handles.push(task::spawn(async move {
                let project_config = &config.projects[&project_name];
                Ok((
                    project_name,
                    commit_project(
                        &config,
                        project_config,
                        perf_logger,
                        &schema,
                        programs,
                        artifacts,
                        artifact_map,
                        removed_definition_names,
                        dirty_artifact_paths,
                        source_control_update_status,
                    )
                    .await?,
                    schema,
                ))
            }));
        }

        for commit_result in join_all(handles).await {
            let commit_result: std::result::Result<std::result::Result<_, _>, _> = commit_result;
            let inner_result = commit_result.map_err(|e| Error::JoinError {
                error: e.to_string(),
            })?;
            match inner_result {
                Ok((project_name, next_artifact_map, schema)) => {
                    let next_artifact_map = Arc::new(ArtifactMapKind::Mapping(next_artifact_map));
                    compiler_state
                        .artifacts
                        .insert(project_name, next_artifact_map);
                    compiler_state.schema_cache.insert(project_name, schema);
                }
                Err(BuildProjectFailure::Error(error)) => {
                    errors.push(error);
                }
                Err(BuildProjectFailure::Cancelled) => {
                    build_cancelled_during_commit = true;
                }
            }
        }
    }

    if errors.is_empty() {
        match build_cancelled_during_commit {
            true => Err(Error::Cancelled),
            false => Ok(()),
        }
    } else {
        Err(Error::BuildProjectsErrors { errors })
    }
}
<|endoftext|>


<commit_before>use ggez::{Context, GameResult, graphics, event};
use std::time::Duration;

mod well;
mod tetromino;
mod shapes;
mod bag;
mod util;

use ggez::event::*;
use self::well::Well;
use self::tetromino::Piece;
use self::bag::PieceBag;
use self::util::DurationExt;

const BLOCK_SIZE: f32 = 30.0;
const FALL_SPEED: f64 = 0.5;
const INPUT_DELAY_TIME: f64 = 0.05;

#[derive(Copy, Clone, Debug)]
pub struct Position {
    x: i32,
    y: i32,
}

#[derive(Debug)]
pub struct InputStateField {
    is_active: bool,
    delay_timer: f64,
}

impl Default for InputStateField {
    fn default() -> Self {
        InputStateField {
            is_active: false,
            delay_timer: INPUT_DELAY_TIME,
        }
    }
}

#[derive(Debug)]
pub struct InputState {
    left: InputStateField,
    right: InputStateField,
    soft_drop: InputStateField,
    hard_drop: InputStateField,
    rotate_clockwise: InputStateField,
    rotate_counterclockwise: InputStateField,
    drop: InputStateField,
}

impl Default for InputState {
    fn default() -> Self {

        InputState {
            left: InputStateField::default(),
            right: InputStateField::default(),
            soft_drop: InputStateField::default(),
            hard_drop: InputStateField::default(),
            rotate_clockwise: InputStateField::default(),
            rotate_counterclockwise: InputStateField::default(),
            drop: InputStateField::default(),
        }
    }
}

#[derive(Debug, PartialEq)]
enum Command {
    Left,
    None,
}

pub struct PlayState {
    current_command: Command,
    input: InputState,

    well: Well,
    bag: PieceBag,
    current_piece: Piece,

    fall_timer: f64,
    score: u32,
    cleared_lines: u16,
    level: u32,
    game_over: bool,

    // temp
    image: graphics::Image,
}

impl PlayState {
    pub fn new(ctx: &mut Context) -> GameResult<PlayState> {
        let image = graphics::Image::new(ctx, "/block.png")?;
        let mut bag = PieceBag::new();
        let first_piece = bag.take_piece();

        let state = PlayState {
            current_command: Command::None,
            input: InputState::default(),

            well: Well::new(),
            bag: bag,
            current_piece: first_piece,

            fall_timer: 0.0,
            score: 0,
            cleared_lines: 0,
            level: 0,
            game_over: false,

            image: image,
        };

        Ok(state)
    }

    fn handle_user_input(&mut self, dt: Duration) -> GameResult<()> {
        if self.input.left.is_active {
            self.input.left.delay_timer += dt.as_subsec_millis();
            if self.input.left.delay_timer >= INPUT_DELAY_TIME {
                self.current_piece.potential_top_left.x -= 1;
                self.input.left.delay_timer = 0.0;
            }
        } else if self.input.right.is_active {
            self.input.right.delay_timer += dt.as_subsec_millis();
            if self.input.right.delay_timer >= INPUT_DELAY_TIME {
                self.current_piece.potential_top_left.x += 1;
                self.input.right.delay_timer = 0.0;
            }
        } else if self.input.soft_drop.is_active {
            self.input.soft_drop.delay_timer += dt.as_subsec_millis();
            if self.input.soft_drop.delay_timer >= INPUT_DELAY_TIME {
                self.current_piece.potential_top_left.y += 1;
                self.input.soft_drop.delay_timer = 0.0;
            }
        }

        Ok(())
    }

    /// Advance the fall time. If enough time has passed, allow gravity to
    /// affect the current piece.
    /// Returns a `GameResult<false>` if a landing occured.
    /// Returns a `GameResult<true>` if no landing occured and the piece can advance.
    fn handle_gravity(&mut self, dt: Duration) -> GameResult<bool> {
        self.fall_timer += dt.as_subsec_millis();

        if self.fall_timer >= FALL_SPEED {
            let current_shape = self.current_piece.get_shape();
            self.fall_timer = 0.0;
            self.current_piece.potential_top_left.y += 1;

            let did_land = self.well
                .check_for_landing(&current_shape, &self.current_piece.potential_top_left);

            if did_land {
                if self.current_piece.top_left.y < 2 {
                    println!("game over!");
                    self.game_over = true;
                    return Ok(false);
                }

                // game isn't over - take another piece and move to next frame
                self.well.land(&self.current_piece);
                self.current_piece = self.bag.take_piece();
                return Ok(false);
            }

            // piece did not land - advance!
            self.current_piece.top_left = self.current_piece.potential_top_left;
        }

        Ok(true)
    }

    fn handle_shadow_piece(&mut self) -> GameResult<()> {
        let mut shadow_position = self.current_piece.top_left;
        let mut potential_shadow_position = shadow_position;
        loop {
            potential_shadow_position.y += 1;
            let collision_found = self.well
                .check_for_landing(&self.current_piece.get_shape(), &potential_shadow_position);

            if collision_found {
                break;
            }

            shadow_position = potential_shadow_position;
            self.current_piece.set_shadow_position(shadow_position);
        }

        Ok(())
    }

    fn handle_line_clears(&mut self) -> GameResult<()> {
        // check for line clears
        let mut lines_cleared = 0;
        for r in (0..self.well.data.len()).rev() {
            let mut is_row_filled = true;
            for (c, _) in self.well.data[r].iter().enumerate() {
                if self.well.data[r][c] == 0 {
                    is_row_filled = false;
                    break; // no need to continue iterating, line is not clear...
                }
            }

            if is_row_filled {
                // TODO: implement more line clearing algorithms
                // TODO: make the current line clearing algorithm user selectable
                self.well.naive_line_clear(r);
                lines_cleared += 1;
            }
        }

        // add to score
        let level = self.level;
        match lines_cleared {
            1 => self.increase_score(40, level),
            2 => self.increase_score(100, level),
            3 => self.increase_score(300, level),
            4 => self.increase_score(1200, level),
            _ => (),
        }

        self.cleared_lines += lines_cleared;

        Ok(())
    }

    fn increase_score(&mut self, base_score: u32, level: u32) {
        self.score += base_score * (level + 1);
    }

    fn handle_collisions(&mut self) -> GameResult<()> {
        if self.input.left.is_active || self.input.right.is_active ||
           self.input.soft_drop.is_active {
            let current_shape = self.current_piece.get_shape();
            let collision_found = self.well
                .check_for_collisions(&current_shape, &self.current_piece.potential_top_left);

            if collision_found {
                self.current_piece.potential_top_left = self.current_piece.top_left;
            }

            self.current_piece.top_left = self.current_piece.potential_top_left; // advance tetromino
            Ok(())
        } else if self.input.rotate_clockwise.is_active ||
                  self.input.rotate_counterclockwise.is_active {
            let next_shape = self.current_piece.get_next_shape();
            let collision_found = self.well
                .check_for_collisions(&next_shape, &self.current_piece.top_left);

            if !collision_found {
                self.current_piece.change_shape();
            } else {
                // wall kick attempt!
                // need to do 2 checks:
                // move one piece to the right & perform all above checks
                let mut potential_position = self.current_piece.top_left; // creates a copy of 'Position' struct
                potential_position.x += 1;
                let collision_found = self.well
                    .check_for_collisions(&next_shape, &potential_position);

                if !collision_found {
                    self.current_piece.top_left = potential_position;
                    self.current_piece.potential_top_left = potential_position;
                    self.current_piece.change_shape();
                } else {
                    let mut potential_position = self.current_piece.top_left;
                    potential_position.x -= 1;
                    let collision_found = self.well
                        .check_for_collisions(&next_shape, &potential_position);

                    if !collision_found {
                        self.current_piece.top_left = potential_position;
                        self.current_piece.potential_top_left = potential_position;
                        self.current_piece.change_shape();
                    }
                }
            }

            Ok(())
        } else if self.input.hard_drop.is_active {
            self.current_piece.top_left = self.current_piece.get_shadow_position();
            self.well.land(&self.current_piece);
            self.current_piece = self.bag.take_piece();

            Ok(())
        } else {
            Ok(())
        }
    }
}

impl event::EventHandler for PlayState {
    fn update(&mut self, _: &mut Context, dt: Duration) -> GameResult<()> {
        println!("[state.update] Start of update method");
        println!("[state.update] current score: {}", self.score);
        if self.game_over {
            // do game over stuff
            return Ok(());
        }

        // TODO: handle/respond user input
        self.handle_user_input(dt)?;

        // handle shadow piece
        // TODO: put behind option
        self.handle_shadow_piece()?;

        self.handle_collisions()?;

        // handle gravity - return from update if our current piece landed
        if let Ok(false) = self.handle_gravity(dt) {
            return Ok(());
        }

        self.handle_line_clears()?;

        Ok(())
    }

    fn draw(&mut self, ctx: &mut Context) -> GameResult<()> {
        graphics::set_background_color(ctx, graphics::Color::new(0.0, 0.0, 0.0, 255.0));
        graphics::clear(ctx);

        self.well.draw(ctx, &self.image)?;
        self.current_piece
            .draw_shadow(ctx, &self.image, &self.current_piece.get_shadow_position())?;
        self.current_piece.draw(ctx, &self.image)?;

        graphics::present(ctx);

        Ok(())
    }

    fn key_down_event(&mut self, keycode: Keycode, _keymod: Mod, repeat: bool) {
        println!("[state.key_down_event] Key down event occured");
        println!("Key: {}, Repeat?: {}", keycode, repeat);
        match keycode {
            Keycode::Left => {
                self.input.left.is_active = true;
                self.current_command = Command::Left;
            }
            Keycode::Right => self.input.right.is_active = true,
            Keycode::Up => self.input.hard_drop.is_active = true,
            Keycode::Down => self.input.soft_drop.is_active = true,
            Keycode::Z => self.input.rotate_counterclockwise.is_active = true,
            Keycode::X => self.input.rotate_clockwise.is_active = true,
            _ => (),
        }

    }

    fn key_up_event(&mut self, keycode: Keycode, _keymod: Mod, _repeat: bool) {
        match keycode {
            Keycode::Left => {
                self.input.left.is_active = false;
                self.input.left.delay_timer = INPUT_DELAY_TIME;
            }
            Keycode::Right => self.input.right.is_active = false,
            Keycode::Up => self.input.hard_drop.is_active = false,
            Keycode::Down => self.input.soft_drop.is_active = false,
            Keycode::Z => self.input.rotate_counterclockwise.is_active = false,
            Keycode::X => self.input.rotate_clockwise.is_active = false,
            _ => (),
        }
    }
}
<commit_msg>playing around with input some more<commit_after>use std::time::Duration;
use ggez::{Context, GameResult, graphics, event};

mod well;
mod tetromino;
mod shapes;
mod bag;
mod util;

use ggez::event::*;
use self::well::Well;
use self::tetromino::Piece;
use self::bag::PieceBag;
use self::util::DurationExt;

const BLOCK_SIZE: f32 = 30.0;
const FALL_SPEED: f64 = 0.5;
const INPUT_DELAY_TIME: f64 = 0.05;

#[derive(Copy, Clone, Debug)]
pub struct Position {
    x: i32,
    y: i32,
}

#[derive(Debug, Copy, Clone)]
pub struct InputStateField {
    is_active: bool,
    delay_timer: f64,
}

impl Default for InputStateField {
    fn default() -> Self {
        InputStateField {
            is_active: false,
            delay_timer: INPUT_DELAY_TIME,
        }
    }
}

#[derive(Debug, Copy, Clone)]
pub struct InputState {
    left: InputStateField,
    right: InputStateField,
    soft_drop: InputStateField,
    hard_drop: InputStateField,
    rotate_clockwise: InputStateField,
    rotate_counterclockwise: InputStateField,
    drop: InputStateField,
}

impl Default for InputState {
    fn default() -> Self {

        InputState {
            left: InputStateField::default(),
            right: InputStateField::default(),
            soft_drop: InputStateField::default(),
            hard_drop: InputStateField::default(),
            rotate_clockwise: InputStateField::default(),
            rotate_counterclockwise: InputStateField::default(),
            drop: InputStateField::default(),
        }
    }
}

pub struct PlayState {
    input: InputState,
    prev_input: InputState,

    well: Well,
    bag: PieceBag,
    current_piece: Piece,

    fall_timer: f64,
    score: u32,
    cleared_lines: u16,
    level: u32,
    game_over: bool,

    // temp
    image: graphics::Image,
}

impl PlayState {
    pub fn new(ctx: &mut Context) -> GameResult<PlayState> {
        let image = graphics::Image::new(ctx, "/block.png")?;
        let mut bag = PieceBag::new();
        let first_piece = bag.take_piece();

        let state = PlayState {
            input: InputState::default(),
            prev_input: InputState::default(),

            well: Well::new(),
            bag: bag,
            current_piece: first_piece,

            fall_timer: 0.0,
            score: 0,
            cleared_lines: 0,
            level: 0,
            game_over: false,

            image: image,
        };

        Ok(state)
    }

    fn handle_user_input(&mut self, dt: Duration) -> GameResult<()> {
        if self.input.left.is_active {
            self.input.left.delay_timer += dt.as_subsec_millis();
            if self.input.left.delay_timer >= INPUT_DELAY_TIME {
                self.current_piece.potential_top_left.x -= 1;
                self.input.left.delay_timer = 0.0;
            }
        } else if self.input.right.is_active {
            self.input.right.delay_timer += dt.as_subsec_millis();
            if self.input.right.delay_timer >= INPUT_DELAY_TIME {
                self.current_piece.potential_top_left.x += 1;
                self.input.right.delay_timer = 0.0;
            }
        } else if self.input.soft_drop.is_active {
            self.input.soft_drop.delay_timer += dt.as_subsec_millis();
            if self.input.soft_drop.delay_timer >= INPUT_DELAY_TIME {
                self.current_piece.potential_top_left.y += 1;
                self.input.soft_drop.delay_timer = 0.0;
            }
        }

        Ok(())
    }

    /// Advance the fall time. If enough time has passed, allow gravity to
    /// affect the current piece.
    /// Returns a `GameResult<false>` if a landing occured.
    /// Returns a `GameResult<true>` if no landing occured and the piece can advance.
    fn handle_gravity(&mut self, dt: Duration) -> GameResult<bool> {
        self.fall_timer += dt.as_subsec_millis();

        if self.fall_timer >= FALL_SPEED {
            let current_shape = self.current_piece.get_shape();
            self.fall_timer = 0.0;
            self.current_piece.potential_top_left.y += 1;

            let did_land = self.well
                .check_for_landing(&current_shape, &self.current_piece.potential_top_left);

            if did_land {
                if self.current_piece.top_left.y < 2 {
                    println!("game over!");
                    self.game_over = true;
                    return Ok(false);
                }

                // game isn't over - take another piece and move to next frame
                self.well.land(&self.current_piece);
                self.current_piece = self.bag.take_piece();
                return Ok(false);
            }

            // piece did not land - advance!
            self.current_piece.top_left = self.current_piece.potential_top_left;
        }

        Ok(true)
    }

    fn handle_shadow_piece(&mut self) -> GameResult<()> {
        let mut shadow_position = self.current_piece.top_left;
        let mut potential_shadow_position = shadow_position;
        loop {
            potential_shadow_position.y += 1;
            let collision_found = self.well
                .check_for_landing(&self.current_piece.get_shape(), &potential_shadow_position);

            if collision_found {
                break;
            }

            shadow_position = potential_shadow_position;
            self.current_piece.set_shadow_position(shadow_position);
        }

        Ok(())
    }

    fn handle_line_clears(&mut self) -> GameResult<()> {
        // check for line clears
        let mut lines_cleared = 0;
        for r in (0..self.well.data.len()).rev() {
            let mut is_row_filled = true;
            for (c, _) in self.well.data[r].iter().enumerate() {
                if self.well.data[r][c] == 0 {
                    is_row_filled = false;
                    break; // no need to continue iterating, line is not clear...
                }
            }

            if is_row_filled {
                // TODO: implement more line clearing algorithms
                // TODO: make the current line clearing algorithm user selectable
                self.well.naive_line_clear(r);
                lines_cleared += 1;
            }
        }

        // add to score
        let level = self.level;
        match lines_cleared {
            1 => self.increase_score(40, level),
            2 => self.increase_score(100, level),
            3 => self.increase_score(300, level),
            4 => self.increase_score(1200, level),
            _ => (),
        }

        self.cleared_lines += lines_cleared;

        Ok(())
    }

    fn increase_score(&mut self, base_score: u32, level: u32) {
        self.score += base_score * (level + 1);
    }

    fn handle_collisions(&mut self) -> GameResult<()> {
        if self.input.left.is_active || self.input.right.is_active ||
           self.input.soft_drop.is_active {
            let current_shape = self.current_piece.get_shape();
            let collision_found = self.well
                .check_for_collisions(&current_shape, &self.current_piece.potential_top_left);

            if collision_found {
                self.current_piece.potential_top_left = self.current_piece.top_left;
            }

            self.current_piece.top_left = self.current_piece.potential_top_left; // advance tetromino
            Ok(())
        } else if self.input.rotate_clockwise.is_active ||
                  self.input.rotate_counterclockwise.is_active {
            let next_shape = self.current_piece.get_next_shape();
            let collision_found = self.well
                .check_for_collisions(&next_shape, &self.current_piece.top_left);

            if !collision_found {
                self.current_piece.change_shape();
            } else {
                // wall kick attempt!
                // need to do 2 checks:
                // move one piece to the right & perform all above checks
                let mut potential_position = self.current_piece.top_left; // creates a copy of 'Position' struct
                potential_position.x += 1;
                let collision_found = self.well
                    .check_for_collisions(&next_shape, &potential_position);

                if !collision_found {
                    self.current_piece.top_left = potential_position;
                    self.current_piece.potential_top_left = potential_position;
                    self.current_piece.change_shape();
                } else {
                    let mut potential_position = self.current_piece.top_left;
                    potential_position.x -= 1;
                    let collision_found = self.well
                        .check_for_collisions(&next_shape, &potential_position);

                    if !collision_found {
                        self.current_piece.top_left = potential_position;
                        self.current_piece.potential_top_left = potential_position;
                        self.current_piece.change_shape();
                    }
                }
            }

            Ok(())
        } else if self.input.hard_drop.is_active {
            self.current_piece.top_left = self.current_piece.get_shadow_position();
            self.well.land(&self.current_piece);
            self.current_piece = self.bag.take_piece();

            Ok(())
        } else {
            Ok(())
        }
    }
}

impl event::EventHandler for PlayState {
    fn update(&mut self, _: &mut Context, dt: Duration) -> GameResult<()> {
        println!("[state.update] Start of update method");
        println!("[state.update] current score: {}", self.score);
        if self.game_over {
            // do game over stuff
            return Ok(());
        }

        // TODO: handle/respond user input
        self.handle_user_input(dt)?;

        // handle shadow piece
        // TODO: put behind option
        self.handle_shadow_piece()?;

        self.handle_collisions()?;

        // handle gravity - return from update if our current piece landed
        if let Ok(false) = self.handle_gravity(dt) {
            return Ok(());
        }

        self.handle_line_clears()?;

        self.prev_input = self.input;

        Ok(())
    }

    fn draw(&mut self, ctx: &mut Context) -> GameResult<()> {
        graphics::set_background_color(ctx, graphics::Color::new(0.0, 0.0, 0.0, 255.0));
        graphics::clear(ctx);

        self.well.draw(ctx, &self.image)?;
        self.current_piece
            .draw_shadow(ctx, &self.image, &self.current_piece.get_shadow_position())?;
        self.current_piece.draw(ctx, &self.image)?;

        graphics::present(ctx);

        Ok(())
    }

    fn key_down_event(&mut self, keycode: Keycode, _keymod: Mod, _repeat: bool) {
        match keycode {
            Keycode::Left => self.input.left.is_active = true,
            Keycode::Right => self.input.right.is_active = true,
            Keycode::Up => self.input.hard_drop.is_active = true,
            Keycode::Down => self.input.soft_drop.is_active = true,
            Keycode::Z => self.input.rotate_counterclockwise.is_active = true,
            Keycode::X => self.input.rotate_clockwise.is_active = true,
            _ => (),
        }

    }

    fn key_up_event(&mut self, keycode: Keycode, _keymod: Mod, _repeat: bool) {
        match keycode {
            Keycode::Left => {
                self.input.left.is_active = false;
                self.input.left.delay_timer = INPUT_DELAY_TIME;
            }
            Keycode::Right => {
                self.input.right.is_active = false;
                self.input.right.delay_timer = INPUT_DELAY_TIME;
            },
            Keycode::Up => {
                self.input.hard_drop.is_active = false;
                self.input.hard_drop.delay_timer = INPUT_DELAY_TIME;
            },
            Keycode::Down => {
                self.input.soft_drop.is_active = false;
                self.input.soft_drop.delay_timer = INPUT_DELAY_TIME;
            },
            Keycode::Z => {
                self.input.rotate_counterclockwise.is_active = false;
                self.input.rotate_counterclockwise.delay_timer = INPUT_DELAY_TIME;
            },
            Keycode::X => {
                self.input.rotate_clockwise.is_active = false;
                self.input.rotate_clockwise.delay_timer = INPUT_DELAY_TIME;
            },
            _ => (),
        }
    }
}
<|endoftext|>


<commit_before>//! Filter primitive subregion computation.
use cairo::{self, MatrixTrait};

use bbox::BoundingBox;
use length::RsvgLength;

use super::context::{FilterContext, FilterInput, FilterOutput, IRect};

/// A helper type for filter primitive subregion computation.
#[derive(Clone, Copy)]
pub struct BoundsBuilder<'a> {
    /// The filter context.
    ctx: &'a FilterContext,

    /// The current bounding box.
    bbox: BoundingBox,

    /// Whether one of the input nodes is standard input.
    standard_input_was_referenced: bool,

    /// Filter primitive properties.
    x: Option<RsvgLength>,
    y: Option<RsvgLength>,
    width: Option<RsvgLength>,
    height: Option<RsvgLength>,
}

impl<'a> BoundsBuilder<'a> {
    /// Constructs a new `BoundsBuilder`.
    #[inline]
    pub fn new(
        ctx: &'a FilterContext,
        x: Option<RsvgLength>,
        y: Option<RsvgLength>,
        width: Option<RsvgLength>,
        height: Option<RsvgLength>,
    ) -> Self {
        Self {
            ctx,
            // The matrix is paffine because we're using that fact in apply_properties().
            bbox: BoundingBox::new(&ctx.paffine()),
            standard_input_was_referenced: false,
            x,
            y,
            width,
            height,
        }
    }

    /// Adds a filter primitive input to the bounding box.
    #[inline]
    pub fn add_input(mut self, input: &FilterInput) -> Self {
        // If a standard input was referenced, the default value is the filter effects region
        // regardless of other referenced inputs. This means we can skip computing the bounding
        // box.
        if self.standard_input_was_referenced {
            return self;
        }

        match *input {
            FilterInput::StandardInput(_) => {
                self.standard_input_was_referenced = true;
            }
            FilterInput::PrimitiveOutput(FilterOutput {
                bounds: IRect { x0, y0, x1, y1 },
                ..
            }) => {
                let rect = cairo::Rectangle {
                    x: f64::from(x0),
                    y: f64::from(y0),
                    width: f64::from(x1 - x0),
                    height: f64::from(y1 - y0),
                };

                let input_bbox = BoundingBox::new(&cairo::Matrix::identity()).with_rect(Some(rect));
                self.bbox.insert(&input_bbox);
            }
        }

        self
    }

    /// Returns the final pixel bounds.
    #[inline]
    pub fn into_irect(self) -> IRect {
        let (mut bbox, needs_clipping) = self.apply_properties();

        if needs_clipping {
            let effects_region = self.ctx.effects_region();
            bbox.clip(&effects_region);
        }

        bbox.rect.unwrap().into()
    }

    /// Returns the final pixel bounds without clipping to the filter effects region.
    ///
    /// Used by feImage.
    #[inline]
    pub fn into_irect_without_clipping(self) -> IRect {
        self.apply_properties().0.rect.unwrap().into()
    }

    /// Applies the filter primitive properties.
    fn apply_properties(mut self) -> (BoundingBox, bool) {
        if self.bbox.rect.is_none() || self.standard_input_was_referenced {
            // The default value is the filter effects region.
            let effects_region = self.ctx.effects_region();

            // println!("effects_region: {:#?}", effects_region);

            // Clear out the rect.
            self.bbox = self.bbox.with_rect(None);
            // Convert into the paffine coordinate system.
            self.bbox.insert(&effects_region);
        }

        let mut needs_clipping = false;

        // If any of the properties were specified, we need to respect them.
        if self.x.is_some() || self.y.is_some() || self.width.is_some() || self.height.is_some() {
            self.ctx.with_primitive_units(|normalize| {
                // These replacements are correct only because self.bbox is used with the paffine
                // matrix.
                let rect = self.bbox.rect.as_mut().unwrap();

                if let Some(x) = self.x {
                    rect.x = normalize(&x);
                }
                if let Some(y) = self.y {
                    rect.y = normalize(&y);
                }
                if let Some(width) = self.width {
                    rect.width = normalize(&width);
                }
                if let Some(height) = self.height {
                    rect.height = normalize(&height);
                }
            });

            // x, y, width, height, on the other hand, can exceed the filter effects region, so a
            // clip is needed.
            needs_clipping = true;
        }

        // Convert into the surface coordinate system.
        let mut bbox = BoundingBox::new(&cairo::Matrix::identity());
        bbox.insert(&self.bbox);
        (bbox, needs_clipping)
    }
}
<commit_msg>Remove leftover debug comment<commit_after>//! Filter primitive subregion computation.
use cairo::{self, MatrixTrait};

use bbox::BoundingBox;
use length::RsvgLength;

use super::context::{FilterContext, FilterInput, FilterOutput, IRect};

/// A helper type for filter primitive subregion computation.
#[derive(Clone, Copy)]
pub struct BoundsBuilder<'a> {
    /// The filter context.
    ctx: &'a FilterContext,

    /// The current bounding box.
    bbox: BoundingBox,

    /// Whether one of the input nodes is standard input.
    standard_input_was_referenced: bool,

    /// Filter primitive properties.
    x: Option<RsvgLength>,
    y: Option<RsvgLength>,
    width: Option<RsvgLength>,
    height: Option<RsvgLength>,
}

impl<'a> BoundsBuilder<'a> {
    /// Constructs a new `BoundsBuilder`.
    #[inline]
    pub fn new(
        ctx: &'a FilterContext,
        x: Option<RsvgLength>,
        y: Option<RsvgLength>,
        width: Option<RsvgLength>,
        height: Option<RsvgLength>,
    ) -> Self {
        Self {
            ctx,
            // The matrix is paffine because we're using that fact in apply_properties().
            bbox: BoundingBox::new(&ctx.paffine()),
            standard_input_was_referenced: false,
            x,
            y,
            width,
            height,
        }
    }

    /// Adds a filter primitive input to the bounding box.
    #[inline]
    pub fn add_input(mut self, input: &FilterInput) -> Self {
        // If a standard input was referenced, the default value is the filter effects region
        // regardless of other referenced inputs. This means we can skip computing the bounding
        // box.
        if self.standard_input_was_referenced {
            return self;
        }

        match *input {
            FilterInput::StandardInput(_) => {
                self.standard_input_was_referenced = true;
            }
            FilterInput::PrimitiveOutput(FilterOutput {
                bounds: IRect { x0, y0, x1, y1 },
                ..
            }) => {
                let rect = cairo::Rectangle {
                    x: f64::from(x0),
                    y: f64::from(y0),
                    width: f64::from(x1 - x0),
                    height: f64::from(y1 - y0),
                };

                let input_bbox = BoundingBox::new(&cairo::Matrix::identity()).with_rect(Some(rect));
                self.bbox.insert(&input_bbox);
            }
        }

        self
    }

    /// Returns the final pixel bounds.
    #[inline]
    pub fn into_irect(self) -> IRect {
        let (mut bbox, needs_clipping) = self.apply_properties();

        if needs_clipping {
            let effects_region = self.ctx.effects_region();
            bbox.clip(&effects_region);
        }

        bbox.rect.unwrap().into()
    }

    /// Returns the final pixel bounds without clipping to the filter effects region.
    ///
    /// Used by feImage.
    #[inline]
    pub fn into_irect_without_clipping(self) -> IRect {
        self.apply_properties().0.rect.unwrap().into()
    }

    /// Applies the filter primitive properties.
    fn apply_properties(mut self) -> (BoundingBox, bool) {
        if self.bbox.rect.is_none() || self.standard_input_was_referenced {
            // The default value is the filter effects region.
            let effects_region = self.ctx.effects_region();

            // Clear out the rect.
            self.bbox = self.bbox.with_rect(None);
            // Convert into the paffine coordinate system.
            self.bbox.insert(&effects_region);
        }

        let mut needs_clipping = false;

        // If any of the properties were specified, we need to respect them.
        if self.x.is_some() || self.y.is_some() || self.width.is_some() || self.height.is_some() {
            self.ctx.with_primitive_units(|normalize| {
                // These replacements are correct only because self.bbox is used with the paffine
                // matrix.
                let rect = self.bbox.rect.as_mut().unwrap();

                if let Some(x) = self.x {
                    rect.x = normalize(&x);
                }
                if let Some(y) = self.y {
                    rect.y = normalize(&y);
                }
                if let Some(width) = self.width {
                    rect.width = normalize(&width);
                }
                if let Some(height) = self.height {
                    rect.height = normalize(&height);
                }
            });

            // x, y, width, height, on the other hand, can exceed the filter effects region, so a
            // clip is needed.
            needs_clipping = true;
        }

        // Convert into the surface coordinate system.
        let mut bbox = BoundingBox::new(&cairo::Matrix::identity());
        bbox.insert(&self.bbox);
        (bbox, needs_clipping)
    }
}
<|endoftext|>


<commit_before>use std::io;

/// Circular buffer to store a stream. Read/write boundaries are not preserved.
pub struct StreamBuffer {
    buf: Box<[u8]>,
    head: usize,
    tail: usize,
}

impl StreamBuffer {
    pub fn new(capacity: usize) -> Self {
        Self {
            buf: vec![0; capacity + 1].into_boxed_slice(),
            head: 0,
            tail: 0,
        }
    }

    pub fn size(&self) -> usize {
        if self.head < self.tail {
            self.head + self.buf.len() - self.tail
        } else {
            self.head - self.tail
        }
    }

    pub fn remaining(&self) -> usize {
        self.buf.len() - self.size()
    }

    pub fn write_to<W: io::Write>(&mut self, destination: &mut W) -> io::Result<usize> {
        if self.head > self.tail {
            let source_slice = &self.buf[self.tail..self.head];
            let w = destination.write(source_slice)?;
            self.tail += w;
            Ok(w)
        } else if self.head < self.tail {
            let source_slice = &self.buf[self.tail..];
            let w = destination.write(source_slice)?;
            self.tail = (self.tail + w) % self.buf.len();
            Ok(w)
        } else {
            // else head == tail, which means empty buffer, nothing to do
            Ok(0)
        }
    }

    pub fn read_from(&mut self, source: &[u8]) {
        assert!(self.remaining() >= source.len(), "StreamBuffer must have enough space, check remaining() space before calling read_from()");
        let source_len = source.len();
        let buf_len = self.buf.len();
        if source_len <= buf_len - self.head {
            let target_slice = &mut self.buf[self.head..self.head + source_len];
            target_slice.copy_from_slice(source);
        } else {
            {
                // fill until the right-end of the buffer
                let target_slice = &mut self.buf[self.head..];
                let source_slice = &source[..source_len - self.head];
                target_slice.copy_from_slice(source_slice);
            }
            // fill the remaining from the left-end of the buffer
            let target_slice = &mut self.buf[..self.head + source_len - buf_len];
            let source_slice = &source[source_len - self.head..];
            target_slice.copy_from_slice(source_slice);
        }
        self.head = (self.head + source_len) % buf_len;
    }
}
<commit_msg>Implement StreamBuffer.optimize()<commit_after>use std::io;

/// Circular buffer to store a stream. Read/write boundaries are not preserved.
pub struct StreamBuffer {
    buf: Box<[u8]>,
    head: usize,
    tail: usize,
}

impl StreamBuffer {
    pub fn new(capacity: usize) -> Self {
        Self {
            buf: vec![0; capacity + 1].into_boxed_slice(),
            head: 0,
            tail: 0,
        }
    }

    pub fn is_empty(&self) -> bool{
        self.head == self.tail
    }

    pub fn size(&self) -> usize {
        if self.head < self.tail {
            self.head + self.buf.len() - self.tail
        } else {
            self.head - self.tail
        }
    }

    pub fn remaining(&self) -> usize {
        self.buf.len() - self.size()
    }

    pub fn write_to<W: io::Write>(&mut self, destination: &mut W) -> io::Result<usize> {
        if self.head == self.tail {
            // buffer is empty, nothing to do
            Ok(0)
        } else {
            let w;
            if self.head > self.tail {
                let source_slice = &self.buf[self.tail..self.head];
                w = destination.write(source_slice)?;
                self.tail += w;
            } else { // self.head < self.tail
                let source_slice = &self.buf[self.tail..];
                w = destination.write(source_slice)?;
                self.tail = (self.tail + w) % self.buf.len();
            }
            self.optimize();
            Ok(w)
        }
    }

    pub fn read_from(&mut self, source: &[u8]) {
        assert!(self.remaining() >= source.len(), "StreamBuffer must have enough space, check remaining() space before calling read_from()");
        let source_len = source.len();
        let buf_len = self.buf.len();
        if source_len <= buf_len - self.head {
            let target_slice = &mut self.buf[self.head..self.head + source_len];
            target_slice.copy_from_slice(source);
        } else {
            {
                // fill until the right-end of the buffer
                let target_slice = &mut self.buf[self.head..];
                let source_slice = &source[..source_len - self.head];
                target_slice.copy_from_slice(source_slice);
            }
            // fill the remaining from the left-end of the buffer
            let target_slice = &mut self.buf[..self.head + source_len - buf_len];
            let source_slice = &source[source_len - self.head..];
            target_slice.copy_from_slice(source_slice);
        }
        self.head = (self.head + source_len) % buf_len;
    }

    /// To avoid unnecessary copies, StreamBuffer writes at most until the "end" of the circular
    /// buffer, which is suboptimal (it could have written more data if they have been contiguous).
    ///
    /// In order to minimize the occurrence of this event, reset the head and tail to 0 when the
    /// buffer is empty (no copy is involved).
    ///
    /// This is especially useful when the StreamBuffer is used to read/write one packet at a time,
    /// so the "end" of the buffer is guaranteed to never be reached.
    fn optimize(&mut self) {
        if self.is_empty() {
            self.head = 0;
            self.tail = 0;
        }
    }
}
<|endoftext|>


<commit_before>#![feature(box_syntax)]
// This function takes ownership of the box
fn eat_box(boxed_int: Box<isize>) {
    println!("destroying box that contains {}", boxed_int);
}

// This function borrows the box
fn peep_inside_box(borrowed_box: &Box<isize>) {
    println!("This box contains {}", borrowed_box);
}

fn main() {
    // A boxed integer
    let boxed_int = box 5;

    // Borrow the box, ownership is not taken
    peep_inside_box(&boxed_int);

    // The box can be borrowed again
    peep_inside_box(&boxed_int);

    {
        // Take a reference to the data contained inside the box
        let _ref_to_int: &isize = &*boxed_int;

        // Error! Can't destroy boxed_int, while the inner value has been
        // borrowed
        eat_box(boxed_int);
        // FIXME ^ Comment out this line

        // `_ref_to_int` goes out of scope
    }

    // Give up ownership of the box
    eat_box(boxed_int);
}
<commit_msg>Updating borrow/borrow: removing box_syntax feature, changing isize to i32<commit_after>// This function takes ownership of the box
fn eat_box(boxed_int: Box<i32>) {
    println!("destroying box that contains {}", boxed_int);
}

// This function borrows the box
fn peep_inside_box(borrowed_box: &Box<i32>) {
    println!("This box contains {}", borrowed_box);
}

fn main() {
    // A boxed integer
    let boxed_int = Box::new(5);

    // Borrow the box, ownership is not taken
    peep_inside_box(&boxed_int);

    // The box can be borrowed again
    peep_inside_box(&boxed_int);

    {
        // Take a reference to the data contained inside the box
        let _ref_to_int: &i32 = &*boxed_int;

        // Error! Can't destroy boxed_int, while the inner value has been
        // borrowed
        eat_box(boxed_int);
        // FIXME ^ Comment out this line

        // `_ref_to_int` goes out of scope
    }

    // Give up ownership of the box
    eat_box(boxed_int);
}
<|endoftext|>