<commit_before>use std::fs::read;

use diesel::RunQueryDsl;
use juniper::FieldResult;

use super::*;

use crate::file::FileContentInput;
use content::{FileContent, FileName, FileVariant, MediaType, TextVariant};
use root::ApiContext;

use crate::api::award::ScoreAwardGrading;
use crate::api::contest_evaluation::Evaluation;
use crate::api::contest_problem::Problem;
use crate::api::messages::Message;
use crate::api::root::Query;
use crate::data::award::{ScoreAwardDomain, ScoreAwardGrade, ScoreAwardValue, ScoreRange};
use crate::data::contest::ContestMaterial;
use chrono::DateTime;
use schema::contest;
use std::path::PathBuf;
use user::{User, UserId};

/// A user authorization token
#[derive(juniper::GraphQLObject)]
pub struct UserToken {
    /// The user token encoded as a JWT
    pub token: String,
    /// The ID of the user associated with the given credentials, if any
    pub user_id: Option<UserId>,
}

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

pub struct Contest {
    data: ContestData,
}

impl Contest {
    pub fn current(context: &ApiContext) -> FieldResult<Self> {
        let data = contest::table.first(&context.database)?;
        Ok(Contest { data })
    }

    pub fn init(context: &ApiContext) -> FieldResult<()> {
        let now = chrono::Local::now();
        let configuration = ContestDataInput {
            archive_integrity: &context.create_blob(include_bytes!(concat!(
                env!("OUT_DIR"),
                "/initial-contest.tar.xz"
            )))?,
            start_time: &now.to_rfc3339(),
            end_time: &(now + chrono::Duration::hours(4)).to_rfc3339(),
        };
        diesel::insert_into(schema::contest::table)
            .values(configuration)
            .execute(&context.database)?;
        Ok(())
    }

    pub fn update(&self, context: &ApiContext, input: ContestUpdateInput) -> FieldResult<()> {
        let changeset = ContestChangeset {
            archive_integrity: if let Some(ref content) = input.archive_content {
                Some(context.create_blob(&content.decode()?)?)
            } else {
                None
            },
            start_time: match input.start_time {
                Some(time) => Some(chrono::DateTime::parse_from_rfc3339(&time)?.to_rfc3339()),
                None => None,
            },
            end_time: match input.end_time {
                Some(time) => Some(chrono::DateTime::parse_from_rfc3339(&time)?.to_rfc3339()),
                None => None,
            },
        };

        diesel::update(schema::contest::table)
            .set(changeset)
            .execute(&context.database)?;

        Ok(())
    }

    fn contest_path(&self, context: &ApiContext) -> FieldResult<PathBuf> {
        context.unpack_archive(&self.data.archive_integrity, "contest")
    }
}

#[derive(juniper::GraphQLEnum)]
pub enum ContestStatus {
    NotStarted,
    Running,
    Ended,
}

#[juniper_ext::graphql(Context = ApiContext)]
impl Contest {
    /// Start time of the contest, as RFC3339 date
    ///
    /// Used only for display, e.g., of the elapsed time
    pub fn start_time(&self) -> &str {
        &self.data.start_time
    }

    /// End time of the contest, as RFC3339 date
    ///
    /// Used only for display, e.g., of the elapsed time
    pub fn end_time(&self) -> &str {
        &self.data.end_time
    }

    pub fn status(&self) -> ContestStatus {
        let now = DateTime::parse_from_rfc3339(&Query::server_time()).unwrap();
        let start = DateTime::parse_from_rfc3339(self.start_time()).unwrap();
        let end = DateTime::parse_from_rfc3339(self.end_time()).unwrap();

        if now < start {
            ContestStatus::NotStarted
        } else if now < end {
            ContestStatus::Running
        } else {
            ContestStatus::Ended
        }
    }

    pub fn material(&self, context: &ApiContext) -> FieldResult<ContestMaterial> {
        let contest_path = self.contest_path(context)?;

        Ok(ContestMaterial {
            title: contest_path
                .read_dir()
                .unwrap()
                .flat_map(|result| {
                    let entry = result.unwrap();
                    if entry.file_type().unwrap().is_dir() {
                        return None;
                    }

                    if let (Some(stem), Some(extension)) =
                        (entry.path().file_stem(), entry.path().extension())
                    {
                        if extension.to_str() != Some("txt") {
                            return None;
                        }
                        if stem.to_str() != Some("title") {
                            return None;
                        }

                        // TODO: handle multiple languages

                        return Some(TextVariant {
                            attributes: vec![],
                            value: String::from_utf8(read(entry.path()).unwrap()).unwrap(),
                        });
                    }
                    None
                })
                .collect(),
            description: contest_path
                .read_dir()
                .unwrap()
                .flat_map(|result| {
                    let entry = result.unwrap();
                    if entry.file_type().unwrap().is_dir() {
                        return None;
                    }

                    if let (Some(stem), Some(extension)) =
                        (entry.path().file_stem(), entry.path().extension())
                    {
                        if stem != "home" {
                            return None;
                        }

                        return Some(FileVariant {
                            attributes: vec![],
                            name: Some(FileName(entry.file_name().to_str().unwrap().to_owned())),
                            content: FileContent(read(entry.path()).unwrap()),
                            r#type: match extension.to_str().unwrap() {
                                "pdf" => Some(MediaType("application/pdf".to_owned())),
                                "md" => Some(MediaType("text/markdown".to_owned())),
                                "html" => Some(MediaType("text/html".to_owned())),
                                _ => None,
                            },
                        });
                    }
                    None
                })
                .collect(),
            resources: vec![],   // TODO
            attachments: vec![], // TODO
        })
    }

    /// All the problems
    fn problems(&self, context: &ApiContext) -> FieldResult<Vec<Problem>> {
        // Contestants can see problems only through `view`
        context.authorize_admin()?;
        Problem::all(context)
    }

    /// Get the information about this contest visible to a user
    fn view(&self, context: &ApiContext, user_id: Option<UserId>) -> FieldResult<ContestView> {
        context.authorize_user(&user_id)?;
        ContestView::new(&self, user_id)
    }

    pub fn score_range(&self, context: &ApiContext) -> FieldResult<ScoreRange> {
        Ok(ScoreRange::total(
            self.problems(context)?
                .iter()
                .map(|problem| problem.score_range()),
        ))
    }

    pub fn score_domain(&self, context: &ApiContext) -> FieldResult<ScoreAwardDomain> {
        context.authorize_admin()?;
        Ok(ScoreAwardDomain {
            range: self.score_range(context)?,
        })
    }

    fn users(&self, context: &ApiContext) -> FieldResult<Vec<User>> {
        context.authorize_admin()?;
        User::list(context)
    }

    fn submissions(
        &self,
        context: &ApiContext,
    ) -> FieldResult<Vec<contest_submission::Submission>> {
        context.authorize_admin()?;
        contest_submission::Submission::list(context)
    }

    fn evaluations(&self, context: &ApiContext) -> FieldResult<Vec<Evaluation>> {
        context.authorize_admin()?;
        Evaluation::list(context)
    }

    fn messages(&self, context: &ApiContext) -> FieldResult<Vec<Message>> {
        context.authorize_admin()?;
        Message::list(context)
    }
}

/// Information visible to a contestant
pub struct ContestView<'a> {
    contest: &'a Contest,
    user_id: Option<UserId>,
}

impl<'a> ContestView<'a> {
    pub fn new(contest: &'a Contest, user_id: Option<UserId>) -> FieldResult<Self> {
        Ok(Self { contest, user_id })
    }

    pub fn user_id(&self) -> &Option<UserId> {
        &self.user_id
    }
}

pub struct ProblemSet<'a> {
    #[allow(dead_code)]
    contest: &'a Contest,
}

/// Set of problems currently active
#[juniper_ext::graphql(Context = ApiContext)]
impl ProblemSet<'_> {
    /// The list of problems
    fn problems(&self, context: &ApiContext) -> FieldResult<Vec<Problem>> {
        // TODO: return only the problems that only the user can access
        Ok(Problem::all(context)?)
    }

    /// Range of the total score, obtained as the sum of score range of each problem
    fn score_range(&self, context: &ApiContext) -> FieldResult<ScoreRange> {
        Ok(ScoreRange::total(
            self.problems(context)?
                .iter()
                .map(|problem| problem.score_range()),
        ))
    }

    fn score_domain(&self, context: &ApiContext) -> FieldResult<ScoreAwardDomain> {
        Ok(ScoreAwardDomain {
            range: self.score_range(context)?,
        })
    }

    /// Information about this problem set visible to a user
    fn view(&self, context: &ApiContext, user_id: Option<UserId>) -> FieldResult<ProblemSetView> {
        context.authorize_user(&user_id)?;
        Ok(ProblemSetView {
            problem_set: &self,
            user_id,
        })
    }
}

