<commit_before>use std::cmp::{max, min};

use ra_db::{SyntaxDatabase, Cancelable};
use ra_syntax::{
    AstNode, SyntaxNode, TextUnit, TextRange,
    SyntaxKind::FN_DEF,
    ast::{self, ArgListOwner, DocCommentsOwner},
};
use ra_editor::find_node_at_offset;

use crate::{FilePosition, CallInfo, db::RootDatabase};

/// Computes parameter information for the given call expression.
pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Cancelable<Option<CallInfo>> {
    let file = db.source_file(position.file_id);
    let syntax = file.syntax();

    // Find the calling expression and it's NameRef
    let calling_node = ctry!(FnCallNode::with_node(syntax, position.offset));
    let name_ref = ctry!(calling_node.name_ref());

    // Resolve the function's NameRef (NOTE: this isn't entirely accurate).
    let file_symbols = db.index_resolve(name_ref)?;
    for symbol in file_symbols {
        if symbol.ptr.kind() == FN_DEF {
            let fn_file = db.source_file(symbol.file_id);
            let fn_def = symbol.ptr.resolve(&fn_file);
            let fn_def = ast::FnDef::cast(&fn_def).unwrap();
            if let Some(mut call_info) = CallInfo::new(fn_def) {
                // If we have a calling expression let's find which argument we are on
                let num_params = call_info.parameters.len();
                let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some();

                if num_params == 1 {
                    if !has_self {
                        call_info.active_parameter = Some(0);
                    }
                } else if num_params > 1 {
                    // Count how many parameters into the call we are.
                    // TODO: This is best effort for now and should be fixed at some point.
                    // It may be better to see where we are in the arg_list and then check
                    // where offset is in that list (or beyond).
                    // Revisit this after we get documentation comments in.
                    if let Some(ref arg_list) = calling_node.arg_list() {
                        let start = arg_list.syntax().range().start();

                        let range_search = TextRange::from_to(start, position.offset);
                        let mut commas: usize = arg_list
                            .syntax()
                            .text()
                            .slice(range_search)
                            .to_string()
                            .matches(',')
                            .count();

                        // If we have a method call eat the first param since it's just self.
                        if has_self {
                            commas += 1;
                        }

                        call_info.active_parameter = Some(commas);
                    }
                }

                return Ok(Some(call_info));
            }
        }
    }

    Ok(None)
}

enum FnCallNode<'a> {
    CallExpr(&'a ast::CallExpr),
    MethodCallExpr(&'a ast::MethodCallExpr),
}

impl<'a> FnCallNode<'a> {
    pub fn with_node(syntax: &'a SyntaxNode, offset: TextUnit) -> Option<FnCallNode<'a>> {
        if let Some(expr) = find_node_at_offset::<ast::CallExpr>(syntax, offset) {
            return Some(FnCallNode::CallExpr(expr));
        }
        if let Some(expr) = find_node_at_offset::<ast::MethodCallExpr>(syntax, offset) {
            return Some(FnCallNode::MethodCallExpr(expr));
        }
        None
    }

    pub fn name_ref(&self) -> Option<&'a ast::NameRef> {
        match *self {
            FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()?.kind() {
                ast::ExprKind::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?,
                _ => return None,
            }),

            FnCallNode::MethodCallExpr(call_expr) => call_expr
                .syntax()
                .children()
                .filter_map(ast::NameRef::cast)
                .nth(0),
        }
    }

    pub fn arg_list(&self) -> Option<&'a ast::ArgList> {
        match *self {
            FnCallNode::CallExpr(expr) => expr.arg_list(),
            FnCallNode::MethodCallExpr(expr) => expr.arg_list(),
        }
    }
}

impl CallInfo {
    fn new(node: &ast::FnDef) -> Option<Self> {
        let mut doc = None;

        // Strip the body out for the label.
        let mut label: String = if let Some(body) = node.body() {
            let body_range = body.syntax().range();
            let label: String = node
                .syntax()
                .children()
                .filter(|child| !child.range().is_subrange(&body_range))
                .map(|node| node.text().to_string())
                .collect();
            label
        } else {
            node.syntax().text().to_string()
        };

        if let Some((comment_range, docs)) = CallInfo::extract_doc_comments(node) {
            let comment_range = comment_range
                .checked_sub(node.syntax().range().start())
                .unwrap();
            let start = comment_range.start().to_usize();
            let end = comment_range.end().to_usize();

            // Remove the comment from the label
            label.replace_range(start..end, "");

            // Massage markdown
            let mut processed_lines = Vec::new();
            let mut in_code_block = false;
            for line in docs.lines() {
                if line.starts_with("```") {
                    in_code_block = !in_code_block;
                }

                let line = if in_code_block && line.starts_with("```") && !line.contains("rust") {
                    "```rust".into()
                } else {
                    line.to_string()
                };

                processed_lines.push(line);
            }

            if !processed_lines.is_empty() {
                doc = Some(processed_lines.join("\n"));
            }
        }

        Some(CallInfo {
            parameters: CallInfo::param_list(node),
            label: label.trim().to_owned(),
            doc,
            active_parameter: None,
        })
    }

    fn extract_doc_comments(node: &ast::FnDef) -> Option<(TextRange, String)> {
        if node.doc_comments().count() == 0 {
            return None;
        }

        let comment_text = node.doc_comment_text();

        let (begin, end) = node
            .doc_comments()
            .map(|comment| comment.syntax().range())
            .map(|range| (range.start().to_usize(), range.end().to_usize()))
            .fold((std::usize::MAX, std::usize::MIN), |acc, range| {
                (min(acc.0, range.0), max(acc.1, range.1))
            });

        let range = TextRange::from_to(TextUnit::from_usize(begin), TextUnit::from_usize(end));

        Some((range, comment_text))
    }

    fn param_list(node: &ast::FnDef) -> Vec<String> {
        let mut res = vec![];
        if let Some(param_list) = node.param_list() {
            if let Some(self_param) = param_list.self_param() {
                res.push(self_param.syntax().text().to_string())
            }

            // Maybe use param.pat here? See if we can just extract the name?
            //res.extend(param_list.params().map(|p| p.syntax().text().to_string()));
            res.extend(
                param_list
                    .params()
                    .filter_map(|p| p.pat())
                    .map(|pat| pat.syntax().text().to_string()),
            );
        }
        res
    }
}

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

    use crate::mock_analysis::single_file_with_position;

    fn call_info(text: &str) -> CallInfo {
        let (analysis, position) = single_file_with_position(text);
        analysis.call_info(position).unwrap().unwrap()
    }

    #[test]
    fn test_fn_signature_two_args_first() {
        let info = call_info(
            r#"fn foo(x: u32, y: u32) -> u32 {x + y}
fn bar() { foo(<|>3, ); }"#,
        );

        assert_eq!(info.parameters, vec!("x".to_string(), "y".to_string()));
        assert_eq!(info.active_parameter, Some(0));
    }

    #[test]
    fn test_fn_signature_two_args_second() {
        let info = call_info(
            r#"fn foo(x: u32, y: u32) -> u32 {x + y}
fn bar() { foo(3, <|>); }"#,
        );

        assert_eq!(info.parameters, vec!("x".to_string(), "y".to_string()));
        assert_eq!(info.active_parameter, Some(1));
    }

    #[test]
    fn test_fn_signature_for_impl() {
        let info = call_info(
            r#"struct F; impl F { pub fn new() { F{}} }
fn bar() {let _ : F = F::new(<|>);}"#,
        );

        assert_eq!(info.parameters, Vec::<String>::new());
        assert_eq!(info.active_parameter, None);
    }

    #[test]
    fn test_fn_signature_for_method_self() {
        let info = call_info(
            r#"struct F;
impl F {
    pub fn new() -> F{
        F{}
    }

    pub fn do_it(&self) {}
}

fn bar() {
    let f : F = F::new();
    f.do_it(<|>);
}"#,
        );

        assert_eq!(info.parameters, vec!["&self".to_string()]);
        assert_eq!(info.active_parameter, None);
    }

    #[test]
    fn test_fn_signature_for_method_with_arg() {
        let info = call_info(
            r#"struct F;
impl F {
    pub fn new() -> F{
        F{}
    }

    pub fn do_it(&self, x: i32) {}
}

fn bar() {
    let f : F = F::new();
    f.do_it(<|>);
}"#,
        );

        assert_eq!(info.parameters, vec!["&self".to_string(), "x".to_string()]);
        assert_eq!(info.active_parameter, Some(1));
    }

    #[test]
    fn test_fn_signature_with_docs_simple() {
        let info = call_info(
            r#"
/// test
// non-doc-comment
fn foo(j: u32) -> u32 {
    j
}

fn bar() {
    let _ = foo(<|>);
}
"#,
        );

        assert_eq!(info.parameters, vec!["j".to_string()]);
        assert_eq!(info.active_parameter, Some(0));
        assert_eq!(info.label, "fn foo(j: u32) -> u32".to_string());
        assert_eq!(info.doc, Some("test".into()));
    }

    #[test]
    fn test_fn_signature_with_docs() {
        let info = call_info(
            r#"
/// Adds one to the number given.
///
/// # Examples
///
/// ```
/// let five = 5;
///
/// assert_eq!(6, my_crate::add_one(5));
/// ```
pub fn add_one(x: i32) -> i32 {
    x + 1
}

pub fn do() {
    add_one(<|>
}"#,
        );

        assert_eq!(info.parameters, vec!["x".to_string()]);
        assert_eq!(info.active_parameter, Some(0));
        assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string());
        assert_eq!(
            info.doc,
            Some(
                r#"Adds one to the number given.

# Examples

```rust
let five = 5;

assert_eq!(6, my_crate::add_one(5));
```"#
                    .into()
            )
        );
    }

    #[test]
    fn test_fn_signature_with_docs_impl() {
        let info = call_info(
            r#"
struct addr;
impl addr {
    /// Adds one to the number given.
    ///
    /// # Examples
    ///
    /// ```
    /// let five = 5;
    ///
    /// assert_eq!(6, my_crate::add_one(5));
    /// ```
    pub fn add_one(x: i32) -> i32 {
        x + 1
    }
}

pub fn do_it() {
    addr {};
    addr::add_one(<|>);
}"#,
        );

        assert_eq!(info.parameters, vec!["x".to_string()]);
        assert_eq!(info.active_parameter, Some(0));
        assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string());
        assert_eq!(
            info.doc,
            Some(
                r#"Adds one to the number given.

# Examples

```rust
let five = 5;

assert_eq!(6, my_crate::add_one(5));
```"#
                    .into()
            )
        );
    }

    #[test]
    fn test_fn_signature_with_docs_from_actix() {
        let info = call_info(
            r#"
pub trait WriteHandler<E>
where
    Self: Actor,
    Self::Context: ActorContext,
{
    /// Method is called when writer emits error.
    ///
    /// If this method returns `ErrorAction::Continue` writer processing
    /// continues otherwise stream processing stops.
    fn error(&mut self, err: E, ctx: &mut Self::Context) -> Running {
        Running::Stop
    }

    /// Method is called when writer finishes.
    ///
    /// By default this method stops actor's `Context`.
    fn finished(&mut self, ctx: &mut Self::Context) {
        ctx.stop()
    }
}

pub fn foo() {
    WriteHandler r;
    r.finished(<|>);
}

"#,
        );

        assert_eq!(
            info.parameters,
            vec!["&mut self".to_string(), "ctx".to_string()]
        );
        assert_eq!(info.active_parameter, Some(1));
        assert_eq!(
            info.doc,
            Some(
                r#"Method is called when writer finishes.

By default this method stops actor's `Context`."#
                    .into()
            )
        );
    }

}
<commit_msg>simplify<commit_after>use std::cmp::{max, min};

