<commit_before>use std::result::Result;
use std::collections::HashMap;
use std::cmp;

use gossyp_base::RetrieveToolError;
use gossyp_base::Environment;
use gossyp_base::Tool;

///
/// Errors that can occur when binding a variable
///
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
pub enum BindingError {
    /// The requested variable name is already in use
    AlreadyInUse,
}

///
/// Result of binding to an environment
///
pub enum BindingResult {
    /// Name maps to a variable
    Variable(u32),

    /// Name maps to a tool in the external environment
    Tool(Box<Tool>),

    /// Got an error while mapping a tool (binding isn't a tool or a variable)
    Error(RetrieveToolError)
}

///
/// A binding environment can be used to allocate variable values that will be used
/// during execution of a script.
///
pub struct VariableBindingEnvironment {
    /// The next variable value that will be alllocated
    next_to_allocate: u32,

    /// The current set of binding allocations
    bindings: HashMap<String, u32>
}

///
/// An external environment binding environment can be used to look up tools found
/// in an Environment object
///
struct ExternalEnvironmentBindingEnvironment<'a> {
    /// Where to look up variables
    variable_environment: VariableBindingEnvironment,

    /// The environment to look things up in
    environment: &'a Environment
}

///
/// A child environment for a base environment
///
struct ChildBindingEnvironment<'a> {
    /// The base binding environment that this child environment is for
    base_environment: &'a mut BindingEnvironment,

    /// The current set of binding allocations
    bindings: HashMap<String, u32>
}

///
/// Trait implemented by objects that represent a binding environment
///
pub trait BindingEnvironment {
    ///
    /// Allocates a variable location without assigning a name
    ///
    fn allocate_location(&mut self) -> u32;

    ///
    /// Allocates a variable location with an assigned name (which must not
    /// already be in use)
    ///
    fn allocate_variable(&mut self, name: &str) -> Result<u32, BindingError>;

    ///
    /// Looks up a variable location by name
    ///
    fn lookup(&self, name: &str) -> BindingResult;

    ///
    /// Returns the total number of variables allocated for this environment
    ///
    fn get_number_of_variables(&self) -> u32;

    ///
    /// Creates a sub environment
    ///
    /// This allows names to be re-used with new variables, but existing variables
    /// will continue to refer to their current locations
    ///
    fn create_sub_environment<'a>(&'a mut self) -> Box<BindingEnvironment + 'a>;
}

impl BindingEnvironment {
    ///
    /// Creates a new binding environment. New variables will be mapped from 0
    ///
    pub fn new() -> Box<VariableBindingEnvironment> {
        Box::new(VariableBindingEnvironment {
            next_to_allocate:   0,
            bindings:           HashMap::new()
        })
    }

    ///
    /// Creates a new binding environment which will fetch tools from an outside environment
    ///
    pub fn from_environment<'a>(environment: &'a Environment) -> Box<BindingEnvironment+'a> {
        let variable_environment = VariableBindingEnvironment {
            next_to_allocate:   0,
            bindings:           HashMap::new()
        };

        let external_environment = ExternalEnvironmentBindingEnvironment {
            variable_environment:   variable_environment,
            environment:            environment
        };

        Box::new(external_environment)
    }

    ///
    /// Combines two binding environments into a single environment
    ///
    /// Items not found in the primary environment will be returned in the secondary
    /// one. New variables will be allocated in the secondary environment.
    ///
    pub fn combine<'a>(primary_environment: &'a mut BindingEnvironment, secondary_environment: &'a BindingEnvironment) -> Box<BindingEnvironment+'a> {
        Box::new((primary_environment, secondary_environment))
    }
}

impl BindingEnvironment for VariableBindingEnvironment {
    ///
    /// Creates a new sub-environment, where new variable names can be a
    ///
    fn create_sub_environment<'b>(&'b mut self) -> Box<BindingEnvironment + 'b> {
        Box::new(ChildBindingEnvironment {
            base_environment:   self,
            bindings:           HashMap::new()
        })
    }

    ///
    /// Allocates a new variable location in this binding (without assigning a name to it)
    ///
    fn allocate_location(&mut self) -> u32 {
        // If there's no parent, just allocate directly
        let allocation          = self.next_to_allocate;
        self.next_to_allocate   = allocation + 1;

        allocation
    }

    ///
    /// Allocates a new variable
    ///
    fn allocate_variable(&mut self, name: &str) -> Result<u32, BindingError> {
        if self.bindings.contains_key(name) {
            // Variable name is already taken
            Err(BindingError::AlreadyInUse)
        } else {
            // Can assign this location to value
            let allocation = self.allocate_location();
            self.bindings.insert(String::from(name), allocation);

            Ok(allocation)
        }
    }

    ///
    /// Looks up a name in this binding environment
    ///
    fn lookup(&self, name: &str) -> BindingResult {
        if let Some(variable) = self.bindings.get(name) {
            // Try to retrieve as a variable directly from this environment
            BindingResult::Variable(*variable)
        } else {
            BindingResult::Error(RetrieveToolError::not_found())
        }
    }

    ///
    /// Returns the number of variables used in this environment
    ///
    fn get_number_of_variables(&self) -> u32 {
        self.next_to_allocate
    }
}

impl<'a> BindingEnvironment for ExternalEnvironmentBindingEnvironment<'a> {
    fn allocate_location(&mut self) -> u32 {
        self.variable_environment.allocate_location()
    }

    fn allocate_variable(&mut self, name: &str) -> Result<u32, BindingError> {
        self.variable_environment.allocate_variable(name)
    }

    fn lookup(&self, name: &str) -> BindingResult {
        let variable_result = self.variable_environment.lookup(name);

        match variable_result {
            BindingResult::Error(_) => {
                let tool = self.environment.get_json_tool(name);

                tool.map(|tool| BindingResult::Tool(tool))
                    .unwrap_or_else(|err| BindingResult::Error(err))
            },

            found => found,
        }
    }

    fn get_number_of_variables(&self) -> u32 {
        self.variable_environment.get_number_of_variables()
    }

    fn create_sub_environment<'b>(&'b mut self) -> Box<BindingEnvironment + 'b> {
        Box::new(ChildBindingEnvironment {
            base_environment:   self,
            bindings:           HashMap::new()
        })
    }
}

impl<'a> BindingEnvironment for ChildBindingEnvironment<'a> {
    fn allocate_location(&mut self) -> u32 {
        self.base_environment.allocate_location()
    }

    fn allocate_variable(&mut self, name: &str) -> Result<u32, BindingError> {
        if self.bindings.contains_key(name) {
            // Variable name is already taken
            Err(BindingError::AlreadyInUse)
        } else {
            // Can assign this location to value
            let allocation = self.allocate_location();
            self.bindings.insert(String::from(name), allocation);

            Ok(allocation)
        }
    }

    fn lookup(&self, name: &str) -> BindingResult {
        if let Some(variable) = self.bindings.get(name) {
            BindingResult::Variable(*variable)
        } else {
            self.base_environment.lookup(name)
        }
    }

    fn get_number_of_variables(&self) -> u32 {
        self.base_environment.get_number_of_variables()
    }

    fn create_sub_environment<'b>(&'b mut self) -> Box<BindingEnvironment + 'b> {
        Box::new(ChildBindingEnvironment {
            base_environment:   self,
            bindings:           HashMap::new()
        })
    }
}