pub struct ProblemSetView<'a> {
    problem_set: &'a ProblemSet<'a>,
    user_id: Option<UserId>,
}

#[juniper_ext::graphql(Context = ApiContext)]
impl ProblemSetView<'_> {
    fn grading(&self, context: &ApiContext) -> FieldResult<ScoreAwardGrading> {
        Ok(ScoreAwardGrading {
            domain: self.problem_set.score_domain(context)?,
            grade: match self.tackling() {
                Some(t) => Some(t.grade(context)?),
                None => None,
            },
        })
    }

    /// Current progress of user in solving the problems in this problem set
    fn tackling(&self) -> Option<ProblemSetTackling> {
        // TODO: return `None` if user is not participating in the contest
        self.user_id.as_ref().map(|user_id| ProblemSetTackling {
            problem_set: &self.problem_set,
            user_id: (*user_id).clone(),
        })
    }
}

pub struct ProblemSetTackling<'a> {
    problem_set: &'a ProblemSet<'a>,
    user_id: UserId,
}

#[juniper_ext::graphql(Context = ApiContext)]
impl ProblemSetTackling<'_> {
    /// Total score, obtained as the sum of score of each problem
    fn score(&self, context: &ApiContext) -> FieldResult<ScoreAwardValue> {
        Ok(ScoreAwardValue::total(
            self.problem_set
                .problems(context)?
                .iter()
                .map(|problem| problem.view(context, Some(self.user_id.clone())))
                .collect::<FieldResult<Vec<_>>>()?
                .iter()
                .filter_map(|view| view.tackling())
                .map(|tackling| tackling.score(context))
                .collect::<FieldResult<Vec<_>>>()?,
        ))
    }

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

#[juniper_ext::graphql(Context = ApiContext)]
impl ContestView<'_> {
    fn problem_set(&self) -> Option<ProblemSet> {
        match self.contest.status() {
            ContestStatus::NotStarted => None,
            ContestStatus::Running | ContestStatus::Ended => Some(ProblemSet {
                contest: &self.contest,
            }),
        }
    }

    /// Messages visible by the current user
    fn messages(&self, context: &ApiContext) -> FieldResult<Vec<Message>> {
        Message::for_user(context, &self.user_id)
    }
}

/// The configuration of a contest
#[derive(Queryable)]
pub struct ContestData {
    /// Primary key of the table. Should be *always* 0!
    #[allow(dead_code)]
    id: i32,

    archive_integrity: String,

    /// Starting time of the contest, as RFC3339 date
    start_time: String,

    /// End time of the contest, as RFC3339 date
    end_time: String,
}

#[derive(Insertable)]
#[table_name = "contest"]
struct ContestDataInput<'a> {
    pub archive_integrity: &'a str,
    pub start_time: &'a str,
    pub end_time: &'a str,
}

#[derive(AsChangeset)]
#[table_name = "contest"]
struct ContestChangeset {
    pub archive_integrity: Option<String>,
    pub start_time: Option<String>,
    pub end_time: Option<String>,
}
<commit_msg>Add API to check if the viewer can send a message<commit_after>use std::fs::read;

use diesel::RunQueryDsl;
use juniper::FieldResult;

use super::*;

use crate::file::FileContentInput;
use content::{FileContent, FileName, FileVariant, MediaType, TextVariant};
use root::ApiContext;

use crate::api::award::ScoreAwardGrading;
use crate::api::contest_evaluation::Evaluation;
use crate::api::contest_problem::Problem;
use crate::api::messages::Message;
use crate::api::root::Query;
use crate::data::award::{ScoreAwardDomain, ScoreAwardGrade, ScoreAwardValue, ScoreRange};
use crate::data::contest::ContestMaterial;
use chrono::DateTime;
use schema::contest;
use std::path::PathBuf;
use user::{User, UserId};

/// A user authorization token
#[derive(juniper::GraphQLObject)]
pub struct UserToken {
    /// The user token encoded as a JWT
    pub token: String,
    /// The ID of the user associated with the given credentials, if any
    pub user_id: Option<UserId>,
}

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

pub struct Contest {
    data: ContestData,
}

impl Contest {
    pub fn current(context: &ApiContext) -> FieldResult<Self> {
        let data = contest::table.first(&context.database)?;
        Ok(Contest { data })
    }

    pub fn init(context: &ApiContext) -> FieldResult<()> {
        let now = chrono::Local::now();
        let configuration = ContestDataInput {
            archive_integrity: &context.create_blob(include_bytes!(concat!(
                env!("OUT_DIR"),
                "/initial-contest.tar.xz"
            )))?,
            start_time: &now.to_rfc3339(),
            end_time: &(now + chrono::Duration::hours(4)).to_rfc3339(),
        };
        diesel::insert_into(schema::contest::table)
            .values(configuration)
            .execute(&context.database)?;
        Ok(())
    }

    pub fn update(&self, context: &ApiContext, input: ContestUpdateInput) -> FieldResult<()> {
        let changeset = ContestChangeset {
            archive_integrity: if let Some(ref content) = input.archive_content {
                Some(context.create_blob(&content.decode()?)?)
            } else {
                None
            },
            start_time: match input.start_time {
                Some(time) => Some(chrono::DateTime::parse_from_rfc3339(&time)?.to_rfc3339()),
                None => None,
            },
            end_time: match input.end_time {
                Some(time) => Some(chrono::DateTime::parse_from_rfc3339(&time)?.to_rfc3339()),
                None => None,
            },
        };

        diesel::update(schema::contest::table)
            .set(changeset)
            .execute(&context.database)?;

        Ok(())
    }

    fn contest_path(&self, context: &ApiContext) -> FieldResult<PathBuf> {
        context.unpack_archive(&self.data.archive_integrity, "contest")
    }
}

#[derive(juniper::GraphQLEnum)]
pub enum ContestStatus {
    NotStarted,
    Running,
    Ended,
}

#[juniper_ext::graphql(Context = ApiContext)]
impl Contest {
    /// Start time of the contest, as RFC3339 date
    ///
    /// Used only for display, e.g., of the elapsed time
    pub fn start_time(&self) -> &str {
        &self.data.start_time
    }

    /// End time of the contest, as RFC3339 date
    ///
    /// Used only for display, e.g., of the elapsed time
    pub fn end_time(&self) -> &str {
        &self.data.end_time
    }

    pub fn status(&self) -> ContestStatus {
        let now = DateTime::parse_from_rfc3339(&Query::server_time()).unwrap();
        let start = DateTime::parse_from_rfc3339(self.start_time()).unwrap();
        let end = DateTime::parse_from_rfc3339(self.end_time()).unwrap();

        if now < start {
            ContestStatus::NotStarted
        } else if now < end {
            ContestStatus::Running
        } else {
            ContestStatus::Ended
        }
    }