use ra_db::{SyntaxDatabase, Cancelable};
use ra_syntax::{
    AstNode, SyntaxNode, TextUnit, TextRange,
    SyntaxKind::FN_DEF,
    ast::{self, ArgListOwner, DocCommentsOwner},
};
use ra_editor::find_node_at_offset;

use crate::{FilePosition, CallInfo, db::RootDatabase};

/// Computes parameter information for the given call expression.
pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Cancelable<Option<CallInfo>> {
    let file = db.source_file(position.file_id);
    let syntax = file.syntax();

    // Find the calling expression and it's NameRef
    let calling_node = ctry!(FnCallNode::with_node(syntax, position.offset));
    let name_ref = ctry!(calling_node.name_ref());

    // Resolve the function's NameRef (NOTE: this isn't entirely accurate).
    let file_symbols = db.index_resolve(name_ref)?;
    let symbol = ctry!(file_symbols.into_iter().find(|it| it.ptr.kind() == FN_DEF));
    let fn_file = db.source_file(symbol.file_id);
    let fn_def = symbol.ptr.resolve(&fn_file);
    let fn_def = ast::FnDef::cast(&fn_def).unwrap();
    let mut call_info = ctry!(CallInfo::new(fn_def));
    // If we have a calling expression let's find which argument we are on
    let num_params = call_info.parameters.len();
    let has_self = fn_def.param_list().and_then(|l| l.self_param()).is_some();

    if num_params == 1 {
        if !has_self {
            call_info.active_parameter = Some(0);
        }
    } else if num_params > 1 {
        // Count how many parameters into the call we are.
        // TODO: This is best effort for now and should be fixed at some point.
        // It may be better to see where we are in the arg_list and then check
        // where offset is in that list (or beyond).
        // Revisit this after we get documentation comments in.
        if let Some(ref arg_list) = calling_node.arg_list() {
            let start = arg_list.syntax().range().start();

            let range_search = TextRange::from_to(start, position.offset);
            let mut commas: usize = arg_list
                .syntax()
                .text()
                .slice(range_search)
                .to_string()
                .matches(',')
                .count();

            // If we have a method call eat the first param since it's just self.
            if has_self {
                commas += 1;
            }

            call_info.active_parameter = Some(commas);
        }
    }

    Ok(Some(call_info))
}

enum FnCallNode<'a> {
    CallExpr(&'a ast::CallExpr),
    MethodCallExpr(&'a ast::MethodCallExpr),
}

impl<'a> FnCallNode<'a> {
    pub fn with_node(syntax: &'a SyntaxNode, offset: TextUnit) -> Option<FnCallNode<'a>> {
        if let Some(expr) = find_node_at_offset::<ast::CallExpr>(syntax, offset) {
            return Some(FnCallNode::CallExpr(expr));
        }
        if let Some(expr) = find_node_at_offset::<ast::MethodCallExpr>(syntax, offset) {
            return Some(FnCallNode::MethodCallExpr(expr));
        }
        None
    }

    pub fn name_ref(&self) -> Option<&'a ast::NameRef> {
        match *self {
            FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()?.kind() {
                ast::ExprKind::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?,
                _ => return None,
            }),

            FnCallNode::MethodCallExpr(call_expr) => call_expr
                .syntax()
                .children()
                .filter_map(ast::NameRef::cast)
                .nth(0),
        }
    }

    pub fn arg_list(&self) -> Option<&'a ast::ArgList> {
        match *self {
            FnCallNode::CallExpr(expr) => expr.arg_list(),
            FnCallNode::MethodCallExpr(expr) => expr.arg_list(),
        }
    }
}

impl CallInfo {
    fn new(node: &ast::FnDef) -> Option<Self> {
        let mut doc = None;

        // Strip the body out for the label.
        let mut label: String = if let Some(body) = node.body() {
            let body_range = body.syntax().range();
            let label: String = node
                .syntax()
                .children()
                .filter(|child| !child.range().is_subrange(&body_range))
                .map(|node| node.text().to_string())
                .collect();
            label
        } else {
            node.syntax().text().to_string()
        };

        if let Some((comment_range, docs)) = CallInfo::extract_doc_comments(node) {
            let comment_range = comment_range
                .checked_sub(node.syntax().range().start())
                .unwrap();
            let start = comment_range.start().to_usize();
            let end = comment_range.end().to_usize();

            // Remove the comment from the label
            label.replace_range(start..end, "");

            // Massage markdown
            let mut processed_lines = Vec::new();
            let mut in_code_block = false;
            for line in docs.lines() {
                if line.starts_with("```") {
                    in_code_block = !in_code_block;
                }

                let line = if in_code_block && line.starts_with("```") && !line.contains("rust") {
                    "```rust".into()
                } else {
                    line.to_string()
                };

                processed_lines.push(line);
            }

            if !processed_lines.is_empty() {
                doc = Some(processed_lines.join("\n"));
            }
        }

        Some(CallInfo {
            parameters: CallInfo::param_list(node),
            label: label.trim().to_owned(),
            doc,
            active_parameter: None,
        })
    }

    fn extract_doc_comments(node: &ast::FnDef) -> Option<(TextRange, String)> {
        if node.doc_comments().count() == 0 {
            return None;
        }

        let comment_text = node.doc_comment_text();

        let (begin, end) = node
            .doc_comments()
            .map(|comment| comment.syntax().range())
            .map(|range| (range.start().to_usize(), range.end().to_usize()))
            .fold((std::usize::MAX, std::usize::MIN), |acc, range| {
                (min(acc.0, range.0), max(acc.1, range.1))
            });

        let range = TextRange::from_to(TextUnit::from_usize(begin), TextUnit::from_usize(end));

        Some((range, comment_text))
    }

    fn param_list(node: &ast::FnDef) -> Vec<String> {
        let mut res = vec![];
        if let Some(param_list) = node.param_list() {
            if let Some(self_param) = param_list.self_param() {
                res.push(self_param.syntax().text().to_string())
            }

            // Maybe use param.pat here? See if we can just extract the name?
            //res.extend(param_list.params().map(|p| p.syntax().text().to_string()));
            res.extend(
                param_list
                    .params()
                    .filter_map(|p| p.pat())
                    .map(|pat| pat.syntax().text().to_string()),
            );
        }
        res
    }
}

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

    use crate::mock_analysis::single_file_with_position;

    fn call_info(text: &str) -> CallInfo {
        let (analysis, position) = single_file_with_position(text);
        analysis.call_info(position).unwrap().unwrap()
    }

    #[test]
    fn test_fn_signature_two_args_first() {
        let info = call_info(
            r#"fn foo(x: u32, y: u32) -> u32 {x + y}
fn bar() { foo(<|>3, ); }"#,
        );

        assert_eq!(info.parameters, vec!("x".to_string(), "y".to_string()));
        assert_eq!(info.active_parameter, Some(0));
    }

    #[test]
    fn test_fn_signature_two_args_second() {
        let info = call_info(
            r#"fn foo(x: u32, y: u32) -> u32 {x + y}
fn bar() { foo(3, <|>); }"#,
        );

        assert_eq!(info.parameters, vec!("x".to_string(), "y".to_string()));
        assert_eq!(info.active_parameter, Some(1));
    }

    #[test]
    fn test_fn_signature_for_impl() {
        let info = call_info(
            r#"struct F; impl F { pub fn new() { F{}} }
fn bar() {let _ : F = F::new(<|>);}"#,
        );

        assert_eq!(info.parameters, Vec::<String>::new());
        assert_eq!(info.active_parameter, None);
    }

    #[test]
    fn test_fn_signature_for_method_self() {
        let info = call_info(
            r#"struct F;
impl F {
    pub fn new() -> F{
        F{}
    }

    pub fn do_it(&self) {}
}

fn bar() {
    let f : F = F::new();
    f.do_it(<|>);
}"#,
        );

        assert_eq!(info.parameters, vec!["&self".to_string()]);
        assert_eq!(info.active_parameter, None);
    }

    #[test]
    fn test_fn_signature_for_method_with_arg() {
        let info = call_info(
            r#"struct F;
impl F {
    pub fn new() -> F{
        F{}
    }

    pub fn do_it(&self, x: i32) {}
}

fn bar() {
    let f : F = F::new();
    f.do_it(<|>);
}"#,
        );

        assert_eq!(info.parameters, vec!["&self".to_string(), "x".to_string()]);
        assert_eq!(info.active_parameter, Some(1));
    }

    #[test]
    fn test_fn_signature_with_docs_simple() {
        let info = call_info(
            r#"
/// test
// non-doc-comment
fn foo(j: u32) -> u32 {
    j
}

fn bar() {
    let _ = foo(<|>);
}
"#,
        );

        assert_eq!(info.parameters, vec!["j".to_string()]);
        assert_eq!(info.active_parameter, Some(0));
        assert_eq!(info.label, "fn foo(j: u32) -> u32".to_string());
        assert_eq!(info.doc, Some("test".into()));
    }

    #[test]
    fn test_fn_signature_with_docs() {
        let info = call_info(
            r#"
/// Adds one to the number given.
///
/// # Examples
///
/// ```
/// let five = 5;
///
/// assert_eq!(6, my_crate::add_one(5));
/// ```
pub fn add_one(x: i32) -> i32 {
    x + 1
}

pub fn do() {
    add_one(<|>
}"#,
        );

        assert_eq!(info.parameters, vec!["x".to_string()]);
        assert_eq!(info.active_parameter, Some(0));
        assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string());
        assert_eq!(
            info.doc,
            Some(
                r#"Adds one to the number given.

# Examples

```rust
let five = 5;

assert_eq!(6, my_crate::add_one(5));
```"#
                    .into()
            )
        );
    }

    #[test]
    fn test_fn_signature_with_docs_impl() {
        let info = call_info(
            r#"
struct addr;
impl addr {
    /// Adds one to the number given.
    ///
    /// # Examples
    ///
    /// ```
    /// let five = 5;
    ///
    /// assert_eq!(6, my_crate::add_one(5));
    /// ```
    pub fn add_one(x: i32) -> i32 {
        x + 1
    }
}

pub fn do_it() {
    addr {};
    addr::add_one(<|>);
}"#,
        );

        assert_eq!(info.parameters, vec!["x".to_string()]);
        assert_eq!(info.active_parameter, Some(0));
        assert_eq!(info.label, "pub fn add_one(x: i32) -> i32".to_string());
        assert_eq!(
            info.doc,
            Some(
                r#"Adds one to the number given.

# Examples

```rust
let five = 5;

assert_eq!(6, my_crate::add_one(5));
```"#
                    .into()
            )
        );
    }

    #[test]
    fn test_fn_signature_with_docs_from_actix() {
        let info = call_info(
            r#"
pub trait WriteHandler<E>
where
    Self: Actor,
    Self::Context: ActorContext,
{
    /// Method is called when writer emits error.
    ///
    /// If this method returns `ErrorAction::Continue` writer processing
    /// continues otherwise stream processing stops.
    fn error(&mut self, err: E, ctx: &mut Self::Context) -> Running {
        Running::Stop
    }

    /// Method is called when writer finishes.
    ///
    /// By default this method stops actor's `Context`.
    fn finished(&mut self, ctx: &mut Self::Context) {
        ctx.stop()
    }
}

pub fn foo() {
    WriteHandler r;
    r.finished(<|>);
}

"#,
        );

        assert_eq!(
            info.parameters,
            vec!["&mut self".to_string(), "ctx".to_string()]
        );
        assert_eq!(info.active_parameter, Some(1));
        assert_eq!(
            info.doc,
            Some(
                r#"Method is called when writer finishes.

By default this method stops actor's `Context`."#
                    .into()
            )
        );
    }

}
<|endoftext|>