impl<'a> BindingEnvironment for (&'a mut BindingEnvironment, &'a BindingEnvironment) {
    fn allocate_location(&mut self) -> u32 {
        let (ref mut primary, _) = *self;

        primary.allocate_location()
    }

    fn allocate_variable(&mut self, name: &str) -> Result<u32, BindingError> {
        let (ref mut primary, _) = *self;

        primary.allocate_variable(name)
    }

    fn lookup(&self, name: &str) -> BindingResult {
        let (ref primary, ref secondary) = *self;

        match primary.lookup(name) {
            BindingResult::Error(_) => secondary.lookup(name),
            found                   => found
        }
    }

    fn get_number_of_variables(&self) -> u32 {
        let (ref primary, ref secondary) = *self;

        cmp::max(primary.get_number_of_variables(), secondary.get_number_of_variables())
    }

    fn create_sub_environment<'b>(&'b mut self) -> Box<BindingEnvironment + 'b> {
        Box::new(ChildBindingEnvironment {
            base_environment:   self,
            bindings:           HashMap::new()
        })
    }
}

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

    #[test]
    fn can_allocate_location() {
        let empty_environment   = EmptyEnvironment::new();
        let mut binding         = BindingEnvironment::from_environment(&empty_environment);

        assert!(binding.allocate_location() == 0);
    }

    #[test]
    fn can_allocate_many_locations() {
        let empty_environment   = EmptyEnvironment::new();
        let mut binding         = BindingEnvironment::from_environment(&empty_environment);

        assert!(binding.allocate_location() == 0);
        assert!(binding.allocate_location() == 1);
        assert!(binding.allocate_location() == 2);
    }

    #[test]
    fn can_get_number_of_variables() {
        let empty_environment   = EmptyEnvironment::new();
        let mut binding         = BindingEnvironment::from_environment(&empty_environment);

        binding.allocate_location();
        binding.allocate_location();
        binding.allocate_location();

        assert!(binding.get_number_of_variables() == 3);

        binding.allocate_location();

        assert!(binding.get_number_of_variables() == 4);
    }

    #[test]
    fn allocating_locations_in_child_environments_also_does_in_parent() {
        let empty_environment   = EmptyEnvironment::new();
        let mut binding         = BindingEnvironment::from_environment(&empty_environment);

        assert!(binding.allocate_location() == 0);

        {
            let mut child_environment = binding.create_sub_environment();

            assert!(child_environment.allocate_location() == 1);
        }

        assert!(binding.allocate_location() == 2);
    }

    #[test]
    fn can_reallocate_variable_name_in_child_environment() {
        let empty_environment   = EmptyEnvironment::new();
        let mut binding         = BindingEnvironment::from_environment(&empty_environment);

        binding.allocate_variable("test");
        assert!(match binding.lookup("test") { BindingResult::Variable(0) => true, _ => false });

        {
            let mut child_environment = binding.create_sub_environment();
            assert!(child_environment.allocate_variable("test") == Ok(1));
        }

        assert!(match binding.lookup("test") { BindingResult::Variable(0) => true, _ => false });
    }

    #[test]
    fn child_environment_lookup_falls_through_to_parent() {
        let empty_environment   = EmptyEnvironment::new();
        let mut binding         = BindingEnvironment::from_environment(&empty_environment);

        binding.allocate_variable("test1");
        binding.allocate_variable("test2");
        assert!(match binding.lookup("test1") { BindingResult::Variable(0) => true, _ => false });
        assert!(match binding.lookup("test2") { BindingResult::Variable(1) => true, _ => false });

        {
            let mut child_environment = binding.create_sub_environment();
            assert!(child_environment.allocate_variable("test1") == Ok(2));

            assert!(match child_environment.lookup("test1") { BindingResult::Variable(2) => true, _ => false });
            assert!(match child_environment.lookup("test2") { BindingResult::Variable(1) => true, _ => false });
        }

        assert!(match binding.lookup("test1") { BindingResult::Variable(0) => true, _ => false });
        assert!(match binding.lookup("test2") { BindingResult::Variable(1) => true, _ => false });
    }

    #[test]
    fn can_allocate_variable_name() {
        let empty_environment   = EmptyEnvironment::new();
        let mut binding         = BindingEnvironment::from_environment(&empty_environment);

        assert!(binding.allocate_variable("test") == Ok(0));
    }

    #[test]
    fn cannot_allocate_variable_name_twice() {
        let empty_environment   = EmptyEnvironment::new();
        let mut binding         = BindingEnvironment::from_environment(&empty_environment);

        assert!(binding.allocate_variable("test") == Ok(0));
        assert!(binding.allocate_variable("test") == Err(BindingError::AlreadyInUse));
    }

    #[test]
    fn can_lookup_variable_name() {
        let empty_environment   = EmptyEnvironment::new();
        let mut binding         = BindingEnvironment::from_environment(&empty_environment);

        binding.allocate_variable("test");

        assert!(match binding.lookup("test") { BindingResult::Variable(v) => v == 0, _ => false });
    }

    #[test]
    fn can_lookup_many_variable_names() {
        let empty_environment   = EmptyEnvironment::new();
        let mut binding         = BindingEnvironment::from_environment(&empty_environment);

        binding.allocate_variable("test1");
        binding.allocate_variable("test2");
        binding.allocate_variable("test3");

        assert!(match binding.lookup("test1") { BindingResult::Variable(v) => v == 0, _ => false });
        assert!(match binding.lookup("test2") { BindingResult::Variable(v) => v == 1, _ => false });
        assert!(match binding.lookup("test3") { BindingResult::Variable(v) => v == 2, _ => false });
    }

    #[test]
    fn can_lookup_tool_name() {
        let tool_environment = DynamicEnvironment::new();
        tool_environment.define("test", Box::new(make_pure_tool(|_: ()| "Success")));

        let mut binding = BindingEnvironment::from_environment(&tool_environment);

        assert!(match binding.lookup("test") { BindingResult::Tool(_) => true, _ => false });
    }

    #[test]
    fn variable_name_has_precedence_over_tool() {
        let tool_environment = DynamicEnvironment::new();
        tool_environment.define("test", Box::new(make_pure_tool(|_: ()| "Success")));

        let mut binding = BindingEnvironment::from_environment(&tool_environment);

        binding.allocate_variable("test");

        assert!(match binding.lookup("test") { BindingResult::Variable(v) => v == 0, _ => false });
    }
}
<commit_msg>Rename ExternalEnvironment to ToolEnvironment to better reflect its purpose<commit_after>use std::result::Result;
use std::collections::HashMap;
use std::cmp;

use gossyp_base::RetrieveToolError;
use gossyp_base::Environment;
use gossyp_base::Tool;

///
/// Errors that can occur when binding a variable
///
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
pub enum BindingError {
    /// The requested variable name is already in use
    AlreadyInUse,
}

///
/// Result of binding to an environment
///
pub enum BindingResult {
    /// Name maps to a variable
    Variable(u32),

    /// Name maps to a tool in the external environment
    Tool(Box<Tool>),

    /// Got an error while mapping a tool (binding isn't a tool or a variable)
    Error(RetrieveToolError)
}

///
/// A binding environment can be used to allocate variable values that will be used
/// during execution of a script.
///
pub struct VariableBindingEnvironment {
    /// The next variable value that will be alllocated
    next_to_allocate: u32,

    /// The current set of binding allocations
    bindings: HashMap<String, u32>
}

///
/// An external environment binding environment can be used to look up tools found
/// in an Environment object
///
struct ToolBindingEnvironment<'a> {
    /// Where to look up variables
    variable_environment: VariableBindingEnvironment,

    /// The environment to look things up in
    environment: &'a Environment
}

///
/// A child environment for a base environment
///
struct ChildBindingEnvironment<'a> {
    /// The base binding environment that this child environment is for
    base_environment: &'a mut BindingEnvironment,

    /// The current set of binding allocations
    bindings: HashMap<String, u32>
}

///
/// Trait implemented by objects that represent a binding environment
///
pub trait BindingEnvironment {
    ///
    /// Allocates a variable location without assigning a name
    ///
    fn allocate_location(&mut self) -> u32;

    ///
    /// Allocates a variable location with an assigned name (which must not
    /// already be in use)
    ///
    fn allocate_variable(&mut self, name: &str) -> Result<u32, BindingError>;

    ///
    /// Looks up a variable location by name
    ///
    fn lookup(&self, name: &str) -> BindingResult;

    ///
    /// Returns the total number of variables allocated for this environment
    ///
    fn get_number_of_variables(&self) -> u32;

    ///
    /// Creates a sub environment
    ///
    /// This allows names to be re-used with new variables, but existing variables
    /// will continue to refer to their current locations
    ///
    fn create_sub_environment<'a>(&'a mut self) -> Box<BindingEnvironment + 'a>;
}

impl BindingEnvironment {
    ///
    /// Creates a new binding environment. New variables will be mapped from 0
    ///
    pub fn new() -> Box<VariableBindingEnvironment> {
        Box::new(VariableBindingEnvironment {
            next_to_allocate:   0,
            bindings:           HashMap::new()
        })
    }

    ///
    /// Creates a new binding environment which will fetch tools from an outside environment
    ///
    pub fn from_environment<'a>(environment: &'a Environment) -> Box<BindingEnvironment+'a> {
        let variable_environment = VariableBindingEnvironment {
            next_to_allocate:   0,
            bindings:           HashMap::new()
        };

        let tool_environment = ToolBindingEnvironment {
            variable_environment:   variable_environment,
            environment:            environment
        };

        Box::new(tool_environment)
    }

    ///
    /// Combines two binding environments into a single environment
    ///
    /// Items not found in the primary environment will be returned in the secondary
    /// one. New variables will be allocated in the secondary environment.
    ///
    pub fn combine<'a>(primary_environment: &'a mut BindingEnvironment, secondary_environment: &'a BindingEnvironment) -> Box<BindingEnvironment+'a> {
        Box::new((primary_environment, secondary_environment))
    }
}

impl BindingEnvironment for VariableBindingEnvironment {
    ///
    /// Creates a new sub-environment, where new variable names can be a
    ///
    fn create_sub_environment<'b>(&'b mut self) -> Box<BindingEnvironment + 'b> {
        Box::new(ChildBindingEnvironment {
            base_environment:   self,
            bindings:           HashMap::new()
        })
    }

    ///
    /// Allocates a new variable location in this binding (without assigning a name to it)
    ///
    fn allocate_location(&mut self) -> u32 {
        // If there's no parent, just allocate directly
        let allocation          = self.next_to_allocate;
        self.next_to_allocate   = allocation + 1;

        allocation
    }

    ///
    /// Allocates a new variable
    ///
    fn allocate_variable(&mut self, name: &str) -> Result<u32, BindingError> {
        if self.bindings.contains_key(name) {
            // Variable name is already taken
            Err(BindingError::AlreadyInUse)
        } else {
            // Can assign this location to value
            let allocation = self.allocate_location();
            self.bindings.insert(String::from(name), allocation);

            Ok(allocation)
        }
    }

    ///
    /// Looks up a name in this binding environment
    ///
    fn lookup(&self, name: &str) -> BindingResult {
        if let Some(variable) = self.bindings.get(name) {
            // Try to retrieve as a variable directly from this environment
            BindingResult::Variable(*variable)
        } else {
            BindingResult::Error(RetrieveToolError::not_found())
        }
    }

    ///
    /// Returns the number of variables used in this environment
    ///
    fn get_number_of_variables(&self) -> u32 {
        self.next_to_allocate
    }
}

impl<'a> BindingEnvironment for ToolBindingEnvironment<'a> {
    fn allocate_location(&mut self) -> u32 {
        self.variable_environment.allocate_location()
    }

    fn allocate_variable(&mut self, name: &str) -> Result<u32, BindingError> {
        self.variable_environment.allocate_variable(name)
    }

    fn lookup(&self, name: &str) -> BindingResult {
        let variable_result = self.variable_environment.lookup(name);

        match variable_result {
            BindingResult::Error(_) => {
                let tool = self.environment.get_json_tool(name);

                tool.map(|tool| BindingResult::Tool(tool))
                    .unwrap_or_else(|err| BindingResult::Error(err))
            },

            found => found,
        }
    }