    pub fn material(&self, context: &ApiContext) -> FieldResult<ContestMaterial> {
        let contest_path = self.contest_path(context)?;

        Ok(ContestMaterial {
            title: contest_path
                .read_dir()
                .unwrap()
                .flat_map(|result| {
                    let entry = result.unwrap();
                    if entry.file_type().unwrap().is_dir() {
                        return None;
                    }

                    if let (Some(stem), Some(extension)) =
                        (entry.path().file_stem(), entry.path().extension())
                    {
                        if extension.to_str() != Some("txt") {
                            return None;
                        }
                        if stem.to_str() != Some("title") {
                            return None;
                        }

                        // TODO: handle multiple languages

                        return Some(TextVariant {
                            attributes: vec![],
                            value: String::from_utf8(read(entry.path()).unwrap()).unwrap(),
                        });
                    }
                    None
                })
                .collect(),
            description: contest_path
                .read_dir()
                .unwrap()
                .flat_map(|result| {
                    let entry = result.unwrap();
                    if entry.file_type().unwrap().is_dir() {
                        return None;
                    }

                    if let (Some(stem), Some(extension)) =
                        (entry.path().file_stem(), entry.path().extension())
                    {
                        if stem != "home" {
                            return None;
                        }

                        return Some(FileVariant {
                            attributes: vec![],
                            name: Some(FileName(entry.file_name().to_str().unwrap().to_owned())),
                            content: FileContent(read(entry.path()).unwrap()),
                            r#type: match extension.to_str().unwrap() {
                                "pdf" => Some(MediaType("application/pdf".to_owned())),
                                "md" => Some(MediaType("text/markdown".to_owned())),
                                "html" => Some(MediaType("text/html".to_owned())),
                                _ => None,
                            },
                        });
                    }
                    None
                })
                .collect(),
            resources: vec![],   // TODO
            attachments: vec![], // TODO
        })
    }

    /// All the problems
    fn problems(&self, context: &ApiContext) -> FieldResult<Vec<Problem>> {
        // Contestants can see problems only through `view`
        context.authorize_admin()?;
        Problem::all(context)
    }

    /// Get the information about this contest visible to a user
    fn view(&self, context: &ApiContext, user_id: Option<UserId>) -> FieldResult<ContestView> {
        context.authorize_user(&user_id)?;
        ContestView::new(&self, user_id)
    }

    pub fn score_range(&self, context: &ApiContext) -> FieldResult<ScoreRange> {
        Ok(ScoreRange::total(
            self.problems(context)?
                .iter()
                .map(|problem| problem.score_range()),
        ))
    }

    pub fn score_domain(&self, context: &ApiContext) -> FieldResult<ScoreAwardDomain> {
        context.authorize_admin()?;
        Ok(ScoreAwardDomain {
            range: self.score_range(context)?,
        })
    }

    fn users(&self, context: &ApiContext) -> FieldResult<Vec<User>> {
        context.authorize_admin()?;
        User::list(context)
    }

    fn submissions(
        &self,
        context: &ApiContext,
    ) -> FieldResult<Vec<contest_submission::Submission>> {
        context.authorize_admin()?;
        contest_submission::Submission::list(context)
    }

    fn evaluations(&self, context: &ApiContext) -> FieldResult<Vec<Evaluation>> {
        context.authorize_admin()?;
        Evaluation::list(context)
    }

    fn messages(&self, context: &ApiContext) -> FieldResult<Vec<Message>> {
        context.authorize_admin()?;
        Message::list(context)
    }
}

/// Information visible to a contestant
pub struct ContestView<'a> {
    contest: &'a Contest,
    user_id: Option<UserId>,
}

impl<'a> ContestView<'a> {
    pub fn new(contest: &'a Contest, user_id: Option<UserId>) -> FieldResult<Self> {
        Ok(Self { contest, user_id })
    }

    pub fn user_id(&self) -> &Option<UserId> {
        &self.user_id
    }
}

pub struct ProblemSet<'a> {
    #[allow(dead_code)]
    contest: &'a Contest,
}

/// Set of problems currently active
#[juniper_ext::graphql(Context = ApiContext)]
impl ProblemSet<'_> {
    /// The list of problems
    fn problems(&self, context: &ApiContext) -> FieldResult<Vec<Problem>> {
        // TODO: return only the problems that only the user can access
        Ok(Problem::all(context)?)
    }

    /// Range of the total score, obtained as the sum of score range of each problem
    fn score_range(&self, context: &ApiContext) -> FieldResult<ScoreRange> {
        Ok(ScoreRange::total(
            self.problems(context)?
                .iter()
                .map(|problem| problem.score_range()),
        ))
    }

    fn score_domain(&self, context: &ApiContext) -> FieldResult<ScoreAwardDomain> {
        Ok(ScoreAwardDomain {
            range: self.score_range(context)?,
        })
    }

    /// Information about this problem set visible to a user
    fn view(&self, context: &ApiContext, user_id: Option<UserId>) -> FieldResult<ProblemSetView> {
        context.authorize_user(&user_id)?;
        Ok(ProblemSetView {
            problem_set: &self,
            user_id,
        })
    }
}

pub struct ProblemSetView<'a> {
    problem_set: &'a ProblemSet<'a>,
    user_id: Option<UserId>,
}

#[juniper_ext::graphql(Context = ApiContext)]
impl ProblemSetView<'_> {
    fn grading(&self, context: &ApiContext) -> FieldResult<ScoreAwardGrading> {
        Ok(ScoreAwardGrading {
            domain: self.problem_set.score_domain(context)?,
            grade: match self.tackling() {
                Some(t) => Some(t.grade(context)?),
                None => None,
            },
        })
    }

    /// Current progress of user in solving the problems in this problem set
    fn tackling(&self) -> Option<ProblemSetTackling> {
        // TODO: return `None` if user is not participating in the contest
        self.user_id.as_ref().map(|user_id| ProblemSetTackling {
            problem_set: &self.problem_set,
            user_id: (*user_id).clone(),
        })
    }
}

pub struct ProblemSetTackling<'a> {
    problem_set: &'a ProblemSet<'a>,
    user_id: UserId,
}

#[juniper_ext::graphql(Context = ApiContext)]
impl ProblemSetTackling<'_> {
    /// Total score, obtained as the sum of score of each problem
    fn score(&self, context: &ApiContext) -> FieldResult<ScoreAwardValue> {
        Ok(ScoreAwardValue::total(
            self.problem_set
                .problems(context)?
                .iter()
                .map(|problem| problem.view(context, Some(self.user_id.clone())))
                .collect::<FieldResult<Vec<_>>>()?
                .iter()
                .filter_map(|view| view.tackling())
                .map(|tackling| tackling.score(context))
                .collect::<FieldResult<Vec<_>>>()?,
        ))
    }

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

#[juniper_ext::graphql(Context = ApiContext)]
impl ContestView<'_> {
    fn problem_set(&self) -> Option<ProblemSet> {
        match self.contest.status() {
            ContestStatus::NotStarted => None,
            ContestStatus::Running | ContestStatus::Ended => Some(ProblemSet {
                contest: &self.contest,
            }),
        }
    }

    /// Messages visible by the current user
    fn messages(&self, context: &ApiContext) -> FieldResult<Vec<Message>> {
        Message::for_user(context, &self.user_id)
    }

    pub fn can_send_message(&self) -> bool {
        self.user_id.is_some()
    }
}

/// The configuration of a contest
#[derive(Queryable)]
pub struct ContestData {
    /// Primary key of the table. Should be *always* 0!
    #[allow(dead_code)]
    id: i32,

    archive_integrity: String,

    /// Starting time of the contest, as RFC3339 date
    start_time: String,

    /// End time of the contest, as RFC3339 date
    end_time: String,
}

#[derive(Insertable)]
#[table_name = "contest"]
struct ContestDataInput<'a> {
    pub archive_integrity: &'a str,
    pub start_time: &'a str,
    pub end_time: &'a str,
}

#[derive(AsChangeset)]
#[table_name = "contest"]
struct ContestChangeset {
    pub archive_integrity: Option<String>,
    pub start_time: Option<String>,
    pub end_time: Option<String>,
}
<|endoftext|>


<commit_before>//! Completion tests for type position.
use expect_test::{expect, Expect};

use crate::tests::{completion_list, BASE_ITEMS_FIXTURE};

fn check(ra_fixture: &str, expect: Expect) {
    let actual = completion_list(&format!("{}\n{}", BASE_ITEMS_FIXTURE, ra_fixture));
    expect.assert_eq(&actual)
}