<commit_before>use engine::{
    components::{CollisionHandle, DataStoreComponent, Pos, Shape},
    resources::SkyCollisionWorld,
};
use specs::prelude::*;

use ncollide::math::Point as P2;
use std::collections::HashMap;

#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct UnitType {
    pub tag: String,
    pub max_hp: f64,
    pub movable: bool,
    pub shape: Shape,
    pub kill_reward: f64,
    pub death_penalty: f64,
    pub damage_deal_reward: Option<f64>,
    pub damage_recv_penalty: Option<f64>,
    pub speed: f64,
    pub attack_range: f64,
    pub attack_damage: f64,
    pub attack_delay: f64,

    pub death_type: String,
    pub dmg_recv_type: String,
    pub dmg_deal_type: String,
    pub kill_type: String,
}

impl Default for UnitType {
    fn default() -> Self {
        UnitType {
            tag: "".to_string(),
            max_hp: 100.0,
            movable: true,
            shape: Shape::Triangle { base_len: 10.0 },
            kill_reward: 0.0,
            death_penalty: 0.0,
            damage_deal_reward: None,
            damage_recv_penalty: None,
            death_type: "death".to_string(),
            dmg_recv_type: "dmg_recvd".to_string(),
            dmg_deal_type: "dmg_dealt".to_string(),
            kill_type: "killed_enemy".to_string(),
            speed: 20.0,
            attack_range: 10.0,
            attack_delay: 1.0,
            attack_damage: 10.0,
        }
    }
}

impl UnitType {
    pub fn build_entity(
        &self,
        world: &mut World,
        pos: Pos,
        curr_hp: Option<f64>,
        faction: usize,
    ) -> Entity {
        use engine::{
            components::{sensor, FactionId, Hp, Movable, SensorType, Sensors, Speed, UnitTypeTag},
            resources::PLAYER_COLORS,
        };
        use specs::saveload::U64Marker;

        let color = PLAYER_COLORS[faction];

        // Scoping for borrow shenanigans
        let entity = {
            let entity = world
                .create_entity()
                .with(pos)
                .with(self.shape)
                .with(color)
                .with(FactionId(faction))
                .with(UnitTypeTag(self.tag.clone()))
                .with(DataStoreComponent::default())
                .with(Hp {
                    max_hp: self.max_hp,
                    curr_hp: curr_hp.unwrap_or(self.max_hp),
                })
                .marked::<U64Marker>();

            if self.movable {
                entity.with(Movable(0)).with(Speed(self.speed))
            } else {
                entity
            }
        }
        .build();

        let me = {
            let mut col_storage = world.write::<CollisionHandle>();
            let mut c_world = world.write_resource();

            self.register_collision(entity, pos, faction, &mut col_storage, &mut *c_world)
        };

        let atk_radius = self.shape.compute_extended_radius(self.attack_range);
        let atk_sensor = sensor::build_attack_sensor(world, me, atk_radius);
        world
            .write::<Sensors>()
            .entry(me)
            .unwrap()
            .or_insert(Default::default())
            .0
            .insert(SensorType::Attack, atk_sensor);

        me
    }

    /// Registers a unit with the collision system based on its unit type
    /// and places the collision system handles into the appropriate components
    pub fn register_collision(
        &self,
        entity: Entity,
        pos: Pos,
        faction: usize,
        col_storage: &mut WriteStorage<CollisionHandle>,
        c_world: &mut SkyCollisionWorld,
    ) -> Entity {
        use engine::{
            components::Shape,
            resources::{ColliderData, COLLISION_SCALE},
        };
        use nalgebra;
        use nalgebra::{Isometry2, Vector2};
        use ncollide::{
            shape::{Ball, Cuboid, Polyline, ShapeHandle, Triangle},
            transformation::ToPolyline,
            world::{CollisionGroups, GeometricQueryType},
        };

        let mut collider_group = CollisionGroups::new();
        collider_group.set_membership(&[faction]);

        let collider = match self.shape {
            Shape::Rect { width, height } => {
                let width = width / COLLISION_SCALE;
                let height = height / COLLISION_SCALE;

                // ncollide likes half widths and heights, so divide by 2
                let collider = Cuboid::new(Vector2::new(width / 2.0, height / 2.0));
                let collider = ShapeHandle::new(collider);

                collider
            }
            Shape::Triangle { base_len } => {
                let base_len = base_len / COLLISION_SCALE;

                // equilateral triangle dimensions
                let half_height = base_len / (2.0 as f64).sqrt() / 2.0;
                let half_base_len = base_len / 2.0;

                let p1 = P2::new(-half_height, -half_base_len);
                let p2 = P2::new(-half_height, half_base_len);
                let p3 = P2::new(half_height, 0.0);

                let collider = Triangle::new(p1, p2, p3).to_polyline(()).unwrap();
                let collider = Polyline::new(collider.0, None);
                let collider = ShapeHandle::new(collider);

                collider
            }
            Shape::Circle { radius } => ShapeHandle::new(Ball::new(radius / COLLISION_SCALE)),
        };

        // We need the entity ID for this, so do it after building the entity and then add the component.
        let pos = Isometry2::new(
            Vector2::new(pos.x / COLLISION_SCALE, pos.y / COLLISION_SCALE),
            nalgebra::zero(),
        );

        let q_type = GeometricQueryType::Contacts(0.0, 0.0);
        let collider = c_world.add(
            pos,
            collider,
            collider_group,
            q_type,
            ColliderData {
                e: entity,
                owner: None,
                sensor: false,
            },
        );

        col_storage.insert(entity, CollisionHandle(collider.handle()));

        entity
    }
}

#[derive(Clone, Debug, Serialize, Deserialize, Default)]
pub struct UnitTypeMap {
    pub typ_ids: HashMap<String, usize>,
    pub tag_map: HashMap<String, UnitType>,
}
<commit_msg>Fix collision bug from ncollide update<commit_after>use engine::{
    components::{CollisionHandle, DataStoreComponent, Pos, Shape},
    resources::SkyCollisionWorld,
};
use specs::prelude::*;

use ncollide::math::Point as P2;
use std::collections::HashMap;

#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
pub struct UnitType {
    pub tag: String,
    pub max_hp: f64,
    pub movable: bool,
    pub shape: Shape,
    pub kill_reward: f64,
    pub death_penalty: f64,
    pub damage_deal_reward: Option<f64>,
    pub damage_recv_penalty: Option<f64>,
    pub speed: f64,
    pub attack_range: f64,
    pub attack_damage: f64,
    pub attack_delay: f64,

    pub death_type: String,
    pub dmg_recv_type: String,
    pub dmg_deal_type: String,
    pub kill_type: String,
}

impl Default for UnitType {
    fn default() -> Self {
        UnitType {
            tag: "".to_string(),
            max_hp: 100.0,
            movable: true,
            shape: Shape::Triangle { base_len: 10.0 },
            kill_reward: 0.0,
            death_penalty: 0.0,
            damage_deal_reward: None,
            damage_recv_penalty: None,
            death_type: "death".to_string(),
            dmg_recv_type: "dmg_recvd".to_string(),
            dmg_deal_type: "dmg_dealt".to_string(),
            kill_type: "killed_enemy".to_string(),
            speed: 20.0,
            attack_range: 10.0,
            attack_delay: 1.0,
            attack_damage: 10.0,
        }
    }
}

impl UnitType {
    pub fn build_entity(
        &self,
        world: &mut World,
        pos: Pos,
        curr_hp: Option<f64>,
        faction: usize,
    ) -> Entity {
        use engine::{
            components::{sensor, FactionId, Hp, Movable, SensorType, Sensors, Speed, UnitTypeTag},
            resources::PLAYER_COLORS,
        };
        use specs::saveload::U64Marker;

        let color = PLAYER_COLORS[faction];

        // Scoping for borrow shenanigans
        let entity = {
            let entity = world
                .create_entity()
                .with(pos)
                .with(self.shape)
                .with(color)
                .with(FactionId(faction))
                .with(UnitTypeTag(self.tag.clone()))
                .with(DataStoreComponent::default())
                .with(Hp {
                    max_hp: self.max_hp,
                    curr_hp: curr_hp.unwrap_or(self.max_hp),
                })
                .marked::<U64Marker>();

            if self.movable {
                entity.with(Movable(0)).with(Speed(self.speed))
            } else {
                entity
            }
        }
        .build();

        let me = {
            let mut col_storage = world.write::<CollisionHandle>();
            let mut c_world = world.write_resource();

            self.register_collision(entity, pos, faction, &mut col_storage, &mut *c_world)
        };

        let atk_radius = self.shape.compute_extended_radius(self.attack_range);
        let atk_sensor = sensor::build_attack_sensor(world, me, atk_radius);
        world
            .write::<Sensors>()
            .entry(me)
            .unwrap()
            .or_insert(Default::default())
            .0
            .insert(SensorType::Attack, atk_sensor);

        me
    }

    /// Registers a unit with the collision system based on its unit type
    /// and places the collision system handles into the appropriate components
    pub fn register_collision(
        &self,
        entity: Entity,
        pos: Pos,
        faction: usize,
        col_storage: &mut WriteStorage<CollisionHandle>,
        c_world: &mut SkyCollisionWorld,
    ) -> Entity {
        use engine::{
            components::Shape,
            resources::{ColliderData, COLLISION_SCALE},
        };
        use nalgebra;
        use nalgebra::{Isometry2, Vector2};
        use ncollide::{
            shape::{Ball, Cuboid, Polyline, ShapeHandle, ConvexPolygon},
            world::{CollisionGroups, GeometricQueryType},
        };

        let mut collider_group = CollisionGroups::new();
        collider_group.set_membership(&[faction]);

        let collider = match self.shape {
            Shape::Rect { width, height } => {
                let width = width / COLLISION_SCALE;
                let height = height / COLLISION_SCALE;

                // ncollide likes half widths and heights, so divide by 2
                let collider = Cuboid::new(Vector2::new(width / 2.0, height / 2.0));
                let collider = ShapeHandle::new(collider);

                collider
            }
            Shape::Triangle { base_len } => {
                let base_len = base_len / COLLISION_SCALE;

                // equilateral triangle dimensions
                let half_height = base_len / (2.0 as f64).sqrt() / 2.0;
                let half_base_len = base_len / 2.0;

                let p1 = P2::new(-half_height, -half_base_len);
                let p2 = P2::new(-half_height, half_base_len);
                let p3 = P2::new(half_height, 0.0);

                let collider = ConvexPolygon::try_new(vec![p1, p2, p3]).unwrap();
                let collider = ShapeHandle::new(collider);

                collider
            }
            Shape::Circle { radius } => ShapeHandle::new(Ball::new(radius / COLLISION_SCALE)),
        };

        // We need the entity ID for this, so do it after building the entity and then add the component.
        let pos = Isometry2::new(
            Vector2::new(pos.x / COLLISION_SCALE, pos.y / COLLISION_SCALE),
            nalgebra::zero(),
        );

        let q_type = GeometricQueryType::Contacts(0.0, 0.0);
        let collider = c_world.add(
            pos,
            collider,
            collider_group,
            q_type,
            ColliderData {
                e: entity,
                owner: None,
                sensor: false,
            },
        );

        col_storage.insert(entity, CollisionHandle(collider.handle()));

        entity
    }
}