    fn get_number_of_variables(&self) -> u32 {
        self.variable_environment.get_number_of_variables()
    }

    fn create_sub_environment<'b>(&'b mut self) -> Box<BindingEnvironment + 'b> {
        Box::new(ChildBindingEnvironment {
            base_environment:   self,
            bindings:           HashMap::new()
        })
    }
}

impl<'a> BindingEnvironment for ChildBindingEnvironment<'a> {
    fn allocate_location(&mut self) -> u32 {
        self.base_environment.allocate_location()
    }

    fn allocate_variable(&mut self, name: &str) -> Result<u32, BindingError> {
        if self.bindings.contains_key(name) {
            // Variable name is already taken
            Err(BindingError::AlreadyInUse)
        } else {
            // Can assign this location to value
            let allocation = self.allocate_location();
            self.bindings.insert(String::from(name), allocation);

            Ok(allocation)
        }
    }

    fn lookup(&self, name: &str) -> BindingResult {
        if let Some(variable) = self.bindings.get(name) {
            BindingResult::Variable(*variable)
        } else {
            self.base_environment.lookup(name)
        }
    }

    fn get_number_of_variables(&self) -> u32 {
        self.base_environment.get_number_of_variables()
    }

    fn create_sub_environment<'b>(&'b mut self) -> Box<BindingEnvironment + 'b> {
        Box::new(ChildBindingEnvironment {
            base_environment:   self,
            bindings:           HashMap::new()
        })
    }
}

impl<'a> BindingEnvironment for (&'a mut BindingEnvironment, &'a BindingEnvironment) {
    fn allocate_location(&mut self) -> u32 {
        let (ref mut primary, _) = *self;

        primary.allocate_location()
    }

    fn allocate_variable(&mut self, name: &str) -> Result<u32, BindingError> {
        let (ref mut primary, _) = *self;

        primary.allocate_variable(name)
    }

    fn lookup(&self, name: &str) -> BindingResult {
        let (ref primary, ref secondary) = *self;

        match primary.lookup(name) {
            BindingResult::Error(_) => secondary.lookup(name),
            found                   => found
        }
    }

    fn get_number_of_variables(&self) -> u32 {
        let (ref primary, ref secondary) = *self;

        cmp::max(primary.get_number_of_variables(), secondary.get_number_of_variables())
    }

    fn create_sub_environment<'b>(&'b mut self) -> Box<BindingEnvironment + 'b> {
        Box::new(ChildBindingEnvironment {
            base_environment:   self,
            bindings:           HashMap::new()
        })
    }
}

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

    #[test]
    fn can_allocate_location() {
        let empty_environment   = EmptyEnvironment::new();
        let mut binding         = BindingEnvironment::from_environment(&empty_environment);

        assert!(binding.allocate_location() == 0);
    }

    #[test]
    fn can_allocate_many_locations() {
        let empty_environment   = EmptyEnvironment::new();
        let mut binding         = BindingEnvironment::from_environment(&empty_environment);

        assert!(binding.allocate_location() == 0);
        assert!(binding.allocate_location() == 1);
        assert!(binding.allocate_location() == 2);
    }

    #[test]
    fn can_get_number_of_variables() {
        let empty_environment   = EmptyEnvironment::new();
        let mut binding         = BindingEnvironment::from_environment(&empty_environment);

        binding.allocate_location();
        binding.allocate_location();
        binding.allocate_location();

        assert!(binding.get_number_of_variables() == 3);

        binding.allocate_location();

        assert!(binding.get_number_of_variables() == 4);
    }

    #[test]
    fn allocating_locations_in_child_environments_also_does_in_parent() {
        let empty_environment   = EmptyEnvironment::new();
        let mut binding         = BindingEnvironment::from_environment(&empty_environment);

        assert!(binding.allocate_location() == 0);

        {
            let mut child_environment = binding.create_sub_environment();

            assert!(child_environment.allocate_location() == 1);
        }

        assert!(binding.allocate_location() == 2);
    }

    #[test]
    fn can_reallocate_variable_name_in_child_environment() {
        let empty_environment   = EmptyEnvironment::new();
        let mut binding         = BindingEnvironment::from_environment(&empty_environment);

        binding.allocate_variable("test");
        assert!(match binding.lookup("test") { BindingResult::Variable(0) => true, _ => false });

        {
            let mut child_environment = binding.create_sub_environment();
            assert!(child_environment.allocate_variable("test") == Ok(1));
        }

        assert!(match binding.lookup("test") { BindingResult::Variable(0) => true, _ => false });
    }

    #[test]
    fn child_environment_lookup_falls_through_to_parent() {
        let empty_environment   = EmptyEnvironment::new();
        let mut binding         = BindingEnvironment::from_environment(&empty_environment);

        binding.allocate_variable("test1");
        binding.allocate_variable("test2");
        assert!(match binding.lookup("test1") { BindingResult::Variable(0) => true, _ => false });
        assert!(match binding.lookup("test2") { BindingResult::Variable(1) => true, _ => false });

        {
            let mut child_environment = binding.create_sub_environment();
            assert!(child_environment.allocate_variable("test1") == Ok(2));

            assert!(match child_environment.lookup("test1") { BindingResult::Variable(2) => true, _ => false });
            assert!(match child_environment.lookup("test2") { BindingResult::Variable(1) => true, _ => false });
        }

        assert!(match binding.lookup("test1") { BindingResult::Variable(0) => true, _ => false });
        assert!(match binding.lookup("test2") { BindingResult::Variable(1) => true, _ => false });
    }

    #[test]
    fn can_allocate_variable_name() {
        let empty_environment   = EmptyEnvironment::new();
        let mut binding         = BindingEnvironment::from_environment(&empty_environment);

        assert!(binding.allocate_variable("test") == Ok(0));
    }

    #[test]
    fn cannot_allocate_variable_name_twice() {
        let empty_environment   = EmptyEnvironment::new();
        let mut binding         = BindingEnvironment::from_environment(&empty_environment);

        assert!(binding.allocate_variable("test") == Ok(0));
        assert!(binding.allocate_variable("test") == Err(BindingError::AlreadyInUse));
    }

    #[test]
    fn can_lookup_variable_name() {
        let empty_environment   = EmptyEnvironment::new();
        let mut binding         = BindingEnvironment::from_environment(&empty_environment);

        binding.allocate_variable("test");

        assert!(match binding.lookup("test") { BindingResult::Variable(v) => v == 0, _ => false });
    }

    #[test]
    fn can_lookup_many_variable_names() {
        let empty_environment   = EmptyEnvironment::new();
        let mut binding         = BindingEnvironment::from_environment(&empty_environment);

        binding.allocate_variable("test1");
        binding.allocate_variable("test2");
        binding.allocate_variable("test3");

        assert!(match binding.lookup("test1") { BindingResult::Variable(v) => v == 0, _ => false });
        assert!(match binding.lookup("test2") { BindingResult::Variable(v) => v == 1, _ => false });
        assert!(match binding.lookup("test3") { BindingResult::Variable(v) => v == 2, _ => false });
    }

    #[test]
    fn can_lookup_tool_name() {
        let tool_environment = DynamicEnvironment::new();
        tool_environment.define("test", Box::new(make_pure_tool(|_: ()| "Success")));

        let mut binding = BindingEnvironment::from_environment(&tool_environment);

        assert!(match binding.lookup("test") { BindingResult::Tool(_) => true, _ => false });
    }

    #[test]
    fn variable_name_has_precedence_over_tool() {
        let tool_environment = DynamicEnvironment::new();
        tool_environment.define("test", Box::new(make_pure_tool(|_: ()| "Success")));

        let mut binding = BindingEnvironment::from_environment(&tool_environment);

        binding.allocate_variable("test");

        assert!(match binding.lookup("test") { BindingResult::Variable(v) => v == 0, _ => false });
    }
}
<|endoftext|>


<commit_before>use syntax::ast;
use rustc::ty::{self, Ty, TyCtxt, ParamEnv};
use syntax_pos::symbol::Symbol;
use rustc::mir::interpret::{ConstValue, Scalar};

#[derive(PartialEq)]
crate enum LitToConstError {
    UnparseableFloat,
    Reported,
}

crate fn lit_to_const<'a, 'gcx, 'tcx>(
    lit: &'tcx ast::LitKind,
    tcx: TyCtxt<'a, 'gcx, 'tcx>,
    ty: Ty<'tcx>,
    neg: bool,
) -> Result<ty::Const<'tcx>, LitToConstError> {
    use syntax::ast::*;

    let trunc = |n| {
        let param_ty = ParamEnv::reveal_all().and(tcx.lift_to_global(&ty).unwrap());
        let width = tcx.layout_of(param_ty).map_err(|_| LitToConstError::Reported)?.size;
        trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits());
        let shift = 128 - width.bits();
        let result = (n << shift) >> shift;
        trace!("trunc result: {}", result);
        Ok(ConstValue::Scalar(Scalar::Bits {
            bits: result,
            size: width.bytes() as u8,
        }))
    };

    use rustc::mir::interpret::*;
    let lit = match *lit {
        LitKind::Str(ref s, _) => {
            let s = s.as_str();
            let id = tcx.allocate_bytes(s.as_bytes());
            ConstValue::new_slice(Scalar::Ptr(id.into()), s.len() as u64)
        },
        LitKind::Err(ref s) => {
            let s = s.as_str();
            let id = tcx.allocate_bytes(s.as_bytes());
            return Ok(ty::Const {
                val: ConstValue::new_slice(Scalar::Ptr(id.into()), s.len() as u64),
                ty: tcx.types.err,
            });
        },
        LitKind::ByteStr(ref data) => {
            let id = tcx.allocate_bytes(data);
            ConstValue::Scalar(Scalar::Ptr(id.into()))
        },
        LitKind::Byte(n) => ConstValue::Scalar(Scalar::Bits {
            bits: n as u128,
            size: 1,
        }),
        LitKind::Int(n, _) if neg => {
            let n = n as i128;
            let n = n.overflowing_neg().0;
            trunc(n as u128)?
        },
        LitKind::Int(n, _) => trunc(n)?,
        LitKind::Float(n, fty) => {
            parse_float(n, fty, neg).map_err(|_| LitToConstError::UnparseableFloat)?
        }
        LitKind::FloatUnsuffixed(n) => {
            let fty = match ty.sty {
                ty::Float(fty) => fty,
                _ => bug!()
            };
            parse_float(n, fty, neg).map_err(|_| LitToConstError::UnparseableFloat)?
        }
        LitKind::Bool(b) => ConstValue::Scalar(Scalar::from_bool(b)),
        LitKind::Char(c) => ConstValue::Scalar(Scalar::from_char(c)),
    };
    Ok(ty::Const { val: lit, ty })
}