#[test]
fn record_field_ty() {
    check(
        r#"
struct Foo<'lt, T, const C: usize> {
    f: $0
}
"#,
        expect![[r#"
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            sp Self
            st Foo<…>
            st Record
            st Tuple
            st Unit
            tt Trait
            tp T
            un Union
            bt u32
            kw crate::
            kw self::
            kw super::
        "#]],
    )
}

#[test]
fn tuple_struct_field() {
    check(
        r#"
struct Foo<'lt, T, const C: usize>(f$0);
"#,
        expect![[r#"
            en Enum
            ma makro!(…)  macro_rules! makro
            md module
            sp Self
            st Foo<…>
            st Record
            st Tuple
            st Unit
            tt Trait
            tp T
            un Union
            bt u32
            kw crate::
            kw pub
            kw pub(crate)
            kw pub(super)
            kw self::
            kw super::
        "#]],
    )
}

#[test]
fn fn_return_type() {
    check(
        r#"
fn x<'lt, T, const C: usize>() -> $0
"#,
        expect![[r#"
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt Trait
            tp T
            un Union
            bt u32
            kw crate::
            kw self::
            kw super::
        "#]],
    );
}

#[test]
fn fn_return_type_no_local_items() {
    check(
        r#"
fn foo() -> B$0 {
    struct Bar;
    enum Baz {}
    union Bax {
        i: i32,
        f: f32
    }
}
"#,
        expect![[r#"
        en Enum
        ma makro!(…) macro_rules! makro
        md module
        st Record
        st Tuple
        st Unit
        tt Trait
        un Union
        bt u32
        it ()
        kw crate::
        kw self::
        kw super::
    "#]],
    )
}

#[test]
fn inferred_type_const() {
    check(
        r#"
struct Foo<T>(T);
const FOO: $0 = Foo(2);
"#,
        expect![[r#"
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Foo<…>
            st Record
            st Tuple
            st Unit
            tt Trait
            un Union
            bt u32
            it Foo<i32>
            kw crate::
            kw self::
            kw super::
        "#]],
    );
}

#[test]
fn inferred_type_closure_param() {
    check(
        r#"
fn f1(f: fn(i32) -> i32) {}
fn f2() {
    f1(|x: $0);
}
"#,
        expect![[r#"
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt Trait
            un Union
            bt u32
            it i32
            kw crate::
            kw self::
            kw super::
        "#]],
    );
}

#[test]
fn inferred_type_closure_return() {
    check(
        r#"
fn f1(f: fn(u64) -> u64) {}
fn f2() {
    f1(|x| -> $0 {
        x + 5
    });
}
"#,
        expect![[r#"
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt Trait
            un Union
            bt u32
            it u64
            kw crate::
            kw self::
            kw super::
        "#]],
    );
}

#[test]
fn inferred_type_fn_return() {
    check(
        r#"
fn f2(x: u64) -> $0 {
    x + 5
}
"#,
        expect![[r#"
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt Trait
            un Union
            bt u32
            it u64
            kw crate::
            kw self::
            kw super::
        "#]],
    );
}

#[test]
fn inferred_type_fn_param() {
    check(
        r#"
fn f1(x: i32) {}
fn f2(x: $0) {
    f1(x);
}
"#,
        expect![[r#"
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt Trait
            un Union
            bt u32
            it i32
            kw crate::
            kw self::
            kw super::
        "#]],
    );
}

#[test]
fn inferred_type_not_in_the_scope() {
    check(
        r#"
mod a {
    pub struct Foo<T>(T);
    pub fn x() -> Foo<Foo<i32>> {
        Foo(Foo(2))
    }
}
fn foo<'lt, T, const C: usize>() {
    let local = ();
    let foo: $0 = a::x();
}
"#,
        expect![[r#"
            en Enum
            ma makro!(…)           macro_rules! makro
            md a
            md module
            st Record
            st Tuple
            st Unit
            tt Trait
            tp T
            un Union
            bt u32
            it a::Foo<a::Foo<i32>>
            kw crate::
            kw self::
            kw super::
        "#]],
    );
}

#[test]
fn inferred_type_let() {
    check(
        r#"
struct Foo<T>(T);
fn foo<'lt, T, const C: usize>() {
    let local = ();
    let foo: $0 = Foo(2);
}
"#,
        expect![[r#"
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Foo<…>
            st Record
            st Tuple
            st Unit
            tt Trait
            tp T
            un Union
            bt u32
            it Foo<i32>
            kw crate::
            kw self::
            kw super::
        "#]],
    );
}

#[test]
fn body_type_pos() {
    check(
        r#"
fn foo<'lt, T, const C: usize>() {
    let local = ();
    let _: $0;
}
"#,
        expect![[r#"
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt Trait
            tp T
            un Union
            bt u32
            kw crate::
            kw self::
            kw super::
        "#]],
    );
    check(
        r#"
fn foo<'lt, T, const C: usize>() {
    let local = ();
    let _: self::$0;
}
"#,
        expect![[r#"
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt Trait
            un Union
        "#]],
    );
}

#[test]
fn completes_types_and_const_in_arg_list() {
    cov_mark::check!(complete_assoc_type_in_generics_list);
    check(
        r#"
trait Trait1 {
    type Super;
}
trait Trait2: Trait1 {
    type Foo;
}

fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
"#,
        expect![[r#"
            ta Foo =  (as Trait2)   type Foo
            ta Super =  (as Trait1) type Super
        "#]],
    );
    check(
        r#"
trait Trait1 {
    type Super;
}
trait Trait2<T>: Trait1 {
    type Foo;
}

fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
"#,
        expect![[r#"
            ct CONST
            cp CONST_PARAM
            en Enum
            ma makro!(…)   macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt Trait
            tt Trait1
            tt Trait2
            tp T
            un Union
            bt u32
            kw crate::
            kw self::
            kw super::
        "#]],
    );
    check(
        r#"
trait Trait2 {
    type Foo;
}

fn foo<'lt, T: Trait2<self::$0>, const CONST_PARAM: usize>(_: T) {}
    "#,
        expect![[r#"
            ct CONST
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt Trait
            tt Trait2
            un Union
        "#]],
    );
}

#[test]
fn no_assoc_completion_outside_type_bounds() {
    check(
        r#"
struct S;
trait Tr<T> {
    type Ty;
}

impl Tr<$0
    "#,
        expect![[r#"
            ct CONST
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            sp Self
            st Record
            st S
            st Tuple
            st Unit
            tt Tr
            tt Trait
            un Union
            bt u32
            kw crate::
            kw self::
            kw super::
        "#]],
    );
}

#[test]
fn enum_qualified() {
    check(
        r#"
impl Enum {
    type AssocType = ();
    const ASSOC_CONST: () = ();
    fn assoc_fn() {}
}
fn func(_: Enum::$0) {}
"#,
        expect![[r#"
            ta AssocType type AssocType = ()
        "#]],
    );
}

#[test]
fn completes_type_parameter_or_associated_type() {
    check(
        r#"
trait MyTrait<T, U> {
    type Item1;
    type Item2;
};

fn f(t: impl MyTrait<u$0
"#,
        expect![[r#"
            ct CONST
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt MyTrait
            tt Trait
            un Union
            bt u32
            kw crate::
            kw self::
            kw super::
        "#]],
    );

    check(
        r#"
trait MyTrait<T, U> {
    type Item1;
    type Item2;
};

fn f(t: impl MyTrait<u8, u$0
"#,
        expect![[r#"
            ct CONST
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt MyTrait
            tt Trait
            un Union
            bt u32
            kw crate::
            kw self::
            kw super::
        "#]],
    );

    check(
        r#"
trait MyTrait<T, U> {
    type Item1;
    type Item2;
};

fn f(t: impl MyTrait<u8, u8, I$0
"#,
        expect![[r#"
            ta Item1 =  (as MyTrait) type Item1
            ta Item2 =  (as MyTrait) type Item2
        "#]],
    );
}

#[test]
fn completes_type_parameter_or_associated_type_with_default_value() {
    check(
        r#"
trait MyTrait<T = u8, U> {
    type Item1;
    type Item2;
};

fn f(t: impl MyTrait<u$0
"#,
        expect![[r#"
            ct CONST
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt MyTrait
            tt Trait
            un Union
            bt u32
            kw crate::
            kw self::
            kw super::
        "#]],
    );

    check(
        r#"
trait MyTrait<T = u8, U> {
    type Item1;
    type Item2;
};

fn f(t: impl MyTrait<u8, u$0
"#,
        expect![[r#"
            ct CONST
            en Enum
            ma makro!(…)             macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt MyTrait
            tt Trait
            ta Item1 =  (as MyTrait) type Item1
            ta Item2 =  (as MyTrait) type Item2
            un Union
            bt u32
            kw crate::
            kw self::
            kw super::
        "#]],
    );

    check(
        r#"
trait MyTrait<T = u8, U> {
    type Item1;
    type Item2;
};

fn f(t: impl MyTrait<u8, u8, I$0
"#,
        expect![[r#"
            ta Item1 =  (as MyTrait) type Item1
            ta Item2 =  (as MyTrait) type Item2
        "#]],
    );
}

#[test]
fn completes_types_after_associated_type() {
    check(
        r#"
trait MyTrait {
    type Item1;
    type Item2;
};

fn f(t: impl MyTrait<Item1 = $0
"#,
        expect![[r#"
            ct CONST
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt MyTrait
            tt Trait
            un Union
            bt u32
            kw crate::
            kw self::
            kw super::
        "#]],
    );

    check(
        r#"
trait MyTrait {
    type Item1;
    type Item2;
};

fn f(t: impl MyTrait<Item1 = u8, Item2 = $0
"#,
        expect![[r#"
            ct CONST
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt MyTrait
            tt Trait
            un Union
            bt u32
            kw crate::
            kw self::
            kw super::
        "#]],
    );
}
<commit_msg>fix default type param value position<commit_after>//! Completion tests for type position.
use expect_test::{expect, Expect};

use crate::tests::{completion_list, BASE_ITEMS_FIXTURE};

fn check(ra_fixture: &str, expect: Expect) {
    let actual = completion_list(&format!("{}\n{}", BASE_ITEMS_FIXTURE, ra_fixture));
    expect.assert_eq(&actual)
}

#[test]
fn record_field_ty() {
    check(
        r#"
struct Foo<'lt, T, const C: usize> {
    f: $0
}
"#,
        expect![[r#"
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            sp Self
            st Foo<…>
            st Record
            st Tuple
            st Unit
            tt Trait
            tp T
            un Union
            bt u32
            kw crate::
            kw self::
            kw super::
        "#]],
    )
}

#[test]
fn tuple_struct_field() {
    check(
        r#"
struct Foo<'lt, T, const C: usize>(f$0);
"#,
        expect![[r#"
            en Enum
            ma makro!(…)  macro_rules! makro
            md module
            sp Self
            st Foo<…>
            st Record
            st Tuple
            st Unit
            tt Trait
            tp T
            un Union
            bt u32
            kw crate::
            kw pub
            kw pub(crate)
            kw pub(super)
            kw self::
            kw super::
        "#]],
    )
}

#[test]
fn fn_return_type() {
    check(
        r#"
fn x<'lt, T, const C: usize>() -> $0
"#,
        expect![[r#"
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt Trait
            tp T
            un Union
            bt u32
            kw crate::
            kw self::
            kw super::
        "#]],
    );
}

#[test]
fn fn_return_type_no_local_items() {
    check(
        r#"
fn foo() -> B$0 {
    struct Bar;
    enum Baz {}
    union Bax {
        i: i32,
        f: f32
    }
}
"#,
        expect![[r#"
        en Enum
        ma makro!(…) macro_rules! makro
        md module
        st Record
        st Tuple
        st Unit
        tt Trait
        un Union
        bt u32
        it ()
        kw crate::
        kw self::
        kw super::
    "#]],
    )
}

#[test]
fn inferred_type_const() {
    check(
        r#"
struct Foo<T>(T);
const FOO: $0 = Foo(2);
"#,
        expect![[r#"
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Foo<…>
            st Record
            st Tuple
            st Unit
            tt Trait
            un Union
            bt u32
            it Foo<i32>
            kw crate::
            kw self::
            kw super::
        "#]],
    );
}

#[test]
fn inferred_type_closure_param() {
    check(
        r#"
fn f1(f: fn(i32) -> i32) {}
fn f2() {
    f1(|x: $0);
}
"#,
        expect![[r#"
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt Trait
            un Union
            bt u32
            it i32
            kw crate::
            kw self::
            kw super::
        "#]],
    );
}

#[test]
fn inferred_type_closure_return() {
    check(
        r#"
fn f1(f: fn(u64) -> u64) {}
fn f2() {
    f1(|x| -> $0 {
        x + 5
    });
}
"#,
        expect![[r#"
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt Trait
            un Union
            bt u32
            it u64
            kw crate::
            kw self::
            kw super::
        "#]],
    );
}

#[test]
fn inferred_type_fn_return() {
    check(
        r#"
fn f2(x: u64) -> $0 {
    x + 5
}
"#,
        expect![[r#"
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt Trait
            un Union
            bt u32
            it u64
            kw crate::
            kw self::
            kw super::
        "#]],
    );
}

#[test]
fn inferred_type_fn_param() {
    check(
        r#"
fn f1(x: i32) {}
fn f2(x: $0) {
    f1(x);
}
"#,
        expect![[r#"
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt Trait
            un Union
            bt u32
            it i32
            kw crate::
            kw self::
            kw super::
        "#]],
    );
}

#[test]
fn inferred_type_not_in_the_scope() {
    check(
        r#"
mod a {
    pub struct Foo<T>(T);
    pub fn x() -> Foo<Foo<i32>> {
        Foo(Foo(2))
    }
}
fn foo<'lt, T, const C: usize>() {
    let local = ();
    let foo: $0 = a::x();
}
"#,
        expect![[r#"
            en Enum
            ma makro!(…)           macro_rules! makro
            md a
            md module
            st Record
            st Tuple
            st Unit
            tt Trait
            tp T
            un Union
            bt u32
            it a::Foo<a::Foo<i32>>
            kw crate::
            kw self::
            kw super::
        "#]],
    );
}

#[test]
fn inferred_type_let() {
    check(
        r#"
struct Foo<T>(T);
fn foo<'lt, T, const C: usize>() {
    let local = ();
    let foo: $0 = Foo(2);
}
"#,
        expect![[r#"
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Foo<…>
            st Record
            st Tuple
            st Unit
            tt Trait
            tp T
            un Union
            bt u32
            it Foo<i32>
            kw crate::
            kw self::
            kw super::
        "#]],
    );
}

#[test]
fn body_type_pos() {
    check(
        r#"
fn foo<'lt, T, const C: usize>() {
    let local = ();
    let _: $0;
}
"#,
        expect![[r#"
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt Trait
            tp T
            un Union
            bt u32
            kw crate::
            kw self::
            kw super::
        "#]],
    );
    check(
        r#"
fn foo<'lt, T, const C: usize>() {
    let local = ();
    let _: self::$0;
}
"#,
        expect![[r#"
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt Trait
            un Union
        "#]],
    );
}

#[test]
fn completes_types_and_const_in_arg_list() {
    cov_mark::check!(complete_assoc_type_in_generics_list);
    check(
        r#"
trait Trait1 {
    type Super;
}
trait Trait2: Trait1 {
    type Foo;
}

fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
"#,
        expect![[r#"
            ta Foo =  (as Trait2)   type Foo
            ta Super =  (as Trait1) type Super
        "#]],
    );
    check(
        r#"
trait Trait1 {
    type Super;
}
trait Trait2<T>: Trait1 {
    type Foo;
}

fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
"#,
        expect![[r#"
            ct CONST
            cp CONST_PARAM
            en Enum
            ma makro!(…)   macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt Trait
            tt Trait1
            tt Trait2
            tp T
            un Union
            bt u32
            kw crate::
            kw self::
            kw super::
        "#]],
    );
    check(
        r#"
trait Trait2 {
    type Foo;
}

fn foo<'lt, T: Trait2<self::$0>, const CONST_PARAM: usize>(_: T) {}
    "#,
        expect![[r#"
            ct CONST
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt Trait
            tt Trait2
            un Union
        "#]],
    );
}

#[test]
fn no_assoc_completion_outside_type_bounds() {
    check(
        r#"
struct S;
trait Tr<T> {
    type Ty;
}

impl Tr<$0
    "#,
        expect![[r#"
            ct CONST
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            sp Self
            st Record
            st S
            st Tuple
            st Unit
            tt Tr
            tt Trait
            un Union
            bt u32
            kw crate::
            kw self::
            kw super::
        "#]],
    );
}

#[test]
fn enum_qualified() {
    check(
        r#"
impl Enum {
    type AssocType = ();
    const ASSOC_CONST: () = ();
    fn assoc_fn() {}
}
fn func(_: Enum::$0) {}
"#,
        expect![[r#"
            ta AssocType type AssocType = ()
        "#]],
    );
}

#[test]
fn completes_type_parameter_or_associated_type() {
    check(
        r#"
trait MyTrait<T, U> {
    type Item1;
    type Item2;
};

fn f(t: impl MyTrait<u$0
"#,
        expect![[r#"
            ct CONST
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt MyTrait
            tt Trait
            un Union
            bt u32
            kw crate::
            kw self::
            kw super::
        "#]],
    );

    check(
        r#"
trait MyTrait<T, U> {
    type Item1;
    type Item2;
};

fn f(t: impl MyTrait<u8, u$0
"#,
        expect![[r#"
            ct CONST
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt MyTrait
            tt Trait
            un Union
            bt u32
            kw crate::
            kw self::
            kw super::
        "#]],
    );

    check(
        r#"
trait MyTrait<T, U> {
    type Item1;
    type Item2;
};

fn f(t: impl MyTrait<u8, u8, I$0
"#,
        expect![[r#"
            ta Item1 =  (as MyTrait) type Item1
            ta Item2 =  (as MyTrait) type Item2
        "#]],
    );
}

#[test]
fn completes_type_parameter_or_associated_type_with_default_value() {
    check(
        r#"
trait MyTrait<T, U = u8> {
    type Item1;
    type Item2;
};

fn f(t: impl MyTrait<u$0
"#,
        expect![[r#"
            ct CONST
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt MyTrait
            tt Trait
            un Union
            bt u32
            kw crate::
            kw self::
            kw super::
        "#]],
    );

    check(
        r#"
trait MyTrait<T, U = u8> {
    type Item1;
    type Item2;
};

fn f(t: impl MyTrait<u8, u$0
"#,
        expect![[r#"
            ct CONST
            en Enum
            ma makro!(…)             macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt MyTrait
            tt Trait
            ta Item1 =  (as MyTrait) type Item1
            ta Item2 =  (as MyTrait) type Item2
            un Union
            bt u32
            kw crate::
            kw self::
            kw super::
        "#]],
    );

    check(
        r#"
trait MyTrait<T, U = u8> {
    type Item1;
    type Item2;
};

fn f(t: impl MyTrait<u8, u8, I$0
"#,
        expect![[r#"
            ta Item1 =  (as MyTrait) type Item1
            ta Item2 =  (as MyTrait) type Item2
        "#]],
    );
}

#[test]
fn completes_types_after_associated_type() {
    check(
        r#"
trait MyTrait {
    type Item1;
    type Item2;
};

fn f(t: impl MyTrait<Item1 = $0
"#,
        expect![[r#"
            ct CONST
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt MyTrait
            tt Trait
            un Union
            bt u32
            kw crate::
            kw self::
            kw super::
        "#]],
    );

    check(
        r#"
trait MyTrait {
    type Item1;
    type Item2;
};

fn f(t: impl MyTrait<Item1 = u8, Item2 = $0
"#,
        expect![[r#"
            ct CONST
            en Enum
            ma makro!(…) macro_rules! makro
            md module
            st Record
            st Tuple
            st Unit
            tt MyTrait
            tt Trait
            un Union
            bt u32
            kw crate::
            kw self::
            kw super::
        "#]],
    );
}
<|endoftext|>


<commit_before>// Anima Engine. The quirky game engine
// Copyright (C) 2016  Dragoș Tiselice
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

use math::Quaternion;

/// A simple vector `struct` tailored specifically for graphics.
///
/// # Examples
///
/// ```
/// # use anima_engine::math::Vector;
/// let v1 = Vector::zero();
/// let v2 = Vector::one();
///
/// assert_eq!(v1 + v2, Vector::one());
/// assert_eq!(v1 * v2, Vector::zero());
/// assert_eq!(v1.dot(v2), 0.0);
///
/// let v3 = v1;
///
/// assert_eq!(v1.dot(v2), 0.0);
/// assert_eq!((v3 + Vector::one() * 2.0).dot(v2), 6.0);
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Vector {
    /// `f32` *x* coordinate value
    pub x: f32,
    /// `f32` *y* coordinate value
    pub y: f32,
    /// `f32` *z* coordinate value
    pub z: f32
}

impl Vector {
    /// Creates a vector using 3 values.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// let v = Vector::new(0.0, 1.0, 2.0);
    ///
    /// assert_eq!(v, Vector { x: 0.0, y: 1.0, z: 2.0 });
    /// ```
    pub fn new(x: f32, y: f32, z: f32) -> Vector {
        Vector { x: x, y: y, z: z }
    }

    /// Creates a vector using an array.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// let v = Vector::new_arr([0.0, 1.0, 2.0]);
    ///
    /// assert_eq!(v, Vector { x: 0.0, y: 1.0, z: 2.0 });
    /// ```
    pub fn new_arr(array: [f32; 3]) -> Vector {
        Vector { x: array[0], y: array[1], z: array[2] }
    }

    /// Creates a uniform vector using 1 value.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// let v = Vector::new_unf(1.0);
    ///
    /// assert_eq!(v, Vector { x: 1.0, y: 1.0, z: 1.0 });
    /// ```
    pub fn new_unf(v: f32) -> Vector {
        Vector { x: v, y: v, z: v }
    }

    /// Creates a zero (0.0, 0.0, 0.0) Vector.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// assert_eq!(Vector::zero(), Vector { x: 0.0, y: 0.0, z: 0.0 });
    /// ```
    pub fn zero() -> Vector {
        Vector { x: 0.0, y: 0.0, z: 0.0 }
    }

    /// Creates a one (1.0, 1.0, 1.0) Vector.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// assert_eq!(Vector::one(), Vector { x: 1.0, y: 1.0, z: 1.0 });
    /// ```
    pub fn one() -> Vector {
        Vector { x: 1.0, y: 1.0, z: 1.0 }
    }

    /// Creates a back (0.0, 0.0, -1.0) Vector.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// assert_eq!(Vector::back(), Vector { x: 0.0, y: 0.0, z: -1.0 });
    /// ```
    pub fn back() -> Vector {
        Vector { x: 0.0, y: 0.0, z: -1.0 }
    }

    /// Creates a down (0.0, -1.0, 0.0) Vector.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// assert_eq!(Vector::down(), Vector { x: 0.0, y: -1.0, z: 0.0 });
    /// ```
    pub fn down() -> Vector {
        Vector { x: 0.0, y: -1.0, z: 0.0 }
    }

    /// Creates a forward (0.0, 0.0, 1.0) Vector.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// assert_eq!(Vector::forward(), Vector { x: 0.0, y: 0.0, z: 1.0 });
    /// ```
    pub fn forward() -> Vector {
        Vector { x: 0.0, y: 0.0, z: 1.0 }
    }

    /// Creates a left (-1.0, 0.0, 0.0) Vector.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// assert_eq!(Vector::left(), Vector { x: 1.0, y: 0.0, z: 0.0 });
    /// ```
    pub fn left() -> Vector {
        Vector { x: 1.0, y: 0.0, z: 0.0 }
    }

    /// Creates a right (1.0, 0.0, 0.0) Vector.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// assert_eq!(Vector::right(), Vector { x: -1.0, y: 0.0, z: 0.0 });
    /// ```
    pub fn right() -> Vector {
        Vector { x: -1.0, y: 0.0, z: 0.0 }
    }

    /// Creates an up (0.0, 1.0, 0.0) Vector.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// assert_eq!(Vector::up(), Vector { x: 0.0, y: 1.0, z: 0.0 });
    /// ```
    pub fn up() -> Vector {
        Vector { x: 0.0, y: 1.0, z: 0.0 }
    }

    /// Computes the length of a vector.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// let v = Vector::new(1.0, 2.0, 2.0);
    ///
    /// assert_eq!(v.len(), 3.0);
    /// ```
    pub fn len(&self) -> f32 {
        (self.x.powi(2) + self.y.powi(2) + self.z.powi(2)).sqrt()
    }

    /// Computes the normalized version of a vector.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// let v = Vector::new(1.0, 2.0, 2.0);
    /// let n = v.norm();
    ///
    /// assert_eq!(n.len(), 1.0); // Keep precision in mind when comparing floats.
    /// ```
    pub fn norm(&self) -> Vector {
        let length = self.len();

        Vector {
            x: self.x / length,
            y: self.y / length,
            z: self.z / length
        }
    }

    /// Computes the dot product between two vectors.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// let v1 = Vector::new(1.0, 2.0, 2.0);
    /// let v2 = Vector::new(3.0, 3.0, 1.0);
    ///
    /// assert_eq!(v1.dot(v2), 11.0);
    /// ```
    pub fn dot(&self, other: Vector) -> f32 {
        self.x * other.x +
        self.y * other.y +
        self.z * other.z
    }

    /// Computes the cross product between two vectors.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// let v1 = Vector::new(1.0, 2.0, 2.0);
    /// let v2 = Vector::new(3.0, 3.0, 1.0);
    ///
    /// assert_eq!(v1.cross(v2), Vector { x: -4.0, y: 5.0, z: -3.0 });
    /// ```
    pub fn cross(&self, other: Vector) -> Vector {
        Vector {
            x: self.y * other.z - self.z * other.y,
            y: self.z * other.x - self.x * other.z,
            z: self.x * other.y - self.y * other.x
        }
    }

    /// Rotates a vector according to the rotation represented by a quaternion.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// # use anima_engine::math::Quaternion;
    /// let q = Quaternion::new(0.0, 1.0, 0.0, 0.0);
    /// let v = Vector::new(1.0, 0.0, 0.0);
    ///
    /// assert_eq!(v.rot(q), Vector { x: -1.0, y: 0.0, z: 0.0 });
    /// ```
    pub fn rot(&self, quaternion: Quaternion) -> Vector {
        let conjugate = quaternion.conj();
        let result = quaternion *
                     Quaternion::new(self.x, self.y, self.z, 0.0) *
                     conjugate;

        Vector { x: result.x, y: result.y, z: result.z }
    }

    /// Rotates a vector according to the rotation represented by the quaternion around a point.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// # use anima_engine::math::Quaternion;
    /// let q = Quaternion::new(0.0, 1.0, 0.0, 0.0);
    /// let v = Vector::new(1.0, 0.0, 0.0);
    /// let p = Vector::new(2.0, 0.0, 0.0);
    ///
    /// assert_eq!(v.rot_around(q, p), Vector { x: 3.0, y: 0.0, z: 0.0 });
    /// ```
    pub fn rot_around(self, quaternion: Quaternion, point: Vector) -> Vector {
        (self - point).rot(quaternion) + point
    }

    /// Computes the angle in radians between two vectors.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// # use std::f32::consts;
    /// let v1 = Vector::new(1.0, 0.0, 0.0);
    /// let v2 = Vector::new(0.0, 2.0, 0.0);
    ///
    /// assert_eq!(v1.angle(v2), consts::PI / 2.0);
    /// ```
    pub fn angle(&self, other: Vector) -> f32 {
        self.norm().dot(other.norm()).acos()
    }

    /// Computes the distance between two vectors.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// let v1 = Vector::new(0.0, 0.0, 0.0);
    /// let v2 = Vector::new(0.0, 1.0, 0.0);
    ///
    /// assert_eq!(v1.dist(v2), 1.0);
    /// ```
    pub fn dist(self, other: Vector) -> f32 {
        (self - other).len()
    }
}

use std::ops::Add;
use std::ops::Sub;
use std::ops::Mul;
use std::ops::Neg;
use std::cmp::Ordering;
use math::Interpolate;

impl Add for Vector {
    type Output = Vector;

    fn add(self, other: Vector) -> Vector {
        Vector {
            x: self.x + other.x,
            y: self.y + other.y,
            z: self.z + other.z
        }
    }
}

impl Sub for Vector {
    type Output = Vector;

    fn sub(self, other: Vector) -> Vector {
        Vector {
            x: self.x - other.x,
            y: self.y - other.y,
            z: self.z - other.z
        }
    }
}

impl Mul<Vector> for Vector {
    type Output = Vector;

    fn mul(self, other: Vector) -> Vector {
        Vector {
            x: self.x * other.x,
            y: self.y * other.y,
            z: self.z * other.z
        }
    }
}

impl Mul<f32> for Vector {
    type Output = Vector;

    fn mul(self, scalar: f32) -> Vector {
        Vector {
            x: self.x * scalar,
            y: self.y * scalar,
            z: self.z * scalar
        }
    }
}

impl Mul<Vector> for f32 {
    type Output = Vector;

    fn mul(self, vector: Vector) -> Vector {
        Vector {
            x: vector.x * self,
            y: vector.y * self,
            z: vector.z * self
        }
    }
}

impl Neg for Vector {
    type Output = Vector;

    fn neg(self) -> Vector {
        Vector {
            x: -self.x,
            y: -self.y,
            z: -self.z
        }
    }
}

impl PartialOrd for Vector {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        self.len().partial_cmp(&other.len())
    }
}

impl Interpolate for Vector {
    fn interpolate(&self, other: Vector, ratio: f32) -> Vector {
        Vector {
            x: self.x * (1.0 - ratio) + other.x * ratio,
            y: self.y * (1.0 - ratio) + other.y * ratio,
            z: self.z * (1.0 - ratio) + other.z * ratio
        }
    }
}
<commit_msg>Added basic Vector creation and interrogation API.<commit_after>// Anima Engine. The quirky game engine
// Copyright (C) 2016  Dragoș Tiselice
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

use math::Quaternion;

/// A simple vector `struct` tailored specifically for graphics.
///
/// # Examples
///
/// ```
/// # use anima_engine::math::Vector;
/// let v1 = Vector::zero();
/// let v2 = Vector::one();
///
/// assert_eq!(v1 + v2, Vector::one());
/// assert_eq!(v1 * v2, Vector::zero());
/// assert_eq!(v1.dot(v2), 0.0);
///
/// let v3 = v1;
///
/// assert_eq!(v1.dot(v2), 0.0);
/// assert_eq!((v3 + Vector::one() * 2.0).dot(v2), 6.0);
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Vector {
    /// `f32` *x* coordinate value
    pub x: f32,
    /// `f32` *y* coordinate value
    pub y: f32,
    /// `f32` *z* coordinate value
    pub z: f32
}

impl Vector {
    /// Creates a vector using 3 values.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// let v = Vector::new(0.0, 1.0, 2.0);
    ///
    /// assert_eq!(v, Vector { x: 0.0, y: 1.0, z: 2.0 });
    /// ```
    pub fn new(x: f32, y: f32, z: f32) -> Vector {
        Vector { x: x, y: y, z: z }
    }

    /// Creates a vector using an array.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// let v = Vector::new_arr([0.0, 1.0, 2.0]);
    ///
    /// assert_eq!(v, Vector { x: 0.0, y: 1.0, z: 2.0 });
    /// ```
    pub fn new_arr(array: [f32; 3]) -> Vector {
        Vector { x: array[0], y: array[1], z: array[2] }
    }

    /// Creates a uniform vector using 1 value.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// let v = Vector::new_unf(1.0);
    ///
    /// assert_eq!(v, Vector { x: 1.0, y: 1.0, z: 1.0 });
    /// ```
    pub fn new_unf(v: f32) -> Vector {
        Vector { x: v, y: v, z: v }
    }

    /// Creates a zero (0.0, 0.0, 0.0) Vector.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// assert_eq!(Vector::zero(), Vector { x: 0.0, y: 0.0, z: 0.0 });
    /// ```
    pub fn zero() -> Vector {
        Vector { x: 0.0, y: 0.0, z: 0.0 }
    }

    /// Creates a one (1.0, 1.0, 1.0) Vector.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// assert_eq!(Vector::one(), Vector { x: 1.0, y: 1.0, z: 1.0 });
    /// ```
    pub fn one() -> Vector {
        Vector { x: 1.0, y: 1.0, z: 1.0 }
    }

    /// Creates a back (0.0, 0.0, -1.0) Vector.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// assert_eq!(Vector::back(), Vector { x: 0.0, y: 0.0, z: -1.0 });
    /// ```
    pub fn back() -> Vector {
        Vector { x: 0.0, y: 0.0, z: -1.0 }
    }

    /// Creates a down (0.0, -1.0, 0.0) Vector.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// assert_eq!(Vector::down(), Vector { x: 0.0, y: -1.0, z: 0.0 });
    /// ```
    pub fn down() -> Vector {
        Vector { x: 0.0, y: -1.0, z: 0.0 }
    }

    /// Creates a forward (0.0, 0.0, 1.0) Vector.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// assert_eq!(Vector::forward(), Vector { x: 0.0, y: 0.0, z: 1.0 });
    /// ```
    pub fn forward() -> Vector {
        Vector { x: 0.0, y: 0.0, z: 1.0 }
    }

    /// Creates a left (-1.0, 0.0, 0.0) Vector.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// assert_eq!(Vector::left(), Vector { x: 1.0, y: 0.0, z: 0.0 });
    /// ```
    pub fn left() -> Vector {
        Vector { x: 1.0, y: 0.0, z: 0.0 }
    }

    /// Creates a right (1.0, 0.0, 0.0) Vector.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// assert_eq!(Vector::right(), Vector { x: -1.0, y: 0.0, z: 0.0 });
    /// ```
    pub fn right() -> Vector {
        Vector { x: -1.0, y: 0.0, z: 0.0 }
    }

    /// Creates an up (0.0, 1.0, 0.0) Vector.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// assert_eq!(Vector::up(), Vector { x: 0.0, y: 1.0, z: 0.0 });
    /// ```
    pub fn up() -> Vector {
        Vector { x: 0.0, y: 1.0, z: 0.0 }
    }

    /// Computes the length of a vector.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// let v = Vector::new(1.0, 2.0, 2.0);
    ///
    /// assert_eq!(v.len(), 3.0);
    /// ```
    pub fn len(&self) -> f32 {
        (self.x.powi(2) + self.y.powi(2) + self.z.powi(2)).sqrt()
    }

    /// Computes the normalized version of a vector.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// let v = Vector::new(1.0, 2.0, 2.0);
    /// let n = v.norm();
    ///
    /// assert_eq!(n.len(), 1.0); // Keep precision in mind when comparing floats.
    /// ```
    pub fn norm(&self) -> Vector {
        let length = self.len();

        Vector {
            x: self.x / length,
            y: self.y / length,
            z: self.z / length
        }
    }

    /// Computes the dot product between two vectors.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// let v1 = Vector::new(1.0, 2.0, 2.0);
    /// let v2 = Vector::new(3.0, 3.0, 1.0);
    ///
    /// assert_eq!(v1.dot(v2), 11.0);
    /// ```
    pub fn dot(&self, other: Vector) -> f32 {
        self.x * other.x +
        self.y * other.y +
        self.z * other.z
    }

    /// Computes the cross product between two vectors.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// let v1 = Vector::new(1.0, 2.0, 2.0);
    /// let v2 = Vector::new(3.0, 3.0, 1.0);
    ///
    /// assert_eq!(v1.cross(v2), Vector { x: -4.0, y: 5.0, z: -3.0 });
    /// ```
    pub fn cross(&self, other: Vector) -> Vector {
        Vector {
            x: self.y * other.z - self.z * other.y,
            y: self.z * other.x - self.x * other.z,
            z: self.x * other.y - self.y * other.x
        }
    }

    /// Rotates a vector according to the rotation represented by a quaternion.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// # use anima_engine::math::Quaternion;
    /// let q = Quaternion::new(0.0, 1.0, 0.0, 0.0);
    /// let v = Vector::new(1.0, 0.0, 0.0);
    ///
    /// assert_eq!(v.rot(q), Vector { x: -1.0, y: 0.0, z: 0.0 });
    /// ```
    pub fn rot(&self, quaternion: Quaternion) -> Vector {
        let conjugate = quaternion.conj();
        let result = quaternion *
                     Quaternion::new(self.x, self.y, self.z, 0.0) *
                     conjugate;

        Vector { x: result.x, y: result.y, z: result.z }
    }

    /// Rotates a vector according to the rotation represented by the quaternion around a point.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// # use anima_engine::math::Quaternion;
    /// let q = Quaternion::new(0.0, 1.0, 0.0, 0.0);
    /// let v = Vector::new(1.0, 0.0, 0.0);
    /// let p = Vector::new(2.0, 0.0, 0.0);
    ///
    /// assert_eq!(v.rot_around(q, p), Vector { x: 3.0, y: 0.0, z: 0.0 });
    /// ```
    pub fn rot_around(self, quaternion: Quaternion, point: Vector) -> Vector {
        (self - point).rot(quaternion) + point
    }

    /// Computes the angle in radians between two vectors.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// # use std::f32::consts;
    /// let v1 = Vector::new(1.0, 0.0, 0.0);
    /// let v2 = Vector::new(0.0, 2.0, 0.0);
    ///
    /// assert_eq!(v1.angle(v2), consts::PI / 2.0);
    /// ```
    pub fn angle(&self, other: Vector) -> f32 {
        self.norm().dot(other.norm()).acos()
    }

    /// Computes the distance between two vectors.
    ///
    /// # Examples
    ///
    /// ```
    /// # use anima_engine::math::Vector;
    /// let v1 = Vector::new(0.0, 0.0, 0.0);
    /// let v2 = Vector::new(0.0, 1.0, 0.0);
    ///
    /// assert_eq!(v1.dist(v2), 1.0);
    /// ```
    pub fn dist(self, other: Vector) -> f32 {
        (self - other).len()
    }
}

use std::ops::Add;
use std::ops::Sub;
use std::ops::Mul;
use std::ops::Neg;
use std::cmp::Ordering;
use math::Interpolate;

use mrusty::*;

impl Add for Vector {
    type Output = Vector;

    fn add(self, other: Vector) -> Vector {
        Vector {
            x: self.x + other.x,
            y: self.y + other.y,
            z: self.z + other.z
        }
    }
}

impl Sub for Vector {
    type Output = Vector;

    fn sub(self, other: Vector) -> Vector {
        Vector {
            x: self.x - other.x,
            y: self.y - other.y,
            z: self.z - other.z
        }
    }
}

impl Mul<Vector> for Vector {
    type Output = Vector;

    fn mul(self, other: Vector) -> Vector {
        Vector {
            x: self.x * other.x,
            y: self.y * other.y,
            z: self.z * other.z
        }
    }
}

impl Mul<f32> for Vector {
    type Output = Vector;

    fn mul(self, scalar: f32) -> Vector {
        Vector {
            x: self.x * scalar,
            y: self.y * scalar,
            z: self.z * scalar
        }
    }
}

impl Mul<Vector> for f32 {
    type Output = Vector;

    fn mul(self, vector: Vector) -> Vector {
        Vector {
            x: vector.x * self,
            y: vector.y * self,
            z: vector.z * self
        }
    }
}

impl Neg for Vector {
    type Output = Vector;

    fn neg(self) -> Vector {
        Vector {
            x: -self.x,
            y: -self.y,
            z: -self.z
        }
    }
}

impl PartialOrd for Vector {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        self.len().partial_cmp(&other.len())
    }
}

impl Interpolate for Vector {
    fn interpolate(&self, other: Vector, ratio: f32) -> Vector {
        Vector {
            x: self.x * (1.0 - ratio) + other.x * ratio,
            y: self.y * (1.0 - ratio) + other.y * ratio,
            z: self.z * (1.0 - ratio) + other.z * ratio
        }
    }
}

impl MRubyFile for Vector {
    fn require(mruby: MRubyType) {
        mruby.def_class::<Vector>("Vector");

        mruby.def_method::<Vector, _>("initialize", mrfn!(|_mruby, slf: Value,
                                                          x: f64, y: f64, z: f64| {
            let vector = Vector::new(x as f32, y as f32, z as f32);

            slf.init(vector)
        }));

        mruby.def_class_method::<Vector, _>("from_a", mrfn!(|mruby, _slf: Value, array: Vec| {
            let x = array[0].to_f64().unwrap();
            let y = array[1].to_f64().unwrap();
            let z = array[2].to_f64().unwrap();

            let vector = Vector::new(x as f32, y as f32, z as f32);

            mruby.obj(vector)
        }));

        mruby.def_method::<Vector, _>("x", mrfn!(|mruby, slf: Vector| {
            mruby.float(slf.x as f64)
        }));

        mruby.def_method::<Vector, _>("y", mrfn!(|mruby, slf: Vector| {
            mruby.float(slf.y as f64)
        }));

        mruby.def_method::<Vector, _>("z", mrfn!(|mruby, slf: Vector| {
            mruby.float(slf.z as f64)
        }));
    }
}

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

    use super::Vector;

    describe!(Vector, "
      context 'when unit' do
        subject { Vector.new 1.0, 1.0, 1.0 }

        it 'returns 1.0 on #x' do
          expect(subject.x).to eql 1.0
        end

        it 'returns 1.0 on #y' do
          expect(subject.y).to eql 1.0
        end

        it 'returns 1.0 on #z' do
          expect(subject.z).to eql 1.0
        end
      end
    ");
}
<|endoftext|>