#[derive(Clone, Debug, Serialize, Deserialize, Default)]
pub struct UnitTypeMap {
    pub typ_ids: HashMap<String, usize>,
    pub tag_map: HashMap<String, UnitType>,
}
<|endoftext|>


<commit_before>#![feature(unboxed_closures, if_let, slicing_syntax)]
#![allow(dead_code)]

extern crate hyper;
extern crate multipart;

use self::hyper::server::{Listening, Server, Request, Response};
use self::hyper::client::Request as ClientReq;
use self::hyper::{status, Url};

use self::multipart::server::Multipart;

use self::multipart::client::Multipart as ClientMulti;

use std::io::net::ip::Ipv4Addr;

use std::rand::random;

fn hello(req: Request, mut res: Response) {
    print_req(&req);

    let mut multipart = Multipart::from_request(req).ok().expect("Could not create multipart!");

    multipart.foreach_entry(|&: name, content| println!("Name: {} Content: {}", name, content));

    *res.status_mut() = status::Ok;

    res.start().unwrap().end().unwrap();
}

thread_local!(static PORT: u16 = random())

fn server() -> Listening {
    let server = PORT.with(|port| Server::http(Ipv4Addr(127, 0, 0, 1), *port));
    server.listen(hello).unwrap()
}

fn print_req(req: &Request) {
    println!("Request: \nRemote addr: {}\nMethod: {}\nHeaders: {}\nURI: {}",
        req.remote_addr, req.method, req.headers, req.uri);
}

#[test]
fn client_api_test() {
    let mut server = server();

    let address = PORT.with(|port| format!("http://localhost:{}/", port));

    let request = ClientReq::post(Url::parse(&*address).unwrap()).unwrap();

    let mut multipart = ClientMulti::new();

    multipart.add_text("hello", "world");
    multipart.add_text("goodnight", "sun");
    multipart.sized = true;

    multipart.send(request).unwrap();

    server.close().unwrap();
}

<commit_msg>Remove echoes from server<commit_after>#![feature(unboxed_closures, if_let, slicing_syntax)]
#![allow(dead_code)]

extern crate hyper;
extern crate multipart;

use self::hyper::server::{Listening, Server, Request, Response};
use self::hyper::client::Request as ClientReq;
use self::hyper::{status, Url};

use self::multipart::server::Multipart;

use self::multipart::client::Multipart as ClientMulti;

use std::io::net::ip::Ipv4Addr;

use std::rand::random;

fn ok_serv(req: Request, mut res: Response) {
    let mut multipart = Multipart::from_request(req).ok().expect("Could not create multipart!");

    multipart.foreach_entry(|&: _, _| ());

    *res.status_mut() = status::Ok;

    res.start().unwrap().end().unwrap();
}

thread_local!(static PORT: u16 = random())

fn server() -> Listening {
    let server = PORT.with(|port| Server::http(Ipv4Addr(127, 0, 0, 1), *port));
    server.listen(ok_serv).unwrap()
}

#[test]
fn client_api_test() {
    let mut server = server();

    let address = PORT.with(|port| format!("http://localhost:{}/", port));

    let request = ClientReq::post(Url::parse(&*address).unwrap()).unwrap();

    let mut multipart = ClientMulti::new();

    multipart.add_text("hello", "world");
    multipart.add_text("goodnight", "sun");
    multipart.sized = true;

    multipart.send(request).unwrap();

    server.close().unwrap();
}

<|endoftext|>


<commit_before>use std::f64;

/// Used to build a constraint, specifying additional details.
#[derive(Default)]
pub struct ConstraintBuilder {
    width: f64,
    height: f64,
    min_width: f64,
    min_height: f64,
    max_width: f64,
    max_height: f64,
}

/// Used to build a constraint, specifying additional details.
impl ConstraintBuilder {
    /// Creates a new `ConstraintBuilder` with default values.
    pub fn new() -> Self {
        ConstraintBuilder::default()
    }

    /// Inserts a new width.
    pub fn width(mut self, width: impl Into<f64>) -> Self {
        self.width = width.into();
        self
    }

    /// Inserts a new height.
    pub fn height(mut self, height: impl Into<f64>) -> Self {
        self.height = height.into();
        self
    }

    /// Inserts a new size.
    pub fn size(mut self, width: impl Into<f64>, height: impl Into<f64>) -> Self {
        self.width = width.into();
        self.height = height.into();
        self
    }

    /// Inserts a new min_width.
    pub fn min_width(mut self, min_width: impl Into<f64>) -> Self {
        self.min_width = min_width.into();
        self
    }

    /// Inserts a new min_height.
    pub fn min_height(mut self, min_height: impl Into<f64>) -> Self {
        self.min_height = min_height.into();
        self
    }

    /// Inserts a new min_size.
    pub fn min_size(mut self, min_width: impl Into<f64>, min_height: impl Into<f64>) -> Self {
        self.min_width = min_width.into();
        self.min_height = min_height.into();
        self
    }

    /// Inserts a new max_width.
    pub fn max_width(mut self, max_width: impl Into<f64>) -> Self {
        self.max_width = max_width.into();
        self
    }

    /// Inserts a new max_height.
    pub fn max_height(mut self, max_height: impl Into<f64>) -> Self {
        self.max_height = max_height.into();
        self
    }

    /// Inserts a new min_size.
    pub fn max_size(mut self, max_width: impl Into<f64>, max_height: impl Into<f64>) -> Self {
        self.max_width = max_width.into();
        self.max_height = max_height.into();
        self
    }

    /// Builds the constraint.
    pub fn build(self) -> Constraint {
        Constraint {
            width: self.width,
            height: self.height,
            min_width: self.min_width,
            min_height: self.min_height,
            max_width: self.max_width,
            max_height: self.max_height,
        }
    }
}

/// `Constraint` describes a box constraint.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Constraint {
    width: f64,
    height: f64,
    min_width: f64,
    min_height: f64,
    max_width: f64,
    max_height: f64,
}

impl Default for Constraint {
    fn default() -> Self {
        Constraint {
            width: 0.0,
            height: 0.0,
            min_width: 0.0,
            min_height: 0.0,
            max_width: f64::MAX,
            max_height: f64::MAX,
        }
    }
}

impl Constraint {
    /// Returns a constraint builder.
    #[inline]
    pub fn new() -> ConstraintBuilder {
        ConstraintBuilder::new()
    }

    /// Returns a constraint builder.
    #[inline(always)]
    #[deprecated = "Use new instead"]
    pub fn create() -> ConstraintBuilder {
        Constraint::new()
    }

    /// Gets width.
    pub fn width(&self) -> f64 {
        self.width
    }

    /// Sets width.
    pub fn set_width(&mut self, width: f64) {
        self.width = width;

        // adjust min and max
        if self.min_width > width {
            self.min_width = width;
        }

        if self.max_width < width {
            self.max_width = width;
        }
    }

    /// Gets height.
    pub fn height(&self) -> f64 {
        self.height
    }

    /// Sets height.
    pub fn set_height(&mut self, height: f64) {
        self.height = height;

        // adjust min and max
        if self.min_height > height {
            self.min_height = height;
        }

        if self.max_height < height {
            self.max_height = height;
        }
    }

    /// Gets the size.
    pub fn size(&self) -> (f64, f64) {
        (self.width, self.height)
    }

    /// Sets the size.
    pub fn set_size(&mut self, width: f64, height: f64) {
        self.set_width(width);
        self.set_height(height);
    }

    /// Gets min_width.
    pub fn min_width(&self) -> f64 {
        self.min_width
    }

    /// Sets min_width and set width to 0.0.
    pub fn set_min_width(&mut self, min_width: f64) {
        self.min_width = min_width;

        self.width = 0.;
    }

    /// Gets min_height.
    pub fn min_height(&self) -> f64 {
        self.min_height
    }

    /// Sets min_height and set height to min_height if height < min_height.
    pub fn set_min_height(&mut self, min_height: f64) {
        self.min_height = min_height;

        self.height = 0.;
    }

    /// Gets the min_size.
    pub fn min_size(&self) -> (f64, f64) {
        (self.min_width, self.min_height)
    }

    /// Sets the min size.
    pub fn set_min_size(&mut self, min_width: f64, min_height: f64) {
        self.set_min_width(min_width);
        self.set_min_height(min_height);
    }

    /// Gets max_width.
    pub fn max_width(&self) -> f64 {
        self.max_width
    }

    /// Sets max_width and set width to 0.0.
    pub fn set_max_width(&mut self, max_width: f64) {
        self.max_width = max_width;

        self.width = 0.;
    }

    /// Gets max_height.
    pub fn max_height(&self) -> f64 {
        self.max_height
    }

    /// Sets max_height and set height to 0.0.
    pub fn set_max_height(&mut self, max_height: f64) {
        self.max_height = max_height;

        self.height = 0.;
    }

    /// Gets the max_size.
    pub fn max_size(&self) -> (f64, f64) {
        (self.max_width, self.max_height)
    }

    /// Sets the max size.
    pub fn set_max_size(&mut self, max_width: f64, max_height: f64) {
        self.set_max_width(max_width);
        self.set_max_height(max_height);
    }

    /// Adjust the given `size` to match the constraint.
    pub fn perform(&self, size: (f64, f64)) -> (f64, f64) {
        let size = {
            let width = if self.width > 0.0 { self.width } else { size.0 };
            let height = if self.height > 0.0 {
                self.height
            } else {
                size.1
            };

            (width, height)
        };

        (
            constrain(size.0, self.min_width, self.max_width, self.width),
            constrain(size.1, self.min_height, self.max_height, self.height),
        )
    }
}

// Check constraint for the given
fn constrain(val: f64, min: f64, max: f64, size: f64) -> f64 {
    if min == 0.0 && max == 0.0 && size > 0.0 {
        size
    } else if val < min && min > 0.0 {
        min
    } else if val > max && max > 0.0 {
        max
    } else {
        val
    }
}