fn parse_float<'tcx>(
    num: Symbol,
    fty: ast::FloatTy,
    neg: bool,
) -> Result<ConstValue<'tcx>, ()> {
    let num = num.as_str();
    use rustc_apfloat::ieee::{Single, Double};
    use rustc_apfloat::Float;
    let (bits, size) = match fty {
        ast::FloatTy::F32 => {
            num.parse::<f32>().map_err(|_| ())?;
            let mut f = num.parse::<Single>().unwrap_or_else(|e| {
                panic!("apfloat::ieee::Single failed to parse `{}`: {:?}", num, e)
            });
            if neg {
                f = -f;
            }
            (f.to_bits(), 4)
        }
        ast::FloatTy::F64 => {
            num.parse::<f64>().map_err(|_| ())?;
            let mut f = num.parse::<Double>().unwrap_or_else(|e| {
                panic!("apfloat::ieee::Single failed to parse `{}`: {:?}", num, e)
            });
            if neg {
                f = -f;
            }
            (f.to_bits(), 8)
        }
    };

    Ok(ConstValue::Scalar(Scalar::Bits { bits, size }))
}
<commit_msg>Rollup merge of #58687 - kenta7777:reduce-miri-code-repetition, r=oli-obk<commit_after>use syntax::ast;
use rustc::ty::{self, Ty, TyCtxt, ParamEnv};
use syntax_pos::symbol::Symbol;
use rustc::mir::interpret::{ConstValue, Scalar};

#[derive(PartialEq)]
crate enum LitToConstError {
    UnparseableFloat,
    Reported,
}

crate fn lit_to_const<'a, 'gcx, 'tcx>(
    lit: &'tcx ast::LitKind,
    tcx: TyCtxt<'a, 'gcx, 'tcx>,
    ty: Ty<'tcx>,
    neg: bool,
) -> Result<ty::Const<'tcx>, LitToConstError> {
    use syntax::ast::*;

    let trunc = |n| {
        let param_ty = ParamEnv::reveal_all().and(tcx.lift_to_global(&ty).unwrap());
        let width = tcx.layout_of(param_ty).map_err(|_| LitToConstError::Reported)?.size;
        trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits());
        let result = truncate(n, width);
        trace!("trunc result: {}", result);
        Ok(ConstValue::Scalar(Scalar::Bits {
            bits: result,
            size: width.bytes() as u8,
        }))
    };

    use rustc::mir::interpret::*;
    let lit = match *lit {
        LitKind::Str(ref s, _) => {
            let s = s.as_str();
            let id = tcx.allocate_bytes(s.as_bytes());
            ConstValue::new_slice(Scalar::Ptr(id.into()), s.len() as u64)
        },
        LitKind::Err(ref s) => {
            let s = s.as_str();
            let id = tcx.allocate_bytes(s.as_bytes());
            return Ok(ty::Const {
                val: ConstValue::new_slice(Scalar::Ptr(id.into()), s.len() as u64),
                ty: tcx.types.err,
            });
        },
        LitKind::ByteStr(ref data) => {
            let id = tcx.allocate_bytes(data);
            ConstValue::Scalar(Scalar::Ptr(id.into()))
        },
        LitKind::Byte(n) => ConstValue::Scalar(Scalar::Bits {
            bits: n as u128,
            size: 1,
        }),
        LitKind::Int(n, _) if neg => {
            let n = n as i128;
            let n = n.overflowing_neg().0;
            trunc(n as u128)?
        },
        LitKind::Int(n, _) => trunc(n)?,
        LitKind::Float(n, fty) => {
            parse_float(n, fty, neg).map_err(|_| LitToConstError::UnparseableFloat)?
        }
        LitKind::FloatUnsuffixed(n) => {
            let fty = match ty.sty {
                ty::Float(fty) => fty,
                _ => bug!()
            };
            parse_float(n, fty, neg).map_err(|_| LitToConstError::UnparseableFloat)?
        }
        LitKind::Bool(b) => ConstValue::Scalar(Scalar::from_bool(b)),
        LitKind::Char(c) => ConstValue::Scalar(Scalar::from_char(c)),
    };
    Ok(ty::Const { val: lit, ty })
}

fn parse_float<'tcx>(
    num: Symbol,
    fty: ast::FloatTy,
    neg: bool,
) -> Result<ConstValue<'tcx>, ()> {
    let num = num.as_str();
    use rustc_apfloat::ieee::{Single, Double};
    use rustc_apfloat::Float;
    let (bits, size) = match fty {
        ast::FloatTy::F32 => {
            num.parse::<f32>().map_err(|_| ())?;
            let mut f = num.parse::<Single>().unwrap_or_else(|e| {
                panic!("apfloat::ieee::Single failed to parse `{}`: {:?}", num, e)
            });
            if neg {
                f = -f;
            }
            (f.to_bits(), 4)
        }
        ast::FloatTy::F64 => {
            num.parse::<f64>().map_err(|_| ())?;
            let mut f = num.parse::<Double>().unwrap_or_else(|e| {
                panic!("apfloat::ieee::Single failed to parse `{}`: {:?}", num, e)
            });
            if neg {
                f = -f;
            }
            (f.to_bits(), 8)
        }
    };

    Ok(ConstValue::Scalar(Scalar::Bits { bits, size }))
}
<|endoftext|>


<commit_before>use borrow_check::nll::type_check;
use build;
use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
use rustc::mir::{Mir, MirPhase, Promoted};
use rustc::ty::TyCtxt;
use rustc::ty::query::Providers;
use rustc::ty::steal::Steal;
use rustc::hir;
use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
use rustc::util::nodemap::DefIdSet;
use rustc_data_structures::sync::Lrc;
use std::borrow::Cow;
use syntax::ast;
use syntax_pos::Span;

pub mod add_retag;
pub mod add_moves_for_packed_drops;
pub mod cleanup_post_borrowck;
pub mod check_unsafety;
pub mod simplify_branches;
pub mod simplify;
pub mod erase_regions;
pub mod no_landing_pads;
pub mod rustc_peek;
pub mod elaborate_drops;
pub mod add_call_guards;
pub mod promote_consts;
pub mod qualify_consts;
mod qualify_min_const_fn;
pub mod remove_noop_landing_pads;
pub mod dump_mir;
pub mod deaggregator;
pub mod instcombine;
pub mod copy_prop;
pub mod const_prop;
pub mod generator;
pub mod inline;
pub mod lower_128bit;
pub mod uniform_array_move_out;

pub(crate) fn provide(providers: &mut Providers) {
    self::qualify_consts::provide(providers);
    self::check_unsafety::provide(providers);
    *providers = Providers {
        mir_keys,
        mir_built,
        mir_const,
        mir_validated,
        optimized_mir,
        is_mir_available,
        ..*providers
    };
}

fn is_mir_available<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
    tcx.mir_keys(def_id.krate).contains(&def_id)
}

/// Finds the full set of def-ids within the current crate that have
/// MIR associated with them.
fn mir_keys<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, krate: CrateNum)
                      -> Lrc<DefIdSet> {
    assert_eq!(krate, LOCAL_CRATE);

    let mut set = DefIdSet::default();

    // All body-owners have MIR associated with them.
    set.extend(tcx.body_owners());

    // Additionally, tuple struct/variant constructors have MIR, but
    // they don't have a BodyId, so we need to build them separately.
    struct GatherCtors<'a, 'tcx: 'a> {
        tcx: TyCtxt<'a, 'tcx, 'tcx>,
        set: &'a mut DefIdSet,
    }
    impl<'a, 'tcx> Visitor<'tcx> for GatherCtors<'a, 'tcx> {
        fn visit_variant_data(&mut self,
                              v: &'tcx hir::VariantData,
                              _: ast::Name,
                              _: &'tcx hir::Generics,
                              _: ast::NodeId,
                              _: Span) {
            if let hir::VariantData::Tuple(_, node_id) = *v {
                self.set.insert(self.tcx.hir().local_def_id(node_id));
            }
            intravisit::walk_struct_def(self, v)
        }
        fn nested_visit_map<'b>(&'b mut self) -> NestedVisitorMap<'b, 'tcx> {
            NestedVisitorMap::None
        }
    }
    tcx.hir().krate().visit_all_item_likes(&mut GatherCtors {
        tcx,
        set: &mut set,
    }.as_deep_visitor());

    Lrc::new(set)
}

fn mir_built<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx Steal<Mir<'tcx>> {
    let mir = build::mir_build(tcx, def_id);
    tcx.alloc_steal_mir(mir)
}

/// Where a specific Mir comes from.
#[derive(Debug, Copy, Clone)]
pub struct MirSource {
    pub def_id: DefId,