impl From<ConstraintBuilder> for Constraint {
    fn from(builder: ConstraintBuilder) -> Self {
        builder.build()
    }
}

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

    #[test]
    fn test_builder_width() {
        let width = 12.0;

        let constraint = Constraint::new().width(width).build();

        assert_eq!(constraint.width(), width);
    }

    #[test]
    fn test_builder_height() {
        let height = 12.0;

        let constraint = Constraint::new().height(height).build();

        assert_eq!(constraint.height(), height);
    }

    #[test]
    fn test_builder_min_width() {
        let width = 12.0;

        let constraint = Constraint::new().min_width(width).build();

        assert_eq!(constraint.min_width(), width);
    }

    #[test]
    fn test_builder_min_height() {
        let height = 12.0;

        let constraint = Constraint::new().min_height(height).build();

        assert_eq!(constraint.min_height(), height);
    }

    #[test]
    fn test_builder_max_width() {
        let width = 12.0;

        let constraint = Constraint::new().max_width(width).build();

        assert_eq!(constraint.max_width(), width);
    }

    #[test]
    fn test_builder_max_height() {
        let height = 12.0;

        let constraint = Constraint::new().max_height(height).build();

        assert_eq!(constraint.max_height(), height);
    }

    #[test]
    fn test_set_width() {
        let width = 12.0;

        let mut constraint = Constraint::default();
        constraint.set_width(width);
        assert_eq!(constraint.width(), width);
    }

    #[test]
    fn test_set_height() {
        let height = 12.0;

        let mut constraint = Constraint::default();
        constraint.set_height(height);

        assert_eq!(constraint.height(), height);
    }

    #[test]
    fn test_set_size() {
        let width = 12.0;
        let height = 14.0;

        let mut constraint = Constraint::default();
        constraint.set_size(width, height);

        assert_eq!(constraint.size(), (width, height));
    }

    #[test]
    fn test_set_min_width() {
        let min_width = 12.0;

        let mut constraint = Constraint::default();
        constraint.set_min_width(min_width);

        assert_eq!(constraint.min_width(), min_width);
    }

    #[test]
    fn test_set_min_height() {
        let min_height = 12.0;

        let mut constraint = Constraint::default();
        constraint.set_min_height(min_height);

        assert_eq!(constraint.min_height(), min_height);
    }

    #[test]
    fn test_set_min_size() {
        let min_width = 12.0;
        let min_height = 14.0;

        let mut constraint = Constraint::default();
        constraint.set_min_size(min_width, min_height);

        assert_eq!(constraint.min_size(), (min_width, min_height));
    }

    #[test]
    fn test_set_max_width() {
        let max_width = 12.0;

        let mut constraint = Constraint::default();
        constraint.set_max_width(max_width);

        assert_eq!(constraint.max_width(), max_width);
    }

    #[test]
    fn test_set_max_height() {
        let max_height = 12.0;

        let mut constraint = Constraint::default();
        constraint.set_max_height(max_height);

        assert_eq!(constraint.max_height(), max_height);
    }

    #[test]
    fn test_set_max_size() {
        let max_width = 12.0;
        let max_height = 14.0;

        let mut constraint = Constraint::default();
        constraint.set_max_size(max_width, max_height);

        assert_eq!(constraint.max_size(), (max_width, max_height));
    }

    #[test]
    fn test_perform() {
        let mut constraint = Constraint::default();

        constraint.set_min_width(10.0);
        constraint.set_min_height(10.0);
        constraint.set_max_width(50.0);
        constraint.set_max_height(60.0);
        constraint.set_width(0.0);
        constraint.set_height(0.0);

        assert_eq!(constraint.perform((10.0, 59.0)), (10.0, 59.0));
        assert_eq!(constraint.perform((5.0, 40.0)), (10.0, 40.0));
        assert_eq!(constraint.perform((10.0, 70.0)), (10.0, 60.0));
    }
}
<commit_msg>Fix test.<commit_after>use std::f64;

/// Used to build a constraint, specifying additional details.
#[derive(Default)]
pub struct ConstraintBuilder {
    width: f64,
    height: f64,
    min_width: f64,
    min_height: f64,
    max_width: f64,
    max_height: f64,
}

/// Used to build a constraint, specifying additional details.
impl ConstraintBuilder {
    /// Creates a new `ConstraintBuilder` with default values.
    pub fn new() -> Self {
        ConstraintBuilder::default()
    }

    /// Inserts a new width.
    pub fn width(mut self, width: impl Into<f64>) -> Self {
        self.width = width.into();
        self
    }

    /// Inserts a new height.
    pub fn height(mut self, height: impl Into<f64>) -> Self {
        self.height = height.into();
        self
    }

    /// Inserts a new size.
    pub fn size(mut self, width: impl Into<f64>, height: impl Into<f64>) -> Self {
        self.width = width.into();
        self.height = height.into();
        self
    }

    /// Inserts a new min_width.
    pub fn min_width(mut self, min_width: impl Into<f64>) -> Self {
        self.min_width = min_width.into();
        self
    }

    /// Inserts a new min_height.
    pub fn min_height(mut self, min_height: impl Into<f64>) -> Self {
        self.min_height = min_height.into();
        self
    }

    /// Inserts a new min_size.
    pub fn min_size(mut self, min_width: impl Into<f64>, min_height: impl Into<f64>) -> Self {
        self.min_width = min_width.into();
        self.min_height = min_height.into();
        self
    }

    /// Inserts a new max_width.
    pub fn max_width(mut self, max_width: impl Into<f64>) -> Self {
        self.max_width = max_width.into();
        self
    }

    /// Inserts a new max_height.
    pub fn max_height(mut self, max_height: impl Into<f64>) -> Self {
        self.max_height = max_height.into();
        self
    }

    /// Inserts a new min_size.
    pub fn max_size(mut self, max_width: impl Into<f64>, max_height: impl Into<f64>) -> Self {
        self.max_width = max_width.into();
        self.max_height = max_height.into();
        self
    }

    /// Builds the constraint.
    pub fn build(self) -> Constraint {
        Constraint {
            width: self.width,
            height: self.height,
            min_width: self.min_width,
            min_height: self.min_height,
            max_width: self.max_width,
            max_height: self.max_height,
        }
    }
}

/// `Constraint` describes a box constraint.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Constraint {
    width: f64,
    height: f64,
    min_width: f64,
    min_height: f64,
    max_width: f64,
    max_height: f64,
}

impl Default for Constraint {
    fn default() -> Self {
        Constraint {
            width: 0.0,
            height: 0.0,
            min_width: 0.0,
            min_height: 0.0,
            max_width: f64::MAX,
            max_height: f64::MAX,
        }
    }
}

impl Constraint {
    /// Returns a constraint builder.
    #[inline]
    pub fn new() -> ConstraintBuilder {
        ConstraintBuilder::new()
    }

    /// Returns a constraint builder.
    #[inline(always)]
    #[deprecated = "Use new instead"]
    pub fn create() -> ConstraintBuilder {
        Constraint::new()
    }

    /// Gets width.
    pub fn width(&self) -> f64 {
        self.width
    }

    /// Sets width.
    pub fn set_width(&mut self, width: f64) {
        self.width = width;

        // adjust min and max
        if self.min_width > width {
            self.min_width = width;
        }

        if self.max_width < width {
            self.max_width = width;
        }
    }

    /// Gets height.
    pub fn height(&self) -> f64 {
        self.height
    }

    /// Sets height.
    pub fn set_height(&mut self, height: f64) {
        self.height = height;

        // adjust min and max
        if self.min_height > height {
            self.min_height = height;
        }

        if self.max_height < height {
            self.max_height = height;
        }
    }

    /// Gets the size.
    pub fn size(&self) -> (f64, f64) {
        (self.width, self.height)
    }

    /// Sets the size.
    pub fn set_size(&mut self, width: f64, height: f64) {
        self.set_width(width);
        self.set_height(height);
    }

    /// Gets min_width.
    pub fn min_width(&self) -> f64 {
        self.min_width
    }

    /// Sets min_width and set width to 0.0.
    pub fn set_min_width(&mut self, min_width: f64) {
        self.min_width = min_width;

        self.width = 0.;
    }

    /// Gets min_height.
    pub fn min_height(&self) -> f64 {
        self.min_height
    }

    /// Sets min_height and set height to min_height if height < min_height.
    pub fn set_min_height(&mut self, min_height: f64) {
        self.min_height = min_height;

        self.height = 0.;
    }

    /// Gets the min_size.
    pub fn min_size(&self) -> (f64, f64) {
        (self.min_width, self.min_height)
    }

    /// Sets the min size.
    pub fn set_min_size(&mut self, min_width: f64, min_height: f64) {
        self.set_min_width(min_width);
        self.set_min_height(min_height);
    }

    /// Gets max_width.
    pub fn max_width(&self) -> f64 {
        self.max_width
    }

    /// Sets max_width and set width to 0.0.
    pub fn set_max_width(&mut self, max_width: f64) {
        self.max_width = max_width;

        self.width = 0.;
    }

    /// Gets max_height.
    pub fn max_height(&self) -> f64 {
        self.max_height
    }

    /// Sets max_height and set height to 0.0.
    pub fn set_max_height(&mut self, max_height: f64) {
        self.max_height = max_height;

        self.height = 0.;
    }

    /// Gets the max_size.
    pub fn max_size(&self) -> (f64, f64) {
        (self.max_width, self.max_height)
    }

    /// Sets the max size.
    pub fn set_max_size(&mut self, max_width: f64, max_height: f64) {
        self.set_max_width(max_width);
        self.set_max_height(max_height);
    }

    /// Adjust the given `size` to match the constraint.
    pub fn perform(&self, size: (f64, f64)) -> (f64, f64) {
        let size = {
            let width = if self.width > 0.0 { self.width } else { size.0 };
            let height = if self.height > 0.0 {
                self.height
            } else {
                size.1
            };

            (width, height)
        };

        (
            constrain(size.0, self.min_width, self.max_width, self.width),
            constrain(size.1, self.min_height, self.max_height, self.height),
        )
    }
}

// Check constraint for the given
fn constrain(val: f64, min: f64, max: f64, size: f64) -> f64 {
    if min == 0.0 && max == 0.0 && size > 0.0 {
        size
    } else if val < min && min > 0.0 {
        min
    } else if val > max && max > 0.0 {
        max
    } else {
        val
    }
}

impl From<ConstraintBuilder> for Constraint {
    fn from(builder: ConstraintBuilder) -> Self {
        builder.build()
    }
}

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

    #[test]
    fn test_builder_width() {
        let width = 12.0;

        let constraint = Constraint::new().width(width).build();

        assert_eq!(constraint.width(), width);
    }

    #[test]
    fn test_builder_height() {
        let height = 12.0;

        let constraint = Constraint::new().height(height).build();

        assert_eq!(constraint.height(), height);
    }

    #[test]
    fn test_builder_min_width() {
        let width = 12.0;

        let constraint = Constraint::new().min_width(width).build();

        assert_eq!(constraint.min_width(), width);
    }

    #[test]
    fn test_builder_min_height() {
        let height = 12.0;

        let constraint = Constraint::new().min_height(height).build();

        assert_eq!(constraint.min_height(), height);
    }

    #[test]
    fn test_builder_max_width() {
        let width = 12.0;

        let constraint = Constraint::new().max_width(width).build();

        assert_eq!(constraint.max_width(), width);
    }

    #[test]
    fn test_builder_max_height() {
        let height = 12.0;

        let constraint = Constraint::new().max_height(height).build();

        assert_eq!(constraint.max_height(), height);
    }

    #[test]
    fn test_set_width() {
        let width = 12.0;

        let mut constraint = Constraint::default();
        constraint.set_width(width);
        assert_eq!(constraint.width(), width);
    }

    #[test]
    fn test_set_height() {
        let height = 12.0;

        let mut constraint = Constraint::default();
        constraint.set_height(height);

        assert_eq!(constraint.height(), height);
    }

    #[test]
    fn test_set_size() {
        let width = 12.0;
        let height = 14.0;

        let mut constraint = Constraint::default();
        constraint.set_size(width, height);

        assert_eq!(constraint.size(), (width, height));
    }

    #[test]
    fn test_set_min_width() {
        let min_width = 12.0;

        let mut constraint = Constraint::default();
        constraint.set_min_width(min_width);

        assert_eq!(constraint.min_width(), min_width);
    }

    #[test]
    fn test_set_min_height() {
        let min_height = 12.0;

        let mut constraint = Constraint::default();
        constraint.set_min_height(min_height);

        assert_eq!(constraint.min_height(), min_height);
    }

    #[test]
    fn test_set_min_size() {
        let min_width = 12.0;
        let min_height = 14.0;

        let mut constraint = Constraint::default();
        constraint.set_min_size(min_width, min_height);

        assert_eq!(constraint.min_size(), (min_width, min_height));
    }

    #[test]
    fn test_set_max_width() {
        let max_width = 12.0;

        let mut constraint = Constraint::default();
        constraint.set_max_width(max_width);

        assert_eq!(constraint.max_width(), max_width);
    }

    #[test]
    fn test_set_max_height() {
        let max_height = 12.0;

        let mut constraint = Constraint::default();
        constraint.set_max_height(max_height);

        assert_eq!(constraint.max_height(), max_height);
    }

    #[test]
    fn test_set_max_size() {
        let max_width = 12.0;
        let max_height = 14.0;

        let mut constraint = Constraint::default();
        constraint.set_max_size(max_width, max_height);

        assert_eq!(constraint.max_size(), (max_width, max_height));
    }

    #[test]
    fn test_perform() {
        let mut constraint = Constraint::default();

        constraint.set_min_width(10.0);
        constraint.set_min_height(10.0);
        constraint.set_max_width(50.0);
        constraint.set_max_height(60.0);

        assert_eq!(constraint.perform((10.0, 59.0)), (10.0, 59.0));
        assert_eq!(constraint.perform((5.0, 40.0)), (10.0, 40.0));
        assert_eq!(constraint.perform((10.0, 70.0)), (10.0, 60.0));
    }
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here