    /// If `Some`, this is a promoted rvalue within the parent function.
    pub promoted: Option<Promoted>,
}

impl MirSource {
    pub fn item(def_id: DefId) -> Self {
        MirSource {
            def_id,
            promoted: None
        }
    }
}

/// Generates a default name for the pass based on the name of the
/// type `T`.
pub fn default_name<T: ?Sized>() -> Cow<'static, str> {
    let name = unsafe { ::std::intrinsics::type_name::<T>() };
    if let Some(tail) = name.rfind(":") {
        Cow::from(&name[tail+1..])
    } else {
        Cow::from(name)
    }
}

/// A streamlined trait that you can implement to create a pass; the
/// pass will be named after the type, and it will consist of a main
/// loop that goes over each available MIR and applies `run_pass`.
pub trait MirPass {
    fn name<'a>(&'a self) -> Cow<'a, str> {
        default_name::<Self>()
    }

    fn run_pass<'a, 'tcx>(&self,
                          tcx: TyCtxt<'a, 'tcx, 'tcx>,
                          source: MirSource,
                          mir: &mut Mir<'tcx>);
}

pub fn run_passes(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &mut Mir<'tcx>,
    def_id: DefId,
    mir_phase: MirPhase,
    passes: &[&dyn MirPass],
) {
    let phase_index = mir_phase.phase_index();

    let run_passes = |mir: &mut Mir<'tcx>, promoted| {
        if mir.phase >= mir_phase {
            return;
        }

        let source = MirSource {
            def_id,
            promoted,
        };
        let mut index = 0;
        let mut run_pass = |pass: &dyn MirPass| {
            let run_hooks = |mir: &_, index, is_after| {
                dump_mir::on_mir_pass(tcx, &format_args!("{:03}-{:03}", phase_index, index),
                                      &pass.name(), source, mir, is_after);
            };
            run_hooks(mir, index, false);
            pass.run_pass(tcx, source, mir);
            run_hooks(mir, index, true);

            index += 1;
        };

        for pass in passes {
            run_pass(*pass);
        }

        mir.phase = mir_phase;
    };

    run_passes(mir, None);

    for (index, promoted_mir) in mir.promoted.iter_enumerated_mut() {
        run_passes(promoted_mir, Some(index));

        //Let's make sure we don't miss any nested instances
        assert!(promoted_mir.promoted.is_empty())
    }
}

fn mir_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx Steal<Mir<'tcx>> {
    // Unsafety check uses the raw mir, so make sure it is run
    let _ = tcx.unsafety_check_result(def_id);

    let mut mir = tcx.mir_built(def_id).steal();
    run_passes(tcx, &mut mir, def_id, MirPhase::Const, &[
        // What we need to do constant evaluation.
        &simplify::SimplifyCfg::new("initial"),
        &type_check::TypeckMir,
        &rustc_peek::SanityCheck,
        &uniform_array_move_out::UniformArrayMoveOut,
    ]);
    tcx.alloc_steal_mir(mir)
}

fn mir_validated<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx Steal<Mir<'tcx>> {
    let node_id = tcx.hir().as_local_node_id(def_id).unwrap();
    if let hir::BodyOwnerKind::Const = tcx.hir().body_owner_kind(node_id) {
        // Ensure that we compute the `mir_const_qualif` for constants at
        // this point, before we steal the mir-const result.
        let _ = tcx.mir_const_qualif(def_id);
    }

    let mut mir = tcx.mir_const(def_id).steal();
    run_passes(tcx, &mut mir, def_id, MirPhase::Validated, &[
        // What we need to run borrowck etc.
        &qualify_consts::QualifyAndPromoteConstants,
        &simplify::SimplifyCfg::new("qualify-consts"),
    ]);
    tcx.alloc_steal_mir(mir)
}

fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx Mir<'tcx> {
    // (Mir-)Borrowck uses `mir_validated`, so we have to force it to
    // execute before we can steal.
    let _ = tcx.mir_borrowck(def_id);

    if tcx.use_ast_borrowck() {
        let _ = tcx.borrowck(def_id);
    }

    let mut mir = tcx.mir_validated(def_id).steal();
    run_passes(tcx, &mut mir, def_id, MirPhase::Optimized, &[
        // Remove all things not needed by analysis
        &no_landing_pads::NoLandingPads,
        &simplify_branches::SimplifyBranches::new("initial"),
        &remove_noop_landing_pads::RemoveNoopLandingPads,
        // Remove all `AscribeUserType` statements.
        &cleanup_post_borrowck::CleanAscribeUserType,
        // Remove all `FakeRead` statements and the borrows that are only
        // used for checking matches
        &cleanup_post_borrowck::CleanFakeReadsAndBorrows,

        &simplify::SimplifyCfg::new("early-opt"),

        // These next passes must be executed together
        &add_call_guards::CriticalCallEdges,
        &elaborate_drops::ElaborateDrops,
        &no_landing_pads::NoLandingPads,
        // AddMovesForPackedDrops needs to run after drop
        // elaboration.
        &add_moves_for_packed_drops::AddMovesForPackedDrops,
        // AddRetag needs to run after ElaborateDrops, and it needs
        // an AllCallEdges pass right before it.  Otherwise it should
        // run fairly late, but before optimizations begin.
        &add_call_guards::AllCallEdges,
        &add_retag::AddRetag,

        &simplify::SimplifyCfg::new("elaborate-drops"),

        // No lifetime analysis based on borrowing can be done from here on out.

        // From here on out, regions are gone.
        &erase_regions::EraseRegions,

        &lower_128bit::Lower128Bit,

        // Optimizations begin.
        &uniform_array_move_out::RestoreSubsliceArrayMoveOut,
        &inline::Inline,

        // Lowering generator control-flow and variables
        // has to happen before we do anything else to them.
        &generator::StateTransform,

        &instcombine::InstCombine,
        &const_prop::ConstProp,
        &simplify_branches::SimplifyBranches::new("after-const-prop"),
        &deaggregator::Deaggregator,
        &copy_prop::CopyPropagation,
        &remove_noop_landing_pads::RemoveNoopLandingPads,
        &simplify::SimplifyCfg::new("final"),
        &simplify::SimplifyLocals,

        &add_call_guards::CriticalCallEdges,
        &dump_mir::Marker("PreCodegen"),
    ]);
    tcx.alloc_mir(mir)
}
<commit_msg>Rollup merge of #57342 - phansch:make_public, r=Centril<commit_after>use borrow_check::nll::type_check;
use build;
use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
use rustc::mir::{Mir, MirPhase, Promoted};
use rustc::ty::TyCtxt;
use rustc::ty::query::Providers;
use rustc::ty::steal::Steal;
use rustc::hir;
use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap};
use rustc::util::nodemap::DefIdSet;
use rustc_data_structures::sync::Lrc;
use std::borrow::Cow;
use syntax::ast;
use syntax_pos::Span;

pub mod add_retag;
pub mod add_moves_for_packed_drops;
pub mod cleanup_post_borrowck;
pub mod check_unsafety;
pub mod simplify_branches;
pub mod simplify;
pub mod erase_regions;
pub mod no_landing_pads;
pub mod rustc_peek;
pub mod elaborate_drops;
pub mod add_call_guards;
pub mod promote_consts;
pub mod qualify_consts;
pub mod qualify_min_const_fn;
pub mod remove_noop_landing_pads;
pub mod dump_mir;
pub mod deaggregator;
pub mod instcombine;
pub mod copy_prop;
pub mod const_prop;
pub mod generator;
pub mod inline;
pub mod lower_128bit;
pub mod uniform_array_move_out;

pub(crate) fn provide(providers: &mut Providers) {
    self::qualify_consts::provide(providers);
    self::check_unsafety::provide(providers);
    *providers = Providers {
        mir_keys,
        mir_built,
        mir_const,
        mir_validated,
        optimized_mir,
        is_mir_available,
        ..*providers
    };
}

fn is_mir_available<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
    tcx.mir_keys(def_id.krate).contains(&def_id)
}

/// Finds the full set of def-ids within the current crate that have
/// MIR associated with them.
fn mir_keys<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, krate: CrateNum)
                      -> Lrc<DefIdSet> {
    assert_eq!(krate, LOCAL_CRATE);

    let mut set = DefIdSet::default();

    // All body-owners have MIR associated with them.
    set.extend(tcx.body_owners());

    // Additionally, tuple struct/variant constructors have MIR, but
    // they don't have a BodyId, so we need to build them separately.
    struct GatherCtors<'a, 'tcx: 'a> {
        tcx: TyCtxt<'a, 'tcx, 'tcx>,
        set: &'a mut DefIdSet,
    }
    impl<'a, 'tcx> Visitor<'tcx> for GatherCtors<'a, 'tcx> {
        fn visit_variant_data(&mut self,
                              v: &'tcx hir::VariantData,
                              _: ast::Name,
                              _: &'tcx hir::Generics,
                              _: ast::NodeId,
                              _: Span) {
            if let hir::VariantData::Tuple(_, node_id) = *v {
                self.set.insert(self.tcx.hir().local_def_id(node_id));
            }
            intravisit::walk_struct_def(self, v)
        }
        fn nested_visit_map<'b>(&'b mut self) -> NestedVisitorMap<'b, 'tcx> {
            NestedVisitorMap::None
        }
    }
    tcx.hir().krate().visit_all_item_likes(&mut GatherCtors {
        tcx,
        set: &mut set,
    }.as_deep_visitor());

    Lrc::new(set)
}

fn mir_built<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx Steal<Mir<'tcx>> {
    let mir = build::mir_build(tcx, def_id);
    tcx.alloc_steal_mir(mir)
}

/// Where a specific Mir comes from.
#[derive(Debug, Copy, Clone)]
pub struct MirSource {
    pub def_id: DefId,

    /// If `Some`, this is a promoted rvalue within the parent function.
    pub promoted: Option<Promoted>,
}

impl MirSource {
    pub fn item(def_id: DefId) -> Self {
        MirSource {
            def_id,
            promoted: None
        }
    }
}

/// Generates a default name for the pass based on the name of the
/// type `T`.
pub fn default_name<T: ?Sized>() -> Cow<'static, str> {
    let name = unsafe { ::std::intrinsics::type_name::<T>() };
    if let Some(tail) = name.rfind(":") {
        Cow::from(&name[tail+1..])
    } else {
        Cow::from(name)
    }
}

/// A streamlined trait that you can implement to create a pass; the
/// pass will be named after the type, and it will consist of a main
/// loop that goes over each available MIR and applies `run_pass`.
pub trait MirPass {
    fn name<'a>(&'a self) -> Cow<'a, str> {
        default_name::<Self>()
    }

    fn run_pass<'a, 'tcx>(&self,
                          tcx: TyCtxt<'a, 'tcx, 'tcx>,
                          source: MirSource,
                          mir: &mut Mir<'tcx>);
}

pub fn run_passes(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &mut Mir<'tcx>,
    def_id: DefId,
    mir_phase: MirPhase,
    passes: &[&dyn MirPass],
) {
    let phase_index = mir_phase.phase_index();

    let run_passes = |mir: &mut Mir<'tcx>, promoted| {
        if mir.phase >= mir_phase {
            return;
        }

        let source = MirSource {
            def_id,
            promoted,
        };
        let mut index = 0;
        let mut run_pass = |pass: &dyn MirPass| {
            let run_hooks = |mir: &_, index, is_after| {
                dump_mir::on_mir_pass(tcx, &format_args!("{:03}-{:03}", phase_index, index),
                                      &pass.name(), source, mir, is_after);
            };
            run_hooks(mir, index, false);
            pass.run_pass(tcx, source, mir);
            run_hooks(mir, index, true);

            index += 1;
        };

        for pass in passes {
            run_pass(*pass);
        }

        mir.phase = mir_phase;
    };

    run_passes(mir, None);

    for (index, promoted_mir) in mir.promoted.iter_enumerated_mut() {
        run_passes(promoted_mir, Some(index));

        //Let's make sure we don't miss any nested instances
        assert!(promoted_mir.promoted.is_empty())
    }
}

fn mir_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx Steal<Mir<'tcx>> {
    // Unsafety check uses the raw mir, so make sure it is run
    let _ = tcx.unsafety_check_result(def_id);

    let mut mir = tcx.mir_built(def_id).steal();
    run_passes(tcx, &mut mir, def_id, MirPhase::Const, &[
        // What we need to do constant evaluation.
        &simplify::SimplifyCfg::new("initial"),
        &type_check::TypeckMir,
        &rustc_peek::SanityCheck,
        &uniform_array_move_out::UniformArrayMoveOut,
    ]);
    tcx.alloc_steal_mir(mir)
}

fn mir_validated<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx Steal<Mir<'tcx>> {
    let node_id = tcx.hir().as_local_node_id(def_id).unwrap();
    if let hir::BodyOwnerKind::Const = tcx.hir().body_owner_kind(node_id) {
        // Ensure that we compute the `mir_const_qualif` for constants at
        // this point, before we steal the mir-const result.
        let _ = tcx.mir_const_qualif(def_id);
    }

    let mut mir = tcx.mir_const(def_id).steal();
    run_passes(tcx, &mut mir, def_id, MirPhase::Validated, &[
        // What we need to run borrowck etc.
        &qualify_consts::QualifyAndPromoteConstants,
        &simplify::SimplifyCfg::new("qualify-consts"),
    ]);
    tcx.alloc_steal_mir(mir)
}

fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx Mir<'tcx> {
    // (Mir-)Borrowck uses `mir_validated`, so we have to force it to
    // execute before we can steal.
    let _ = tcx.mir_borrowck(def_id);

    if tcx.use_ast_borrowck() {
        let _ = tcx.borrowck(def_id);
    }

    let mut mir = tcx.mir_validated(def_id).steal();
    run_passes(tcx, &mut mir, def_id, MirPhase::Optimized, &[
        // Remove all things not needed by analysis
        &no_landing_pads::NoLandingPads,
        &simplify_branches::SimplifyBranches::new("initial"),
        &remove_noop_landing_pads::RemoveNoopLandingPads,
        // Remove all `AscribeUserType` statements.
        &cleanup_post_borrowck::CleanAscribeUserType,
        // Remove all `FakeRead` statements and the borrows that are only
        // used for checking matches
        &cleanup_post_borrowck::CleanFakeReadsAndBorrows,

        &simplify::SimplifyCfg::new("early-opt"),

        // These next passes must be executed together
        &add_call_guards::CriticalCallEdges,
        &elaborate_drops::ElaborateDrops,
        &no_landing_pads::NoLandingPads,
        // AddMovesForPackedDrops needs to run after drop
        // elaboration.
        &add_moves_for_packed_drops::AddMovesForPackedDrops,
        // AddRetag needs to run after ElaborateDrops, and it needs
        // an AllCallEdges pass right before it.  Otherwise it should
        // run fairly late, but before optimizations begin.
        &add_call_guards::AllCallEdges,
        &add_retag::AddRetag,

        &simplify::SimplifyCfg::new("elaborate-drops"),

        // No lifetime analysis based on borrowing can be done from here on out.

        // From here on out, regions are gone.
        &erase_regions::EraseRegions,

        &lower_128bit::Lower128Bit,

        // Optimizations begin.
        &uniform_array_move_out::RestoreSubsliceArrayMoveOut,
        &inline::Inline,

        // Lowering generator control-flow and variables
        // has to happen before we do anything else to them.
        &generator::StateTransform,

        &instcombine::InstCombine,
        &const_prop::ConstProp,
        &simplify_branches::SimplifyBranches::new("after-const-prop"),
        &deaggregator::Deaggregator,
        &copy_prop::CopyPropagation,
        &remove_noop_landing_pads::RemoveNoopLandingPads,
        &simplify::SimplifyCfg::new("final"),
        &simplify::SimplifyLocals,

        &add_call_guards::CriticalCallEdges,
        &dump_mir::Marker("PreCodegen"),
    ]);
    tcx.alloc_mir(mir)
}
<|endoftext|>


<commit_before>use kernel_micro::{include_file, MicroKernel};

pub fn new() -> MicroKernel {
    MicroKernel::new(
        "udeno",
        &["javascript", "typescript"],
        ("deno", ">1.7"),
        &["run", "--quiet", "--unstable", "{{script}}"],
        include_file!("deno-kernel.ts"),
        &[include_file!("deno-codec.ts")],
        "{{name}} = JSON.parse('{{json}}')",
        "{{name}}",
    )
}

#[cfg(test)]
mod tests {
    use super::*;
    use kernel::{eyre::Result, stencila_schema::Node, KernelTrait};
    use test_utils::{assert_json_eq, serde_json::json};

    /// Tests of basic functionality
    /// This test is replicated in all the microkernels.
    /// Other test should be written for language specific quirks and regressions.
    #[tokio::test]
    async fn basics() -> Result<()> {
        let mut kernel = new();
        if !kernel.available().await {
            return Ok(());
        } else {
            kernel.start().await?;
        }

        // Assign a variable and output it
        let (outputs, messages) = kernel.exec("const a = 2\na").await?;
        assert_json_eq!(messages, json!([]));
        assert_json_eq!(outputs, [2]);

        // Print the variable twice and then output it
        let (outputs, messages) = kernel.exec("console.log(a)\nconsole.log(a)\na").await?;
        assert_json_eq!(messages, json!([]));
        assert_json_eq!(outputs, [2, 2, 2]);

        // Syntax error
        let (outputs, messages) = kernel.exec("bad ^ # syntax").await?;
        //assert_json_eq!(messages[0].error_type, "SyntaxError");
        //assert_json_eq!(messages[0].error_message, "Invalid or unexpected token");
        assert!(messages[0].stack_trace.is_some());
        assert_json_eq!(outputs, json!([]));

        // Runtime error
        let (outputs, messages) = kernel.exec("foo").await?;
        assert_json_eq!(messages[0].error_type, "ReferenceError");
        assert_json_eq!(messages[0].error_message, "foo is not defined");
        assert!(messages[0].stack_trace.is_some());
        assert_json_eq!(outputs, json!([]));

        // Set and get another variable
        kernel.set("b", Node::Integer(3)).await?;
        let b = kernel.get("b").await?;
        assert_json_eq!(b, 3);

        // Use both variables
        let (outputs, messages) = kernel.exec("a*b").await?;
        assert_json_eq!(messages, json!([]));
        assert_json_eq!(outputs, [6]);

        Ok(())
    }

    // Test that `console.log` arguments are treated as separate outputs
    #[tokio::test]
    async fn console_log() -> Result<()> {
        let mut kernel = new();
        if !kernel.available().await {
            return Ok(());
        } else {
            kernel.start().await?;
        }

        let (outputs, messages) = kernel.exec("console.log(1)").await?;
        assert_json_eq!(messages, json!([]));
        assert_json_eq!(outputs, [1]);

        let (outputs, messages) = kernel.exec("console.log(1, 2, 3, 4)").await?;
        assert_json_eq!(messages, json!([]));
        assert_json_eq!(outputs, [1, 2, 3, 4]);

        let (outputs, messages) = kernel.exec("console.log([1, 2, 3], 4, 'str')").await?;
        assert_json_eq!(messages, json!([]));
        assert_json_eq!(outputs, json!([[1, 2, 3], 4, "str"]));

        Ok(())
    }
}
<commit_msg>fix(Deno microkernel): FIx semver requirement<commit_after>use kernel_micro::{include_file, MicroKernel};