use std::{
    ffi::OsStr,
    ops,
    path::{Path, PathBuf},
    process::Command,
};

use anyhow::{Context, Result};
use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId};
use paths::{AbsPath, AbsPathBuf};
use ra_arena::{Arena, Idx};
use ra_db::Edition;
use rustc_hash::FxHashMap;

use crate::cfg_flag::CfgFlag;

/// `CargoWorkspace` represents the logical structure of, well, a Cargo
/// workspace. It pretty closely mirrors `cargo metadata` output.
///
/// Note that internally, rust analyzer uses a different structure:
/// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates,
/// while this knows about `Packages` & `Targets`: purely cargo-related
/// concepts.
///
/// We use absolute paths here, `cargo metadata` guarantees to always produce
/// abs paths.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct CargoWorkspace {
    packages: Arena<PackageData>,
    targets: Arena<TargetData>,
    workspace_root: AbsPathBuf,
}

impl ops::Index<Package> for CargoWorkspace {
    type Output = PackageData;
    fn index(&self, index: Package) -> &PackageData {
        &self.packages[index]
    }
}

impl ops::Index<Target> for CargoWorkspace {
    type Output = TargetData;
    fn index(&self, index: Target) -> &TargetData {
        &self.targets[index]
    }
}

#[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct CargoConfig {
    /// Do not activate the `default` feature.
    pub no_default_features: bool,

    /// Activate all available features
    pub all_features: bool,

    /// List of features to activate.
    /// This will be ignored if `cargo_all_features` is true.
    pub features: Vec<String>,

    /// Runs cargo check on launch to figure out the correct values of OUT_DIR
    pub load_out_dirs_from_check: bool,

    /// rustc target
    pub target: Option<String>,
}

pub type Package = Idx<PackageData>;

pub type Target = Idx<TargetData>;

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct PackageData {
    pub version: String,
    pub name: String,
    pub manifest: AbsPathBuf,
    pub targets: Vec<Target>,
    pub is_member: bool,
    pub dependencies: Vec<PackageDependency>,
    pub edition: Edition,
    pub features: Vec<String>,
    pub cfgs: Vec<CfgFlag>,
    pub out_dir: Option<AbsPathBuf>,
    pub proc_macro_dylib_path: Option<AbsPathBuf>,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct PackageDependency {
    pub pkg: Package,
    pub name: String,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct TargetData {
    pub package: Package,
    pub name: String,
    pub root: AbsPathBuf,
    pub kind: TargetKind,
    pub is_proc_macro: bool,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TargetKind {
    Bin,
    /// Any kind of Cargo lib crate-type (dylib, rlib, proc-macro, ...).
    Lib,
    Example,
    Test,
    Bench,
    Other,
}

impl TargetKind {
    fn new(kinds: &[String]) -> TargetKind {
        for kind in kinds {
            return match kind.as_str() {
                "bin" => TargetKind::Bin,
                "test" => TargetKind::Test,
                "bench" => TargetKind::Bench,
                "example" => TargetKind::Example,
                "proc-macro" => TargetKind::Lib,
                _ if kind.contains("lib") => TargetKind::Lib,
                _ => continue,
            };
        }
        TargetKind::Other
    }
}

impl PackageData {
    pub fn root(&self) -> &AbsPath {
        self.manifest.parent().unwrap()
    }
}

impl CargoWorkspace {
    pub fn from_cargo_metadata(
        cargo_toml: &AbsPath,
        cargo_features: &CargoConfig,
    ) -> Result<CargoWorkspace> {
        let mut meta = MetadataCommand::new();
        meta.cargo_path(ra_toolchain::cargo());
        meta.manifest_path(cargo_toml.to_path_buf());
        if cargo_features.all_features {
            meta.features(CargoOpt::AllFeatures);
        } else if cargo_features.no_default_features {
            // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
            // https://github.com/oli-obk/cargo_metadata/issues/79
            meta.features(CargoOpt::NoDefaultFeatures);
        } else if !cargo_features.features.is_empty() {
            meta.features(CargoOpt::SomeFeatures(cargo_features.features.clone()));
        }
        if let Some(parent) = cargo_toml.parent() {
            meta.current_dir(parent.to_path_buf());
        }
        if let Some(target) = cargo_features.target.as_ref() {
            meta.other_options(vec![String::from("--filter-platform"), target.clone()]);
        }
        let mut meta = meta.exec().with_context(|| {
            format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display())
        })?;

        let mut out_dir_by_id = FxHashMap::default();
        let mut cfgs = FxHashMap::default();
        let mut proc_macro_dylib_paths = FxHashMap::default();
        if cargo_features.load_out_dirs_from_check {
            let resources = load_extern_resources(cargo_toml, cargo_features)?;
            out_dir_by_id = resources.out_dirs;
            cfgs = resources.cfgs;
            proc_macro_dylib_paths = resources.proc_dylib_paths;
        }

        let mut pkg_by_id = FxHashMap::default();
        let mut packages = Arena::default();
        let mut targets = Arena::default();

        let ws_members = &meta.workspace_members;

        meta.packages.sort_by(|a, b| a.id.cmp(&b.id));
        for meta_pkg in meta.packages {
            let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } =
                meta_pkg;
            let is_member = ws_members.contains(&id);
            let edition = edition
                .parse::<Edition>()
                .with_context(|| format!("Failed to parse edition {}", edition))?;
            let pkg = packages.alloc(PackageData {
                name,
                version: version.to_string(),
                manifest: AbsPathBuf::assert(manifest_path),
                targets: Vec::new(),
                is_member,
                edition,
                dependencies: Vec::new(),
                features: Vec::new(),
                cfgs: cfgs.get(&id).cloned().unwrap_or_default(),
                out_dir: out_dir_by_id.get(&id).cloned(),
                proc_macro_dylib_path: proc_macro_dylib_paths.get(&id).cloned(),
            });
            let pkg_data = &mut packages[pkg];
            pkg_by_id.insert(id, pkg);
            for meta_tgt in meta_pkg.targets {
                let is_proc_macro = meta_tgt.kind.as_slice() == ["proc-macro"];
                let tgt = targets.alloc(TargetData {
                    package: pkg,
                    name: meta_tgt.name,
                    root: AbsPathBuf::assert(meta_tgt.src_path.clone()),
                    kind: TargetKind::new(meta_tgt.kind.as_slice()),
                    is_proc_macro,
                });
                pkg_data.targets.push(tgt);
            }
        }
        let resolve = meta.resolve.expect("metadata executed with deps");
        for mut node in resolve.nodes {
            let source = match pkg_by_id.get(&node.id) {
                Some(&src) => src,
                // FIXME: replace this and a similar branch below with `.unwrap`, once
                // https://github.com/rust-lang/cargo/issues/7841
                // is fixed and hits stable (around 1.43-is probably?).
                None => {
                    log::error!("Node id do not match in cargo metadata, ignoring {}", node.id);
                    continue;
                }
            };
            node.deps.sort_by(|a, b| a.pkg.cmp(&b.pkg));
            for dep_node in node.deps {
                let pkg = match pkg_by_id.get(&dep_node.pkg) {
                    Some(&pkg) => pkg,
                    None => {
                        log::error!(
                            "Dep node id do not match in cargo metadata, ignoring {}",
                            dep_node.pkg
                        );
                        continue;
                    }
                };
                let dep = PackageDependency { name: dep_node.name, pkg };
                packages[source].dependencies.push(dep);
            }
            packages[source].features.extend(node.features);
        }

        let workspace_root = AbsPathBuf::assert(meta.workspace_root);
        Ok(CargoWorkspace { packages, targets, workspace_root: workspace_root })
    }

    pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + ExactSizeIterator + 'a {
        self.packages.iter().map(|(id, _pkg)| id)
    }

    pub fn target_by_root(&self, root: &AbsPath) -> Option<Target> {
        self.packages()
            .filter_map(|pkg| self[pkg].targets.iter().find(|&&it| &self[it].root == root))
            .next()
            .copied()
    }

    pub fn workspace_root(&self) -> &AbsPath {
        &self.workspace_root
    }

    pub fn package_flag(&self, package: &PackageData) -> String {
        if self.is_unique(&*package.name) {
            package.name.clone()
        } else {
            format!("{}:{}", package.name, package.version)
        }
    }

    fn is_unique(&self, name: &str) -> bool {
        self.packages.iter().filter(|(_, v)| v.name == name).count() == 1
    }
}

#[derive(Debug, Clone, Default)]
pub struct ExternResources {
    out_dirs: FxHashMap<PackageId, AbsPathBuf>,
    proc_dylib_paths: FxHashMap<PackageId, AbsPathBuf>,
    cfgs: FxHashMap<PackageId, Vec<CfgFlag>>,
}

pub fn load_extern_resources(
    cargo_toml: &Path,
    cargo_features: &CargoConfig,
) -> Result<ExternResources> {
    let mut cmd = Command::new(ra_toolchain::cargo());
    cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml);
    if cargo_features.all_features {
        cmd.arg("--all-features");
    } else if cargo_features.no_default_features {
        // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
        // https://github.com/oli-obk/cargo_metadata/issues/79
        cmd.arg("--no-default-features");
    } else {
        cmd.args(&cargo_features.features);
    }

    let output = cmd.output()?;

    let mut res = ExternResources::default();

    for message in cargo_metadata::Message::parse_stream(output.stdout.as_slice()) {
        if let Ok(message) = message {
            match message {
                Message::BuildScriptExecuted(BuildScript { package_id, out_dir, cfgs, .. }) => {
                    let cfgs = {
                        let mut acc = Vec::new();
                        for cfg in cfgs {
                            match cfg.parse::<CfgFlag>() {
                                Ok(it) => acc.push(it),
                                Err(err) => {
                                    anyhow::bail!("invalid cfg from cargo-metadata: {}", err)
                                }
                            };
                        }
                        acc
                    };
                    // cargo_metadata crate returns default (empty) path for
                    // older cargos, which is not absolute, so work around that.
                    if out_dir != PathBuf::default() {
                        let out_dir = AbsPathBuf::assert(out_dir);
                        res.out_dirs.insert(package_id.clone(), out_dir);
                        res.cfgs.insert(package_id, cfgs);
                    }
                }
                Message::CompilerArtifact(message) => {
                    if message.target.kind.contains(&"proc-macro".to_string()) {
                        let package_id = message.package_id;
                        // Skip rmeta file
                        if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name))
                        {
                            let filename = AbsPathBuf::assert(filename.clone());
                            res.proc_dylib_paths.insert(package_id, filename);
                        }
                    }
                }
                Message::CompilerMessage(_) => (),
                Message::Unknown => (),
                Message::BuildFinished(_) => {}
                Message::TextLine(_) => {}
            }
        }
    }
    Ok(res)
}

// FIXME: File a better way to know if it is a dylib
fn is_dylib(path: &Path) -> bool {
    match path.extension().and_then(OsStr::to_str).map(|it| it.to_string().to_lowercase()) {
        None => false,
        Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"),
    }
}
<commit_msg>ra_project_model: Fix configuration of features<commit_after>//! FIXME: write short doc here

use std::{
    ffi::OsStr,
    ops,
    path::{Path, PathBuf},
    process::Command,
};

use anyhow::{Context, Result};
use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId};
use paths::{AbsPath, AbsPathBuf};
use ra_arena::{Arena, Idx};
use ra_db::Edition;
use rustc_hash::FxHashMap;

use crate::cfg_flag::CfgFlag;

/// `CargoWorkspace` represents the logical structure of, well, a Cargo
/// workspace. It pretty closely mirrors `cargo metadata` output.
///
/// Note that internally, rust analyzer uses a different structure:
/// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates,
/// while this knows about `Packages` & `Targets`: purely cargo-related
/// concepts.
///
/// We use absolute paths here, `cargo metadata` guarantees to always produce
/// abs paths.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct CargoWorkspace {
    packages: Arena<PackageData>,
    targets: Arena<TargetData>,
    workspace_root: AbsPathBuf,
}

impl ops::Index<Package> for CargoWorkspace {
    type Output = PackageData;
    fn index(&self, index: Package) -> &PackageData {
        &self.packages[index]
    }
}

impl ops::Index<Target> for CargoWorkspace {
    type Output = TargetData;
    fn index(&self, index: Target) -> &TargetData {
        &self.targets[index]
    }
}

#[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct CargoConfig {
    /// Do not activate the `default` feature.
    pub no_default_features: bool,

    /// Activate all available features
    pub all_features: bool,

    /// List of features to activate.
    /// This will be ignored if `cargo_all_features` is true.
    pub features: Vec<String>,

    /// Runs cargo check on launch to figure out the correct values of OUT_DIR
    pub load_out_dirs_from_check: bool,

    /// rustc target
    pub target: Option<String>,
}

pub type Package = Idx<PackageData>;

pub type Target = Idx<TargetData>;

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct PackageData {
    pub version: String,
    pub name: String,
    pub manifest: AbsPathBuf,
    pub targets: Vec<Target>,
    pub is_member: bool,
    pub dependencies: Vec<PackageDependency>,
    pub edition: Edition,
    pub features: Vec<String>,
    pub cfgs: Vec<CfgFlag>,
    pub out_dir: Option<AbsPathBuf>,
    pub proc_macro_dylib_path: Option<AbsPathBuf>,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct PackageDependency {
    pub pkg: Package,
    pub name: String,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct TargetData {
    pub package: Package,
    pub name: String,
    pub root: AbsPathBuf,
    pub kind: TargetKind,
    pub is_proc_macro: bool,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TargetKind {
    Bin,
    /// Any kind of Cargo lib crate-type (dylib, rlib, proc-macro, ...).
    Lib,
    Example,
    Test,
    Bench,
    Other,
}

impl TargetKind {
    fn new(kinds: &[String]) -> TargetKind {
        for kind in kinds {
            return match kind.as_str() {
                "bin" => TargetKind::Bin,
                "test" => TargetKind::Test,
                "bench" => TargetKind::Bench,
                "example" => TargetKind::Example,
                "proc-macro" => TargetKind::Lib,
                _ if kind.contains("lib") => TargetKind::Lib,
                _ => continue,
            };
        }
        TargetKind::Other
    }
}

impl PackageData {
    pub fn root(&self) -> &AbsPath {
        self.manifest.parent().unwrap()
    }
}

impl CargoWorkspace {
    pub fn from_cargo_metadata(
        cargo_toml: &AbsPath,
        cargo_features: &CargoConfig,
    ) -> Result<CargoWorkspace> {
        let mut meta = MetadataCommand::new();
        meta.cargo_path(ra_toolchain::cargo());
        meta.manifest_path(cargo_toml.to_path_buf());
        if cargo_features.all_features {
            meta.features(CargoOpt::AllFeatures);
        } else {
            if cargo_features.no_default_features {
                // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
                // https://github.com/oli-obk/cargo_metadata/issues/79
                meta.features(CargoOpt::NoDefaultFeatures);
            }
            if !cargo_features.features.is_empty() {
                meta.features(CargoOpt::SomeFeatures(cargo_features.features.clone()));
            }
        }
        if let Some(parent) = cargo_toml.parent() {
            meta.current_dir(parent.to_path_buf());
        }
        if let Some(target) = cargo_features.target.as_ref() {
            meta.other_options(vec![String::from("--filter-platform"), target.clone()]);
        }
        let mut meta = meta.exec().with_context(|| {
            format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display())
        })?;

        let mut out_dir_by_id = FxHashMap::default();
        let mut cfgs = FxHashMap::default();
        let mut proc_macro_dylib_paths = FxHashMap::default();
        if cargo_features.load_out_dirs_from_check {
            let resources = load_extern_resources(cargo_toml, cargo_features)?;
            out_dir_by_id = resources.out_dirs;
            cfgs = resources.cfgs;
            proc_macro_dylib_paths = resources.proc_dylib_paths;
        }

        let mut pkg_by_id = FxHashMap::default();
        let mut packages = Arena::default();
        let mut targets = Arena::default();

        let ws_members = &meta.workspace_members;

        meta.packages.sort_by(|a, b| a.id.cmp(&b.id));
        for meta_pkg in meta.packages {
            let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } =
                meta_pkg;
            let is_member = ws_members.contains(&id);
            let edition = edition
                .parse::<Edition>()
                .with_context(|| format!("Failed to parse edition {}", edition))?;
            let pkg = packages.alloc(PackageData {
                name,
                version: version.to_string(),
                manifest: AbsPathBuf::assert(manifest_path),
                targets: Vec::new(),
                is_member,
                edition,
                dependencies: Vec::new(),
                features: Vec::new(),
                cfgs: cfgs.get(&id).cloned().unwrap_or_default(),
                out_dir: out_dir_by_id.get(&id).cloned(),
                proc_macro_dylib_path: proc_macro_dylib_paths.get(&id).cloned(),
            });
            let pkg_data = &mut packages[pkg];
            pkg_by_id.insert(id, pkg);
            for meta_tgt in meta_pkg.targets {
                let is_proc_macro = meta_tgt.kind.as_slice() == ["proc-macro"];
                let tgt = targets.alloc(TargetData {
                    package: pkg,
                    name: meta_tgt.name,
                    root: AbsPathBuf::assert(meta_tgt.src_path.clone()),
                    kind: TargetKind::new(meta_tgt.kind.as_slice()),
                    is_proc_macro,
                });
                pkg_data.targets.push(tgt);
            }
        }
        let resolve = meta.resolve.expect("metadata executed with deps");
        for mut node in resolve.nodes {
            let source = match pkg_by_id.get(&node.id) {
                Some(&src) => src,
                // FIXME: replace this and a similar branch below with `.unwrap`, once
                // https://github.com/rust-lang/cargo/issues/7841
                // is fixed and hits stable (around 1.43-is probably?).
                None => {
                    log::error!("Node id do not match in cargo metadata, ignoring {}", node.id);
                    continue;
                }
            };
            node.deps.sort_by(|a, b| a.pkg.cmp(&b.pkg));
            for dep_node in node.deps {
                let pkg = match pkg_by_id.get(&dep_node.pkg) {
                    Some(&pkg) => pkg,
                    None => {
                        log::error!(
                            "Dep node id do not match in cargo metadata, ignoring {}",
                            dep_node.pkg
                        );
                        continue;
                    }
                };
                let dep = PackageDependency { name: dep_node.name, pkg };
                packages[source].dependencies.push(dep);
            }
            packages[source].features.extend(node.features);
        }

        let workspace_root = AbsPathBuf::assert(meta.workspace_root);
        Ok(CargoWorkspace { packages, targets, workspace_root: workspace_root })
    }

    pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + ExactSizeIterator + 'a {
        self.packages.iter().map(|(id, _pkg)| id)
    }

    pub fn target_by_root(&self, root: &AbsPath) -> Option<Target> {
        self.packages()
            .filter_map(|pkg| self[pkg].targets.iter().find(|&&it| &self[it].root == root))
            .next()
            .copied()
    }

    pub fn workspace_root(&self) -> &AbsPath {
        &self.workspace_root
    }

    pub fn package_flag(&self, package: &PackageData) -> String {
        if self.is_unique(&*package.name) {
            package.name.clone()
        } else {
            format!("{}:{}", package.name, package.version)
        }
    }

    fn is_unique(&self, name: &str) -> bool {
        self.packages.iter().filter(|(_, v)| v.name == name).count() == 1
    }
}

#[derive(Debug, Clone, Default)]
pub struct ExternResources {
    out_dirs: FxHashMap<PackageId, AbsPathBuf>,
    proc_dylib_paths: FxHashMap<PackageId, AbsPathBuf>,
    cfgs: FxHashMap<PackageId, Vec<CfgFlag>>,
}

pub fn load_extern_resources(
    cargo_toml: &Path,
    cargo_features: &CargoConfig,
) -> Result<ExternResources> {
    let mut cmd = Command::new(ra_toolchain::cargo());
    cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml);
    if cargo_features.all_features {
        cmd.arg("--all-features");
    } else {
        if cargo_features.no_default_features {
            // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
            // https://github.com/oli-obk/cargo_metadata/issues/79
            cmd.arg("--no-default-features");
        }
        if !cargo_features.features.is_empty() {
            cmd.arg("--features");
            cmd.arg(cargo_features.features.join(" "));
        }
    }

    let output = cmd.output()?;

    let mut res = ExternResources::default();

    for message in cargo_metadata::Message::parse_stream(output.stdout.as_slice()) {
        if let Ok(message) = message {
            match message {
                Message::BuildScriptExecuted(BuildScript { package_id, out_dir, cfgs, .. }) => {
                    let cfgs = {
                        let mut acc = Vec::new();
                        for cfg in cfgs {
                            match cfg.parse::<CfgFlag>() {
                                Ok(it) => acc.push(it),
                                Err(err) => {
                                    anyhow::bail!("invalid cfg from cargo-metadata: {}", err)
                                }
                            };
                        }
                        acc
                    };
                    // cargo_metadata crate returns default (empty) path for
                    // older cargos, which is not absolute, so work around that.
                    if out_dir != PathBuf::default() {
                        let out_dir = AbsPathBuf::assert(out_dir);
                        res.out_dirs.insert(package_id.clone(), out_dir);
                        res.cfgs.insert(package_id, cfgs);
                    }
                }
                Message::CompilerArtifact(message) => {
                    if message.target.kind.contains(&"proc-macro".to_string()) {
                        let package_id = message.package_id;
                        // Skip rmeta file
                        if let Some(filename) = message.filenames.iter().find(|name| is_dylib(name))
                        {
                            let filename = AbsPathBuf::assert(filename.clone());
                            res.proc_dylib_paths.insert(package_id, filename);
                        }
                    }
                }
                Message::CompilerMessage(_) => (),
                Message::Unknown => (),
                Message::BuildFinished(_) => {}
                Message::TextLine(_) => {}
            }
        }
    }
    Ok(res)
}