pub fn new() -> MicroKernel {
    MicroKernel::new(
        "udeno",
        &["javascript", "typescript"],
        ("deno", ">=1.7"),
        &["run", "--quiet", "--unstable", "{{script}}"],
        include_file!("deno-kernel.ts"),
        &[include_file!("deno-codec.ts")],
        "{{name}} = JSON.parse('{{json}}')",
        "{{name}}",
    )
}

#[cfg(test)]
mod tests {
    use super::*;
    use kernel::{eyre::Result, stencila_schema::Node, KernelTrait};
    use test_utils::{assert_json_eq, serde_json::json};

    /// Tests of basic functionality
    /// This test is replicated in all the microkernels.
    /// Other test should be written for language specific quirks and regressions.
    #[tokio::test]
    async fn basics() -> Result<()> {
        let mut kernel = new();
        if !kernel.available().await {
            return Ok(());
        } else {
            kernel.start().await?;
        }

        // Assign a variable and output it
        let (outputs, messages) = kernel.exec("const a = 2\na").await?;
        assert_json_eq!(messages, json!([]));
        assert_json_eq!(outputs, [2]);

        // Print the variable twice and then output it
        let (outputs, messages) = kernel.exec("console.log(a)\nconsole.log(a)\na").await?;
        assert_json_eq!(messages, json!([]));
        assert_json_eq!(outputs, [2, 2, 2]);

        // Syntax error
        let (outputs, messages) = kernel.exec("bad ^ # syntax").await?;
        //assert_json_eq!(messages[0].error_type, "SyntaxError");
        //assert_json_eq!(messages[0].error_message, "Invalid or unexpected token");
        assert!(messages[0].stack_trace.is_some());
        assert_json_eq!(outputs, json!([]));

        // Runtime error
        let (outputs, messages) = kernel.exec("foo").await?;
        assert_json_eq!(messages[0].error_type, "ReferenceError");
        assert_json_eq!(messages[0].error_message, "foo is not defined");
        assert!(messages[0].stack_trace.is_some());
        assert_json_eq!(outputs, json!([]));

        // Set and get another variable
        kernel.set("b", Node::Integer(3)).await?;
        let b = kernel.get("b").await?;
        assert_json_eq!(b, 3);

        // Use both variables
        let (outputs, messages) = kernel.exec("a*b").await?;
        assert_json_eq!(messages, json!([]));
        assert_json_eq!(outputs, [6]);

        Ok(())
    }

    // Test that `console.log` arguments are treated as separate outputs
    #[tokio::test]
    async fn console_log() -> Result<()> {
        let mut kernel = new();
        if !kernel.available().await {
            return Ok(());
        } else {
            kernel.start().await?;
        }

        let (outputs, messages) = kernel.exec("console.log(1)").await?;
        assert_json_eq!(messages, json!([]));
        assert_json_eq!(outputs, [1]);

        let (outputs, messages) = kernel.exec("console.log(1, 2, 3, 4)").await?;
        assert_json_eq!(messages, json!([]));
        assert_json_eq!(outputs, [1, 2, 3, 4]);

        let (outputs, messages) = kernel.exec("console.log([1, 2, 3], 4, 'str')").await?;
        assert_json_eq!(messages, json!([]));
        assert_json_eq!(outputs, json!([[1, 2, 3], 4, "str"]));

        Ok(())
    }
}
<|endoftext|>


<commit_before>pub use self::client_goal_handle::{AsyncClientGoalHandle, SyncClientGoalHandle};
pub use self::comm_state_machine::State;
use crate::msg::actionlib_msgs;
use crate::static_messages::{MUTEX_LOCK_FAIL, UNEXPECTED_FAILED_NAME_RESOLVE};
use crate::{Action, ActionResponse, FeedbackBody, GoalBody, GoalID};
use rosrust::error::Result;
use std::sync::{Arc, Mutex};

mod client_goal_handle;
mod comm_state_machine;
mod goal_manager;

pub struct ActionClient<T: Action> {
    namespace: String,
    pub_goal: rosrust::Publisher<T::Goal>,
    pub_cancel: rosrust::Publisher<GoalID>,
    manager: Arc<Mutex<goal_manager::GoalManager<T>>>,
    last_caller_id: Arc<Mutex<Option<String>>>,
    status_sub: rosrust::Subscriber,
    result_sub: rosrust::Subscriber,
    feedback_sub: rosrust::Subscriber,
}

impl<T: Action> ActionClient<T> {
    pub fn new(namespace: &str) -> Result<Self> {
        let pub_queue_size = decode_queue_size("actionlib_client_pub_queue_size", 10);
        let sub_queue_size = decode_queue_size("actionlib_client_sub_queue_size", 0);

        let pub_goal = rosrust::publish(&format!("{}/goal", namespace), pub_queue_size)?;
        let pub_cancel = rosrust::publish(&format!("{}/cancel", namespace), pub_queue_size)?;

        let on_send_goal = {
            let publisher = pub_goal.clone();
            move |data| {
                if let Err(err) = publisher.send(data) {
                    rosrust::ros_err!("Failed to publish goal: {}", err);
                }
            }
        };

        let on_cancel = {
            let publisher = pub_cancel.clone();
            move |data: GoalID| {
                if let Err(err) = publisher.send(data) {
                    rosrust::ros_err!("Failed to publish cancel: {}", err);
                }
            }
        };

        let manager = Arc::new(Mutex::new(goal_manager::GoalManager::new(
            on_send_goal,
            on_cancel,
        )));

        let last_caller_id: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None));

        let on_status = {
            let manager = Arc::clone(&manager);
            let last_caller_id = Arc::clone(&last_caller_id);
            move |message: actionlib_msgs::GoalStatusArray, caller_id: &str| {
                (*last_caller_id.lock().expect(MUTEX_LOCK_FAIL)) = Some(caller_id.into());
                manager
                    .lock()
                    .expect(MUTEX_LOCK_FAIL)
                    .update_statuses(&message);
            }
        };
        let on_result = {
            let manager = Arc::clone(&manager);
            move |result: T::Result| {
                manager
                    .lock()
                    .expect(MUTEX_LOCK_FAIL)
                    .update_results(&result.into_response());
            }
        };
        let on_feedback = {
            let manager = Arc::clone(&manager);
            move |feedback: T::Feedback| {
                manager
                    .lock()
                    .expect(MUTEX_LOCK_FAIL)
                    .update_feedbacks(&feedback.into_response());
            }
        };

        let status_sub = rosrust::subscribe_with_ids(
            &format!("{}/status", namespace),
            sub_queue_size,
            on_status,
        )?;
        let result_sub =
            rosrust::subscribe(&format!("{}/result", namespace), sub_queue_size, on_result)?;
        let feedback_sub = rosrust::subscribe(
            &format!("{}/feedback", namespace),
            sub_queue_size,
            on_feedback,
        )?;

        Ok(Self {
            namespace: namespace.into(),
            pub_goal,
            pub_cancel,
            manager,
            last_caller_id,
            status_sub,
            result_sub,
            feedback_sub,
        })
    }

    #[inline]
    pub fn namespace(&self) -> &str {
        &self.namespace
    }

    #[inline]
    pub fn send_goal<'a>(
        &'a self,
        goal: GoalBody<T>,
        on_transition: Option<OnTransition<T>>,
        on_feedback: Option<OnFeedback<T>>,
    ) -> AsyncClientGoalHandle<T> {
        self.manager
            .lock()
            .expect(MUTEX_LOCK_FAIL)
            .init_goal(goal, on_transition, on_feedback)
    }

    #[inline]
    pub fn build_goal_sender(&self, goal: GoalBody<T>) -> SendGoalBuilder<T> {
        SendGoalBuilder::new(self, goal)
    }

    #[inline]
    pub fn cancel_all_goals(&self) -> rosrust::error::Result<()> {
        self.cancel_goals_at_and_before_time(rosrust::Time::new())
    }

    #[inline]
    pub fn cancel_goals_at_and_before_time(
        &self,
        stamp: rosrust::Time,
    ) -> rosrust::error::Result<()> {
        self.pub_cancel.send(GoalID {
            id: String::new(),
            stamp,
        })
    }

    pub fn wait_for_server(&self, timeout: Option<rosrust::Duration>) -> bool {
        let timeout_time = timeout.map(|t| rosrust::now() + t);

        let rate = rosrust::rate(100.0);
        while rosrust::is_ok() {
            if let Some(timeout_time) = timeout_time {
                if timeout_time < rosrust::now() {
                    break;
                }
            }
            if self.wait_for_server_iteration() {
                return true;
            }
            rate.sleep();
        }

        false
    }

    fn wait_for_server_iteration(&self) -> bool {
        let last_caller_id = match self.last_caller_id.lock().expect(MUTEX_LOCK_FAIL).clone() {
            Some(caller_id) => caller_id,
            None => return false,
        };

        let is_in_goals = self
            .pub_goal
            .subscriber_names()
            .into_iter()
            .any(|caller_id| caller_id == last_caller_id);
        if !is_in_goals {
            return false;
        }

        let is_in_cancels = self
            .pub_cancel
            .subscriber_names()
            .into_iter()
            .any(|caller_id| caller_id == last_caller_id);
        if !is_in_cancels {
            return false;
        }

        self.status_sub.publisher_count() > 0
            && self.result_sub.publisher_count() > 0
            && self.feedback_sub.publisher_count() > 0
    }
}

fn decode_queue_size(param: &str, default: usize) -> usize {
    let param: Option<i32> = rosrust::param(param)
        .expect(UNEXPECTED_FAILED_NAME_RESOLVE)
        .get()
        .ok();
    match param {
        None => default,
        Some(v) if v < 0 => default,
        Some(v) => v as usize,
    }
}