// FIXME: File a better way to know if it is a dylib
fn is_dylib(path: &Path) -> bool {
    match path.extension().and_then(OsStr::to_str).map(|it| it.to_string().to_lowercase()) {
        None => false,
        Some(ext) => matches!(ext.as_str(), "dll" | "dylib" | "so"),
    }
}
<|endoftext|>


<commit_before>use query_builder::AsQuery;
use query_source::Table;

/// The `for_update` method
///
/// This trait should not be relied on directly by most apps. Its behavior is
/// provided by [`QueryDsl`]. However, you may need a where clause on this trait
/// to call `for_update` from generic code.
///
/// [`QueryDsl`]: ../trait.QueryDsl.html
pub trait ForUpdateDsl {
    /// The type returned by `for_update`. See [`dsl::ForUpdate`] for
    /// convenient access to this type.
    ///
    /// [`dsl::ForUpdate`]: ../../dsl/type.ForUpdate.html
    type Output;

    /// See the trait level documentation
    fn for_update(self) -> Self::Output;
}

impl<T> ForUpdateDsl for T
    where
        T: Table + AsQuery,
        T::Query: ForUpdateDsl,
{
    type Output = <T::Query as ForUpdateDsl>::Output;

    fn for_update(self) -> Self::Output {
        self.as_query().for_update()
    }
}

/// The `for_no_key_update` method
///
/// This trait should not be relied on directly by most apps. Its behavior is
/// provided by [`QueryDsl`]. However, you may need a where clause on this trait
/// to call `for_no_key_update` from generic code.
///
/// [`QueryDsl`]: ../trait.QueryDsl.html

pub trait ForNoKeyUpdateDsl {
    /// The type returned by `for_no_key_update`. See [`dsl::ForNoKeyUpdate`] for
    /// convenient access to this type.
    ///
    /// [`dsl::ForNoKeyUpdate`]: ../../dsl/type.ForNoKeyUpdate.html
    type Output;

    /// See the trait level documentation
    fn for_no_key_update(self) -> Self::Output;
}

impl<T> ForNoKeyUpdateDsl for T
    where
        T: Table + AsQuery,
        T::Query: ForNoKeyUpdateDsl,
{
    type Output = <T::Query as ForNoKeyUpdateDsl>::Output;

    fn for_no_key_update(self) -> Self::Output {
        self.as_query().for_no_key_update()
    }
}

/// The `for_share` method
///
/// This trait should not be relied on directly by most apps. Its behavior is
/// provided by [`QueryDsl`]. However, you may need a where clause on this trait
/// to call `for_share` from generic code.
///
/// [`QueryDsl`]: ../trait.QueryDsl.html
pub trait ForShareDsl {
    /// The type returned by `for_share`. See [`dsl::ForShare`] for
    /// convenient access to this type.
    ///
    /// [`dsl::ForShare`]: ../../dsl/type.ForShare.html
    type Output;

    /// See the trait level documentation
    fn for_share(self) -> Self::Output;
}

impl<T> ForShareDsl for T
    where
        T: Table + AsQuery,
        T::Query: ForShareDsl,
{
    type Output = <T::Query as ForShareDsl>::Output;

    fn for_share(self) -> Self::Output {
        self.as_query().for_share()
    }
}

/// The `for_key_share` method
///
/// This trait should not be relied on directly by most apps. Its behavior is
/// provided by [`QueryDsl`]. However, you may need a where clause on this trait
/// to call `for_key_share` from generic code.
///
/// [`QueryDsl`]: ../trait.QueryDsl.html

pub trait ForKeyShareDsl {
    /// The type returned by `for_key_share`. See [`dsl::ForKeyShare`] for
    /// convenient access to this type.
    ///
    /// [`dsl::ForKeyShare`]: ../../dsl/type.ForKeyShare.html
    type Output;

    /// See the trait level documentation
    fn for_key_share(self) -> Self::Output;
}

impl<T> ForKeyShareDsl for T
    where
        T: Table + AsQuery,
        T::Query: ForKeyShareDsl,
{
    type Output = <T::Query as ForKeyShareDsl>::Output;

    fn for_key_share(self) -> Self::Output {
        self.as_query().for_key_share()
    }
}

/// The `skip_locked` method
///
/// This trait should not be relied on directly by most apps. Its behavior is
/// provided by [`QueryDsl`]. However, you may need a where clause on this trait
/// to call `skip_locked` from generic code.
///
/// [`QueryDsl`]: ../trait.QueryDsl.html
pub trait SkipLockedDsl {
    /// The type returned by `skip_locked`. See [`dsl::SkipLocked`] for
    /// convenient access to this type.
    ///
    /// [`dsl::SkipLocked`]: ../../dsl/type.SkipLocked.html
    type Output;

    /// See the trait level documentation
    fn skip_locked(self) -> Self::Output;
}

impl<T> SkipLockedDsl for T
where
    T: Table + AsQuery,
    T::Query: SkipLockedDsl,
{
    type Output = <T::Query as SkipLockedDsl>::Output;

    fn skip_locked(self) -> Self::Output {
        self.as_query().skip_locked()
    }
}

/// The `no_wait` method
///
/// This trait should not be relied on directly by most apps. Its behavior is
/// provided by [`QueryDsl`]. However, you may need a where clause on this trait
/// to call `no_wait` from generic code.
///
/// [`QueryDsl`]: ../trait.QueryDsl.html
pub trait NoWaitDsl {
    /// The type returned by `no_wait`. See [`dsl::NoWait`] for
    /// convenient access to this type.
    ///
    /// [`dsl::NoWait`]: ../../dsl/type.NoWait.html
    type Output;

    /// See the trait level documentation
    fn no_wait(self) -> Self::Output;
}

impl<T> NoWaitDsl for T
where
    T: Table + AsQuery,
    T::Query: NoWaitDsl,
{
    type Output = <T::Query as NoWaitDsl>::Output;

    fn no_wait(self) -> Self::Output {
        self.as_query().no_wait()
    }
}
<commit_msg>Remove unnecessary impls<commit_after>use query_builder::AsQuery;
use query_source::Table;

/// The `for_update` method
///
/// This trait should not be relied on directly by most apps. Its behavior is
/// provided by [`QueryDsl`]. However, you may need a where clause on this trait
/// to call `for_update` from generic code.
///
/// [`QueryDsl`]: ../trait.QueryDsl.html
pub trait ForUpdateDsl {
    /// The type returned by `for_update`. See [`dsl::ForUpdate`] for
    /// convenient access to this type.
    ///
    /// [`dsl::ForUpdate`]: ../../dsl/type.ForUpdate.html
    type Output;

    /// See the trait level documentation
    fn for_update(self) -> Self::Output;
}

impl<T> ForUpdateDsl for T
    where
        T: Table + AsQuery,
        T::Query: ForUpdateDsl,
{
    type Output = <T::Query as ForUpdateDsl>::Output;

    fn for_update(self) -> Self::Output {
        self.as_query().for_update()
    }
}

/// The `for_no_key_update` method
///
/// This trait should not be relied on directly by most apps. Its behavior is
/// provided by [`QueryDsl`]. However, you may need a where clause on this trait
/// to call `for_no_key_update` from generic code.
///
/// [`QueryDsl`]: ../trait.QueryDsl.html

pub trait ForNoKeyUpdateDsl {
    /// The type returned by `for_no_key_update`. See [`dsl::ForNoKeyUpdate`] for
    /// convenient access to this type.
    ///
    /// [`dsl::ForNoKeyUpdate`]: ../../dsl/type.ForNoKeyUpdate.html
    type Output;

    /// See the trait level documentation
    fn for_no_key_update(self) -> Self::Output;
}

impl<T> ForNoKeyUpdateDsl for T
    where
        T: Table + AsQuery,
        T::Query: ForNoKeyUpdateDsl,
{
    type Output = <T::Query as ForNoKeyUpdateDsl>::Output;

    fn for_no_key_update(self) -> Self::Output {
        self.as_query().for_no_key_update()
    }
}

/// The `for_share` method
///
/// This trait should not be relied on directly by most apps. Its behavior is
/// provided by [`QueryDsl`]. However, you may need a where clause on this trait
/// to call `for_share` from generic code.
///
/// [`QueryDsl`]: ../trait.QueryDsl.html
pub trait ForShareDsl {
    /// The type returned by `for_share`. See [`dsl::ForShare`] for
    /// convenient access to this type.
    ///
    /// [`dsl::ForShare`]: ../../dsl/type.ForShare.html
    type Output;

    /// See the trait level documentation
    fn for_share(self) -> Self::Output;
}

impl<T> ForShareDsl for T
    where
        T: Table + AsQuery,
        T::Query: ForShareDsl,
{
    type Output = <T::Query as ForShareDsl>::Output;

    fn for_share(self) -> Self::Output {
        self.as_query().for_share()
    }
}

/// The `for_key_share` method
///
/// This trait should not be relied on directly by most apps. Its behavior is
/// provided by [`QueryDsl`]. However, you may need a where clause on this trait
/// to call `for_key_share` from generic code.
///
/// [`QueryDsl`]: ../trait.QueryDsl.html

pub trait ForKeyShareDsl {
    /// The type returned by `for_key_share`. See [`dsl::ForKeyShare`] for
    /// convenient access to this type.
    ///
    /// [`dsl::ForKeyShare`]: ../../dsl/type.ForKeyShare.html
    type Output;

    /// See the trait level documentation
    fn for_key_share(self) -> Self::Output;
}

impl<T> ForKeyShareDsl for T
    where
        T: Table + AsQuery,
        T::Query: ForKeyShareDsl,
{
    type Output = <T::Query as ForKeyShareDsl>::Output;

    fn for_key_share(self) -> Self::Output {
        self.as_query().for_key_share()
    }
}

/// The `skip_locked` method
///
/// This trait should not be relied on directly by most apps. Its behavior is
/// provided by [`QueryDsl`]. However, you may need a where clause on this trait
/// to call `skip_locked` from generic code.
///
/// [`QueryDsl`]: ../trait.QueryDsl.html
pub trait SkipLockedDsl {
    /// The type returned by `skip_locked`. See [`dsl::SkipLocked`] for
    /// convenient access to this type.
    ///
    /// [`dsl::SkipLocked`]: ../../dsl/type.SkipLocked.html
    type Output;

    /// See the trait level documentation
    fn skip_locked(self) -> Self::Output;
}

/// The `no_wait` method
///
/// This trait should not be relied on directly by most apps. Its behavior is
/// provided by [`QueryDsl`]. However, you may need a where clause on this trait
/// to call `no_wait` from generic code.
///
/// [`QueryDsl`]: ../trait.QueryDsl.html
pub trait NoWaitDsl {
    /// The type returned by `no_wait`. See [`dsl::NoWait`] for
    /// convenient access to this type.
    ///
    /// [`dsl::NoWait`]: ../../dsl/type.NoWait.html
    type Output;

    /// See the trait level documentation
    fn no_wait(self) -> Self::Output;
}
<|endoftext|>