pub struct SendGoalBuilder<'a, T: Action> {
    client: &'a ActionClient<T>,
    goal: GoalBody<T>,
    on_transition: Option<OnTransition<T>>,
    on_feedback: Option<OnFeedback<T>>,
}

impl<'a, T: Action> SendGoalBuilder<'a, T> {
    fn new(client: &'a ActionClient<T>, goal: GoalBody<T>) -> Self {
        Self {
            client,
            goal,
            on_transition: None,
            on_feedback: None,
        }
    }

    #[inline]
    pub fn on_transition<Fnew>(mut self, callback: Fnew) -> Self
    where
        Fnew: Fn(SyncClientGoalHandle<T>) + Send + 'static,
    {
        self.on_transition = Some(Box::new(callback));
        self
    }

    #[inline]
    pub fn on_feedback<Fnew>(mut self, callback: Fnew) -> Self
    where
        Fnew: Fn(SyncClientGoalHandle<T>, FeedbackBody<T>) + Send + 'static,
    {
        self.on_feedback = Some(Box::new(callback));
        self
    }

    #[inline]
    pub fn send(self) -> AsyncClientGoalHandle<T> {
        self.client
            .send_goal(self.goal, self.on_transition, self.on_feedback)
    }
}

type OnTransition<T> = Box<dyn Fn(SyncClientGoalHandle<T>) + Send + 'static>;
type OnFeedback<T> = Box<dyn Fn(SyncClientGoalHandle<T>, FeedbackBody<T>) + Send + 'static>;
<commit_msg>Extract most action client communication logic<commit_after>pub use self::client_goal_handle::{AsyncClientGoalHandle, SyncClientGoalHandle};
pub use self::comm_state_machine::State;
use crate::msg::actionlib_msgs;
use crate::static_messages::{MUTEX_LOCK_FAIL, UNEXPECTED_FAILED_NAME_RESOLVE};
use crate::{Action, ActionResponse, FeedbackBody, GoalBody, GoalID};
use rosrust::error::Result;
use std::sync::{Arc, Mutex};

mod client_goal_handle;
mod comm_state_machine;
mod goal_manager;

pub struct ActionClient<T: Action> {
    namespace: String,
    manager: Arc<Mutex<goal_manager::GoalManager<T>>>,
    communicator: Communicator<T>,
}

impl<T: Action> ActionClient<T> {
    pub fn new(namespace: &str) -> Result<Self> {
        let pub_queue_size = decode_queue_size("actionlib_client_pub_queue_size", 10);
        let sub_queue_size = decode_queue_size("actionlib_client_sub_queue_size", 0);

        let pub_goal = rosrust::publish(&format!("{}/goal", namespace), pub_queue_size)?;
        let pub_cancel = rosrust::publish(&format!("{}/cancel", namespace), pub_queue_size)?;

        let on_send_goal = {
            let publisher = pub_goal.clone();
            move |data| {
                if let Err(err) = publisher.send(data) {
                    rosrust::ros_err!("Failed to publish goal: {}", err);
                }
            }
        };

        let on_cancel = {
            let publisher = pub_cancel.clone();
            move |data: GoalID| {
                if let Err(err) = publisher.send(data) {
                    rosrust::ros_err!("Failed to publish cancel: {}", err);
                }
            }
        };

        let manager = Arc::new(Mutex::new(goal_manager::GoalManager::new(
            on_send_goal,
            on_cancel,
        )));

        let last_caller_id: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None));

        let on_status = {
            let manager = Arc::clone(&manager);
            let last_caller_id = Arc::clone(&last_caller_id);
            move |message: actionlib_msgs::GoalStatusArray, caller_id: &str| {
                (*last_caller_id.lock().expect(MUTEX_LOCK_FAIL)) = Some(caller_id.into());
                manager
                    .lock()
                    .expect(MUTEX_LOCK_FAIL)
                    .update_statuses(&message);
            }
        };
        let on_result = {
            let manager = Arc::clone(&manager);
            move |result: T::Result| {
                manager
                    .lock()
                    .expect(MUTEX_LOCK_FAIL)
                    .update_results(&result.into_response());
            }
        };
        let on_feedback = {
            let manager = Arc::clone(&manager);
            move |feedback: T::Feedback| {
                manager
                    .lock()
                    .expect(MUTEX_LOCK_FAIL)
                    .update_feedbacks(&feedback.into_response());
            }
        };

        let status_sub = rosrust::subscribe_with_ids(
            &format!("{}/status", namespace),
            sub_queue_size,
            on_status,
        )?;
        let result_sub =
            rosrust::subscribe(&format!("{}/result", namespace), sub_queue_size, on_result)?;
        let feedback_sub = rosrust::subscribe(
            &format!("{}/feedback", namespace),
            sub_queue_size,
            on_feedback,
        )?;

        let communicator = Communicator {
            last_caller_id,
            pub_goal,
            pub_cancel,
            status_sub,
            result_sub,
            feedback_sub,
        };

        Ok(Self {
            namespace: namespace.into(),
            manager,
            communicator,
        })
    }

    #[inline]
    pub fn namespace(&self) -> &str {
        &self.namespace
    }

    #[inline]
    pub fn send_goal(
        &self,
        goal: GoalBody<T>,
        on_transition: Option<OnTransition<T>>,
        on_feedback: Option<OnFeedback<T>>,
    ) -> AsyncClientGoalHandle<T> {
        self.manager
            .lock()
            .expect(MUTEX_LOCK_FAIL)
            .init_goal(goal, on_transition, on_feedback)
    }

    #[inline]
    pub fn build_goal_sender(&self, goal: GoalBody<T>) -> SendGoalBuilder<T> {
        SendGoalBuilder::new(self, goal)
    }

    #[inline]
    pub fn cancel_all_goals(&self) -> rosrust::error::Result<()> {
        self.cancel_goals_at_and_before_time(rosrust::Time::new())
    }

    #[inline]
    pub fn cancel_goals_at_and_before_time(
        &self,
        stamp: rosrust::Time,
    ) -> rosrust::error::Result<()> {
        self.communicator.pub_cancel.send(GoalID {
            id: String::new(),
            stamp,
        })
    }

    #[inline]
    pub fn wait_for_server(&self, timeout: Option<rosrust::Duration>) -> bool {
        self.communicator.wait_for_server(timeout)
    }
}

struct Communicator<T: Action> {
    last_caller_id: Arc<Mutex<Option<String>>>,
    pub_goal: rosrust::Publisher<T::Goal>,
    pub_cancel: rosrust::Publisher<GoalID>,
    status_sub: rosrust::Subscriber,
    result_sub: rosrust::Subscriber,
    feedback_sub: rosrust::Subscriber,
}

impl<T: Action> Communicator<T> {
    fn wait_for_server(&self, timeout: Option<rosrust::Duration>) -> bool {
        let timeout_time = timeout.map(|t| rosrust::now() + t);

        let rate = rosrust::rate(100.0);
        while rosrust::is_ok() {
            if let Some(timeout_time) = timeout_time {
                if timeout_time < rosrust::now() {
                    break;
                }
            }
            if self.wait_for_server_iteration() {
                return true;
            }
            rate.sleep();
        }

        false
    }

    fn wait_for_server_iteration(&self) -> bool {
        let last_caller_id = match self.last_caller_id.lock().expect(MUTEX_LOCK_FAIL).clone() {
            Some(caller_id) => caller_id,
            None => return false,
        };

        let is_in_goals = self
            .pub_goal
            .subscriber_names()
            .into_iter()
            .any(|caller_id| caller_id == last_caller_id);
        if !is_in_goals {
            return false;
        }

        let is_in_cancels = self
            .pub_cancel
            .subscriber_names()
            .into_iter()
            .any(|caller_id| caller_id == last_caller_id);
        if !is_in_cancels {
            return false;
        }

        self.status_sub.publisher_count() > 0
            && self.result_sub.publisher_count() > 0
            && self.feedback_sub.publisher_count() > 0
    }
}

fn decode_queue_size(param: &str, default: usize) -> usize {
    let param: Option<i32> = rosrust::param(param)
        .expect(UNEXPECTED_FAILED_NAME_RESOLVE)
        .get()
        .ok();
    match param {
        None => default,
        Some(v) if v < 0 => default,
        Some(v) => v as usize,
    }
}

pub struct SendGoalBuilder<'a, T: Action> {
    client: &'a ActionClient<T>,
    goal: GoalBody<T>,
    on_transition: Option<OnTransition<T>>,
    on_feedback: Option<OnFeedback<T>>,
}

impl<'a, T: Action> SendGoalBuilder<'a, T> {
    fn new(client: &'a ActionClient<T>, goal: GoalBody<T>) -> Self {
        Self {
            client,
            goal,
            on_transition: None,
            on_feedback: None,
        }
    }

    #[inline]
    pub fn on_transition<Fnew>(mut self, callback: Fnew) -> Self
    where
        Fnew: Fn(SyncClientGoalHandle<T>) + Send + 'static,
    {
        self.on_transition = Some(Box::new(callback));
        self
    }

    #[inline]
    pub fn on_feedback<Fnew>(mut self, callback: Fnew) -> Self
    where
        Fnew: Fn(SyncClientGoalHandle<T>, FeedbackBody<T>) + Send + 'static,
    {
        self.on_feedback = Some(Box::new(callback));
        self
    }

    #[inline]
    pub fn send(self) -> AsyncClientGoalHandle<T> {
        self.client
            .send_goal(self.goal, self.on_transition, self.on_feedback)
    }
}

type OnTransition<T> = Box<dyn Fn(SyncClientGoalHandle<T>) + Send + 'static>;
type OnFeedback<T> = Box<dyn Fn(SyncClientGoalHandle<T>, FeedbackBody<T>) + Send + 'static>;
<|endoftext|>