<commit_before>use nalgebra::{Norm, Vector2, Point2};
use game::object::level::Point;
use game::object::level::action::Action;

// TODO: Write tests - this code is complicated and almost certaintly error prone

pub enum Path {
    Arc(Arc),
    Curve(Curve),
}

impl Path {
    pub fn travel(&mut self, dt: f32) -> Option<Vector2<f32>> {
        match self {
            &mut Path::Arc(ref mut a) => a.travel(dt),
            &mut Path::Curve(ref mut c) => c.travel(dt),
            }
        }

    pub fn finished(&self) -> bool {
        match self {
            &Path::Arc(ref a) => a.degrees <= 0.0,
            &Path::Curve(ref c) => c.points.len() == 1,
        }
    }
}

pub enum RotationDirection {
    Clockwise,
    CounterClockwise
}

pub struct Arc {
    center: Vector2<f32>,
    current_pos: Vector2<f32>,
    radius: f32,
    degrees: f32,
    speed: f32,
    direction: RotationDirection,
    actions: Vec<Action>,
}

impl Arc {
    fn travel(&mut self, dt: f32) -> Option<Vector2<f32>> {
        use std::f32::consts::PI;
        use nalgebra::angle_between;

        if self.degrees > 0.0 {
            let dist = self.speed * dt;
            let circ = 2.0 * self.radius * PI;
            let ang = 360.0 * dist/circ;
            // Handle the case where the angle greatly surpasses degrees left?
            let mut c_ang = angle_between(&Vector2::new(1.0, 0.0), &(self.current_pos - self.center)).to_degrees();
            self.degrees -= ang;
            match self.direction {
                RotationDirection::Clockwise => {
                    c_ang -= ang
                }
                RotationDirection::CounterClockwise => {
                    c_ang += ang
                }
            };
            let dp = Vector2::new(self.radius * c_ang.to_radians().cos(), self.radius * c_ang.to_radians().sin());
            self.current_pos =  dp + self.center;
            Some(self.current_pos)
        } else {
            None
        }
    }
}

pub struct Curve {
    points: Vec<Point2<f32>>,
    current_pos: Vector2<f32>,
    node_dist_left: f32,
    speed: f32,
    actions: Vec<Action>,
}

impl Curve {
    fn travel(&mut self, dt: f32) -> Option<Vector2<f32>> {
        if self.points.len() > 2 {
            let mut dist = self.speed * dt;
            if dist > self.node_dist_left && self.points.len() > 2 {
                // Advance to the next point and then recall the method with reduced dt so that hopefully case 3 is used.
                dist -= self.node_dist_left;
                self.points.remove(0);

                let dp = self.points[1] - self.points[0];
                self.node_dist_left = dp.norm();
                self.current_pos = *self.points[0].as_vector();
                let time_left = dist/self.speed;
                self.travel(time_left)
            } else if dist > self.node_dist_left && self.points.len() == 2 {
                // Return the final point if we finish up travelling to it
                // Do we care that we may have exceeded the final point?
                // Theoretically dt is always small enough that it'll never matter
                self.current_pos = self.points.remove(0).to_vector();
                Some(self.current_pos)
            } else {
                // Reduce node dist left and extend the current pos vector proportional to distance travelled across the vector between the prev point and next point
                self.node_dist_left -= dist;
                let dp = self.points[1] - self.points[0];
                let dt = dp * dist/dp.norm();
                self.current_pos += dt;
                Some(self.current_pos)
            }
        } else {
            None
        }
    }
}

pub struct PathBuilder {
    speed: Option<f32>,
    // Arc info
    center: Option<Point>,
    radius: Option<f32>,
    degrees: Option<f32>,
    direction: Option<RotationDirection>,
    // Curve info
    points: Option<Vec<Point>>,
    actions: Vec<Action>,
}

impl PathBuilder {
    pub fn new() -> PathBuilder {
        PathBuilder {
            speed: None,
            center: None,
            radius: None,
            degrees: None,
            direction: None,
            points: None,
            actions: vec![]
        }
    }

    pub fn speed(mut self, speed: f32) -> PathBuilder {
        self.speed = Some(speed);
        self
    }

    pub fn actions(mut self, mut actions: Vec<Action>) -> PathBuilder {
        self.actions.append(&mut actions);
        self
    }

    pub fn center(mut self, center: Point) -> PathBuilder {
        self.center = Some(center);
        self
    }

    pub fn radius(mut self, radius: f32) -> PathBuilder {
        self.radius = Some(radius);
        self
    }

    pub fn degrees(mut self, degrees: f32) -> PathBuilder {
        self.degrees = Some(degrees);
        self
    }

    pub fn direction(mut self, direction: RotationDirection) -> PathBuilder {
        self.direction = Some(direction);
        self
    }

    pub fn build_arc(self, current_pos: &Vector2<f32>, player_pos: &Vector2<f32>) -> Path {
        let center = self.center.unwrap().eval(current_pos, player_pos);
        Path::Arc(Arc {
            center: center,
            current_pos: *current_pos,
            radius: self.radius.unwrap(),
            degrees: self.degrees.unwrap(),
            speed: self.speed.unwrap(),
            direction: self.direction.unwrap(),
            actions: self.actions,
        })
    }

    pub fn points(mut self, points: Vec<Point>) -> PathBuilder {
        self.points = Some(points);
        self
    }

    pub fn build_curve(self, current_pos: &Vector2<f32>, player_pos: &Vector2<f32>) -> Path {
        use ncollide::procedural::bezier_curve;

        let points: Vec<_> = self.points.unwrap().iter().map(|point| {
            point.eval(current_pos, player_pos).to_point()
        }).collect();
        let (points, _) = bezier_curve(&points[..], 100).unwrap();

        Path::Curve(Curve {
            points: points,
            current_pos: *current_pos,
            node_dist_left: 0.0,
            speed: self.speed.unwrap(),
            actions: self.actions,
        })
    }
}
<commit_msg>Add action extracting function to path<commit_after>use nalgebra::{Norm, Vector2, Point2};
use game::object::level::Point;
use game::object::level::action::Action;

// TODO: Write tests - this code is complicated and almost certaintly error prone

pub enum Path {
    Arc(Arc),
    Curve(Curve),
}

impl Path {
    pub fn travel(&mut self, dt: f32) -> Option<Vector2<f32>> {
        match self {
            &mut Path::Arc(ref mut a) => a.travel(dt),
            &mut Path::Curve(ref mut c) => c.travel(dt),
            }
        }

    pub fn finished(&self) -> bool {
        match self {
            &Path::Arc(ref a) => a.degrees <= 0.0,
            &Path::Curve(ref c) => c.points.len() == 1,
        }
    }

    pub fn actions(&mut self) -> Vec<Action> {
        use std::mem;
        match self {
            &mut Path::Arc(ref mut a) => mem::replace(&mut a.actions, Vec::new()),
            &mut Path::Curve(ref mut c) => mem::replace(&mut c.actions, Vec::new()),
        }
    }
}

pub enum RotationDirection {
    Clockwise,
    CounterClockwise
}

pub struct Arc {
    center: Vector2<f32>,
    current_pos: Vector2<f32>,
    radius: f32,
    degrees: f32,
    speed: f32,
    direction: RotationDirection,
    actions: Vec<Action>,
}

impl Arc {
    fn travel(&mut self, dt: f32) -> Option<Vector2<f32>> {
        use std::f32::consts::PI;
        use nalgebra::angle_between;

        if self.degrees > 0.0 {
            let dist = self.speed * dt;
            let circ = 2.0 * self.radius * PI;
            let ang = 360.0 * dist/circ;
            // Handle the case where the angle greatly surpasses degrees left?
            let mut c_ang = angle_between(&Vector2::new(1.0, 0.0), &(self.current_pos - self.center)).to_degrees();
            self.degrees -= ang;
            match self.direction {
                RotationDirection::Clockwise => {
                    c_ang -= ang
                }
                RotationDirection::CounterClockwise => {
                    c_ang += ang
                }
            };
            let dp = Vector2::new(self.radius * c_ang.to_radians().cos(), self.radius * c_ang.to_radians().sin());
            self.current_pos =  dp + self.center;
            Some(self.current_pos)
        } else {
            None
        }
    }
}

pub struct Curve {
    points: Vec<Point2<f32>>,
    current_pos: Vector2<f32>,
    node_dist_left: f32,
    speed: f32,
    actions: Vec<Action>,
}

impl Curve {
    fn travel(&mut self, dt: f32) -> Option<Vector2<f32>> {
        if self.points.len() > 2 {
            let mut dist = self.speed * dt;
            if dist > self.node_dist_left && self.points.len() > 2 {
                // Advance to the next point and then recall the method with reduced dt so that hopefully case 3 is used.
                dist -= self.node_dist_left;
                self.points.remove(0);

                let dp = self.points[1] - self.points[0];
                self.node_dist_left = dp.norm();
                self.current_pos = *self.points[0].as_vector();
                let time_left = dist/self.speed;
                self.travel(time_left)
            } else if dist > self.node_dist_left && self.points.len() == 2 {
                // Return the final point if we finish up travelling to it
                // Do we care that we may have exceeded the final point?
                // Theoretically dt is always small enough that it'll never matter
                self.current_pos = self.points.remove(0).to_vector();
                Some(self.current_pos)
            } else {
                // Reduce node dist left and extend the current pos vector proportional to distance travelled across the vector between the prev point and next point
                self.node_dist_left -= dist;
                let dp = self.points[1] - self.points[0];
                let dt = dp * dist/dp.norm();
                self.current_pos += dt;
                Some(self.current_pos)
            }
        } else {
            None
        }
    }
}

pub struct PathBuilder {
    speed: Option<f32>,
    // Arc info
    center: Option<Point>,
    radius: Option<f32>,
    degrees: Option<f32>,
    direction: Option<RotationDirection>,
    // Curve info
    points: Option<Vec<Point>>,
    actions: Vec<Action>,
}

impl PathBuilder {
    pub fn new() -> PathBuilder {
        PathBuilder {
            speed: None,
            center: None,
            radius: None,
            degrees: None,
            direction: None,
            points: None,
            actions: vec![]
        }
    }

    pub fn speed(mut self, speed: f32) -> PathBuilder {
        self.speed = Some(speed);
        self
    }

    pub fn actions(mut self, mut actions: Vec<Action>) -> PathBuilder {
        self.actions.append(&mut actions);
        self
    }

    pub fn center(mut self, center: Point) -> PathBuilder {
        self.center = Some(center);
        self
    }

    pub fn radius(mut self, radius: f32) -> PathBuilder {
        self.radius = Some(radius);
        self
    }

    pub fn degrees(mut self, degrees: f32) -> PathBuilder {
        self.degrees = Some(degrees);
        self
    }

    pub fn direction(mut self, direction: RotationDirection) -> PathBuilder {
        self.direction = Some(direction);
        self
    }

    pub fn build_arc(self, current_pos: &Vector2<f32>, player_pos: &Vector2<f32>) -> Path {
        let center = self.center.unwrap().eval(current_pos, player_pos);
        Path::Arc(Arc {
            center: center,
            current_pos: *current_pos,
            radius: self.radius.unwrap(),
            degrees: self.degrees.unwrap(),
            speed: self.speed.unwrap(),
            direction: self.direction.unwrap(),
            actions: self.actions,
        })
    }

    pub fn points(mut self, points: Vec<Point>) -> PathBuilder {
        self.points = Some(points);
        self
    }

    pub fn build_curve(self, current_pos: &Vector2<f32>, player_pos: &Vector2<f32>) -> Path {
        use ncollide::procedural::bezier_curve;

        let points: Vec<_> = self.points.unwrap().iter().map(|point| {
            point.eval(current_pos, player_pos).to_point()
        }).collect();
        let (points, _) = bezier_curve(&points[..], 100).unwrap();

        Path::Curve(Curve {
            points: points,
            current_pos: *current_pos,
            node_dist_left: 0.0,
            speed: self.speed.unwrap(),
            actions: self.actions,
        })
    }
}
<|endoftext|>


<commit_before>extern crate dla;
extern crate pcg;
extern crate rand;

use pcg::PcgRng;
use rand::SeedableRng;
use dla::{simulate_dla, Pruning};

fn main() {
    let mut rng: PcgRng = SeedableRng::from_seed([0, 0]);
    const W: u32 = 400;
    const H: u32 = 180;
    const N: u32 = 20_000;

    const N_IN: u32 = 2;
    const N_OUT: u32 = 3;
    const SEED_HALF_WIDTH: u32 = 2;
    const SEED_HEIGHT: u32 = 2;

    let mut seeds = Vec::new();

    for i in 0..N_IN {
        for y in 0..SEED_HEIGHT {
            for x in 0..SEED_HALF_WIDTH {
                seeds.push(((i + 1) * W / (N_IN + 1) + x, y));
                seeds.push(((i + 1) * W / (N_IN + 1) - x, y));
            }
        }
    }

    for i in 0..N_OUT {
        for y in 0..SEED_HEIGHT {
            for x in 0..SEED_HALF_WIDTH {
                seeds.push(((i + 1) * W / (N_OUT + 1) + x, H - 1 - y));
                seeds.push(((i + 1) * W / (N_OUT + 1) - x, H - 1 - y));
            }
        }
    }

    let pruning = Some(Pruning {
        probability: 0.5,
        every: 10,
        age: 40,
    });

    let colors = [(0, 0, 0), (255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 0, 255), (0, 255, 255)];
    simulate_dla(&mut rng,
                 W,
                 H,
                 N,
                 &seeds,
                 &colors,
                 2000,
                 pruning,
                 500,
                 "dla_pruning_23");
}
<commit_msg>Change config<commit_after>extern crate dla;
extern crate pcg;
extern crate rand;

use pcg::PcgRng;
use rand::SeedableRng;
use dla::{simulate_dla, Pruning};

fn main() {
    let mut rng: PcgRng = SeedableRng::from_seed([0, 0]);
    const W: u32 = 400;
    const H: u32 = 180;
    const N: u32 = 50_000;

    const N_IN: u32 = 2;
    const N_OUT: u32 = 3;
    const SEED_HALF_WIDTH: u32 = 2;
    const SEED_HEIGHT: u32 = 2;

    let mut seeds = Vec::new();

    for i in 0..N_IN {
        for y in 0..SEED_HEIGHT {
            for x in 0..SEED_HALF_WIDTH {
                seeds.push(((i + 1) * W / (N_IN + 1) + x, y));
                seeds.push(((i + 1) * W / (N_IN + 1) - x, y));
            }
        }
    }

    for i in 0..N_OUT {
        for y in 0..SEED_HEIGHT {
            for x in 0..SEED_HALF_WIDTH {
                seeds.push(((i + 1) * W / (N_OUT + 1) + x, H - 1 - y));
                seeds.push(((i + 1) * W / (N_OUT + 1) - x, H - 1 - y));
            }
        }
    }

    let pruning = Some(Pruning {
        probability: 0.4,
        every: 10,
        age: 100,
    });

    let colors = [(0, 0, 0), (255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 0, 255), (0, 255, 255)];
    simulate_dla(&mut rng,
                 W,
                 H,
                 N,
                 &seeds,
                 &colors,
                 2000,
                 pruning,
                 500,
                 "dla_pruning_23");
}
<|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;

/// `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<String>,
    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 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;

        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 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;
                }
            };
            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<String>>,
}

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, .. }) => {
                    // 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>Sort cargo metadata<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;

/// `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<String>,
    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<String>>,
}

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, .. }) => {
                    // 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>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Servo's compiler plugin/macro crate
//!
//! Attributes this crate provides:
//!
//!  - `#[privatize]` : Forces all fields in a struct/enum to be private
//!  - `#[derive(JSTraceable)]` : Auto-derives an implementation of `JSTraceable` for a struct in the script crate
//!  - `#[must_root]` : Prevents data of the marked type from being used on the stack.
//!                     See the lints module for more details
//!  - `#[dom_struct]` : Implies `#[privatize]`,`#[derive(JSTraceable)]`, and `#[must_root]`.
//!                       Use this for structs that correspond to a DOM type

#![feature(plugin_registrar, quote, plugin, box_syntax, rustc_private)]

#[macro_use]
extern crate syntax;
#[macro_use]
extern crate rustc;

extern crate rustc_front;

extern crate tenacious;
#[cfg(feature = "clippy")]
extern crate clippy;

use rustc::plugin::Registry;
use syntax::ext::base::*;
use syntax::feature_gate::AttributeType::Whitelisted;
use syntax::parse::token::intern;

// Public for documentation to show up
/// Handles the auto-deriving for `#[derive(JSTraceable)]`
pub mod jstraceable;
/// Handles the auto-deriving for `#[derive(HeapSizeOf)]`
pub mod heap_size;
/// Autogenerates implementations of Reflectable on DOM structs
pub mod lints;
pub mod reflector;
/// Utilities for writing plugins
pub mod casing;
pub mod utils;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_syntax_extension(intern("dom_struct"), MultiModifier(box jstraceable::expand_dom_struct));
    reg.register_syntax_extension(intern("derive_JSTraceable"), MultiDecorator(box jstraceable::expand_jstraceable));
    reg.register_syntax_extension(intern("_generate_reflector"), MultiDecorator(box reflector::expand_reflector));
    reg.register_syntax_extension(intern("derive_HeapSizeOf"), MultiDecorator(box heap_size::expand_heap_size));
    reg.register_macro("to_lower", casing::expand_lower);
    reg.register_macro("to_upper", casing::expand_upper);
    reg.register_late_lint_pass(box lints::transmute_type::TransmutePass);
    reg.register_late_lint_pass(box lints::unrooted_must_root::UnrootedPass::new());
    reg.register_late_lint_pass(box lints::privatize::PrivatizePass);
    reg.register_late_lint_pass(box lints::inheritance_integrity::InheritancePass);
    reg.register_early_lint_pass(box lints::ban::BanPass);
    reg.register_late_lint_pass(box tenacious::TenaciousPass);
    reg.register_attribute("must_root".to_string(), Whitelisted);
    reg.register_attribute("servo_lang".to_string(), Whitelisted);
    reg.register_attribute("allow_unrooted_interior".to_string(), Whitelisted);
    register_clippy(reg);
}

#[cfg(feature = "clippy")]
fn register_clippy(reg: &mut Registry) {
    ::clippy::plugin_registrar(reg);
}
#[cfg(not(feature = "clippy"))]
fn register_clippy(reg: &mut Registry) {
    reg.register_late_lint_pass(box lints::str_to_string::StrToStringPass);
}
<commit_msg>Fix misplaced doc comment in plugins/lib.rs<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Servo's compiler plugin/macro crate
//!
//! Attributes this crate provides:
//!
//!  - `#[privatize]` : Forces all fields in a struct/enum to be private
//!  - `#[derive(JSTraceable)]` : Auto-derives an implementation of `JSTraceable` for a struct in the script crate
//!  - `#[must_root]` : Prevents data of the marked type from being used on the stack.
//!                     See the lints module for more details
//!  - `#[dom_struct]` : Implies `#[privatize]`,`#[derive(JSTraceable)]`, and `#[must_root]`.
//!                       Use this for structs that correspond to a DOM type

#![feature(plugin_registrar, quote, plugin, box_syntax, rustc_private)]

#[macro_use]
extern crate syntax;
#[macro_use]
extern crate rustc;

extern crate rustc_front;

extern crate tenacious;
#[cfg(feature = "clippy")]
extern crate clippy;

use rustc::plugin::Registry;
use syntax::ext::base::*;
use syntax::feature_gate::AttributeType::Whitelisted;
use syntax::parse::token::intern;

// Public for documentation to show up
/// Handles the auto-deriving for `#[derive(JSTraceable)]`
pub mod jstraceable;
/// Handles the auto-deriving for `#[derive(HeapSizeOf)]`
pub mod heap_size;
pub mod lints;
/// Autogenerates implementations of Reflectable on DOM structs
pub mod reflector;
/// Utilities for writing plugins
pub mod casing;
pub mod utils;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_syntax_extension(intern("dom_struct"), MultiModifier(box jstraceable::expand_dom_struct));
    reg.register_syntax_extension(intern("derive_JSTraceable"), MultiDecorator(box jstraceable::expand_jstraceable));
    reg.register_syntax_extension(intern("_generate_reflector"), MultiDecorator(box reflector::expand_reflector));
    reg.register_syntax_extension(intern("derive_HeapSizeOf"), MultiDecorator(box heap_size::expand_heap_size));
    reg.register_macro("to_lower", casing::expand_lower);
    reg.register_macro("to_upper", casing::expand_upper);
    reg.register_late_lint_pass(box lints::transmute_type::TransmutePass);
    reg.register_late_lint_pass(box lints::unrooted_must_root::UnrootedPass::new());
    reg.register_late_lint_pass(box lints::privatize::PrivatizePass);
    reg.register_late_lint_pass(box lints::inheritance_integrity::InheritancePass);
    reg.register_early_lint_pass(box lints::ban::BanPass);
    reg.register_late_lint_pass(box tenacious::TenaciousPass);
    reg.register_attribute("must_root".to_string(), Whitelisted);
    reg.register_attribute("servo_lang".to_string(), Whitelisted);
    reg.register_attribute("allow_unrooted_interior".to_string(), Whitelisted);
    register_clippy(reg);
}

#[cfg(feature = "clippy")]
fn register_clippy(reg: &mut Registry) {
    ::clippy::plugin_registrar(reg);
}
#[cfg(not(feature = "clippy"))]
fn register_clippy(reg: &mut Registry) {
    reg.register_late_lint_pass(box lints::str_to_string::StrToStringPass);
}
<|endoftext|>


<commit_before>use na::{DefaultAllocator};

use aliases::TVec;
use traits::{Number, Alloc, Dimension};

/// Checks that all the vector components are `true`.
///
/// # Examples:
///
/// ```
/// # use nalgebra_glm as glm;
/// let vec = glm::vec2(true, false);
/// assert_eq!(false, glm::all(&vec));
///
/// let vec = glm::vec2(true, true);
/// assert_eq!(true, glm::all(&vec));
/// ```
///
/// # See also:
///
/// * [`any`](fn.any.html)
/// * [`not`](fn.not.html)
pub fn all<D: Dimension>(v: &TVec<bool, D>) -> bool
    where DefaultAllocator: Alloc<bool, D> {
    v.iter().all(|x| *x)
}

/// Checks that at least one of the vector components is `true`.
///
/// # Examples:
///
/// ```
/// # use nalgebra_glm as glm;
/// let vec = glm::vec2(true, false);
/// assert_eq!(true, glm::any(&vec));
///
/// let vec = glm::vec2(true, true);
/// assert_eq!(true, glm::any(&vec));
///
/// let vec = glm::vec2(false, false);
/// assert_eq!(false, glm::any(&vec));
/// ```
///
/// # See also:
///
/// * [`all`](fn.all.html)
/// * [`not`](fn.not.html)
pub fn any<D: Dimension>(v: &TVec<bool, D>) -> bool
    where DefaultAllocator: Alloc<bool, D> {
    v.iter().any(|x| *x)
}

/// Component-wise equality comparison.
///
/// # See also:
///
/// * [`equal`](fn.equal.html)
/// * [`greater_than`](fn.greater_than.html)
/// * [`greater_than_equal`](fn.greater_than_equal.html)
/// * [`less_than`](fn.less_than.html)
/// * [`less_than_equal`](fn.less_than_equal.html)
/// * [`not`](fn.not.html)
/// * [`not_equal`](fn.not_equal.html)
pub fn equal<N: Number, D: Dimension>(x: &TVec<N, D>, y: &TVec<N, D>) -> TVec<bool, D>
    where DefaultAllocator: Alloc<N, D> {
    x.zip_map(y, |x, y| x == y)
}

/// Component-wise `>` comparison.
///
/// # See also:
///
/// * [`equal`](fn.equal.html)
/// * [`greater_than`](fn.greater_than.html)
/// * [`greater_than_equal`](fn.greater_than_equal.html)
/// * [`less_than`](fn.less_than.html)
/// * [`less_than_equal`](fn.less_than_equal.html)
/// * [`not`](fn.not.html)
/// * [`not_equal`](fn.not_equal.html)
pub fn greater_than<N: Number, D: Dimension>(x: &TVec<N, D>, y: &TVec<N, D>) -> TVec<bool, D>
    where DefaultAllocator: Alloc<N, D> {
    x.zip_map(y, |x, y| x > y)
}

/// Component-wise `>=` comparison.
///
/// # See also:
///
/// * [`equal`](fn.equal.html)
/// * [`greater_than`](fn.greater_than.html)
/// * [`greater_than_equal`](fn.greater_than_equal.html)
/// * [`less_than`](fn.less_than.html)
/// * [`less_than_equal`](fn.less_than_equal.html)
/// * [`not`](fn.not.html)
/// * [`not_equal`](fn.not_equal.html)
pub fn greater_than_equal<N: Number, D: Dimension>(x: &TVec<N, D>, y: &TVec<N, D>) -> TVec<bool, D>
    where DefaultAllocator: Alloc<N, D> {
    x.zip_map(y, |x, y| x >= y)
}

/// Component-wise `<` comparison.
///
/// # See also:
///
/// * [`equal`](fn.equal.html)
/// * [`greater_than`](fn.greater_than.html)
/// * [`greater_than_equal`](fn.greater_than_equal.html)
/// * [`less_than`](fn.less_than.html)
/// * [`less_than_equal`](fn.less_than_equal.html)
/// * [`not`](fn.not.html)
/// * [`not_equal`](fn.not_equal.html)
pub fn less_than<N: Number, D: Dimension>(x: &TVec<N, D>, y: &TVec<N, D>) -> TVec<bool, D>
    where DefaultAllocator: Alloc<N, D> {
    x.zip_map(y, |x, y| x < y)
}

/// Component-wise `>=` comparison.
///
/// # See also:
///
/// * [`equal`](fn.equal.html)
/// * [`greater_than`](fn.greater_than.html)
/// * [`greater_than_equal`](fn.greater_than_equal.html)
/// * [`less_than`](fn.less_than.html)
/// * [`less_than_equal`](fn.less_than_equal.html)
/// * [`not`](fn.not.html)
/// * [`not_equal`](fn.not_equal.html)
pub fn less_than_equal<N: Number, D: Dimension>(x: &TVec<N, D>, y: &TVec<N, D>) -> TVec<bool, D>
    where DefaultAllocator: Alloc<N, D> {
    x.zip_map(y, |x, y| x <= y)
}

/// Component-wise not `!`.
///
/// # Examples:
///
/// ```
/// # use nalgebra_glm as glm;
/// let vec = glm::vec2(true, false);
/// assert_eq!(glm::vec2(false, true), glm::not(&vec));
/// ```
///
/// # See also:
///
/// * [`all`](fn.all.html)
/// * [`any`](fn.any.html)
/// * [`equal`](fn.equal.html)
/// * [`greater_than`](fn.greater_than.html)
/// * [`greater_than_equal`](fn.greater_than_equal.html)
/// * [`less_than`](fn.less_than.html)
/// * [`less_than_equal`](fn.less_than_equal.html)
/// * [`not`](fn.not.html)
/// * [`not_equal`](fn.not_equal.html)
pub fn not<D: Dimension>(v: &TVec<bool, D>) -> TVec<bool, D>
    where DefaultAllocator: Alloc<bool, D> {
    v.map(|x| !x)
}

/// Component-wise not-equality `!=`.
///
/// # See also:
///
/// * [`equal`](fn.equal.html)
/// * [`greater_than`](fn.greater_than.html)
/// * [`greater_than_equal`](fn.greater_than_equal.html)
/// * [`less_than`](fn.less_than.html)
/// * [`less_than_equal`](fn.less_than_equal.html)
/// * [`not_equal`](fn.not_equal.html)
pub fn not_equal<N: Number, D: Dimension>(x: &TVec<N, D>, y: &TVec<N, D>) -> TVec<bool, D>
    where DefaultAllocator: Alloc<N, D> {
    x.zip_map(y, |x, y| x != y)
}
<commit_msg>Add more doc test and correct see also for vector relational functions.<commit_after>use na::{DefaultAllocator};

use aliases::TVec;
use traits::{Number, Alloc, Dimension};

/// Checks that all the vector components are `true`.
///
/// # Examples:
///
/// ```
/// # use nalgebra_glm as glm;
/// let vec = glm::vec2(true, false);
/// assert_eq!(false, glm::all(&vec));
///
/// let vec = glm::vec2(true, true);
/// assert_eq!(true, glm::all(&vec));
/// ```
///
/// # See also:
///
/// * [`any`](fn.any.html)
/// * [`not`](fn.not.html)
pub fn all<D: Dimension>(v: &TVec<bool, D>) -> bool
    where DefaultAllocator: Alloc<bool, D> {
    v.iter().all(|x| *x)
}

/// Checks that at least one of the vector components is `true`.
///
/// # Examples:
///
/// ```
/// # use nalgebra_glm as glm;
/// let vec = glm::vec2(true, false);
/// assert_eq!(true, glm::any(&vec));
///
/// let vec = glm::vec2(true, true);
/// assert_eq!(true, glm::any(&vec));
///
/// let vec = glm::vec2(false, false);
/// assert_eq!(false, glm::any(&vec));
/// ```
///
/// # See also:
///
/// * [`all`](fn.all.html)
/// * [`not`](fn.not.html)
pub fn any<D: Dimension>(v: &TVec<bool, D>) -> bool
    where DefaultAllocator: Alloc<bool, D> {
    v.iter().any(|x| *x)
}

/// Component-wise equality comparison.
///
/// # Examples:
///
/// ```
/// # use nalgebra_glm as glm;
/// assert_eq!(glm::vec2(true, false),
///            glm::equal(&glm::vec2(1.0, 3.0),
///                       &glm::vec2(1.0, 2.0)));
/// ```
///
/// # See also:
///
/// * [`greater_than`](fn.greater_than.html)
/// * [`greater_than_equal`](fn.greater_than_equal.html)
/// * [`less_than`](fn.less_than.html)
/// * [`less_than_equal`](fn.less_than_equal.html)
/// * [`not`](fn.not.html)
/// * [`not_equal`](fn.not_equal.html)
pub fn equal<N: Number, D: Dimension>(x: &TVec<N, D>, y: &TVec<N, D>) -> TVec<bool, D>
    where DefaultAllocator: Alloc<N, D> {
    x.zip_map(y, |x, y| x == y)
}

/// Component-wise `>` comparison.
///
/// # Examples:
///
/// ```
/// # use nalgebra_glm as glm;
/// assert_eq!(glm::vec2(false, true),
///            glm::greater_than(&glm::vec2(1.0, 3.0),
///                              &glm::vec2(1.0, 2.0)));
/// ```
///
/// # See also:
///
/// * [`equal`](fn.equal.html)
/// * [`greater_than_equal`](fn.greater_than_equal.html)
/// * [`less_than`](fn.less_than.html)
/// * [`less_than_equal`](fn.less_than_equal.html)
/// * [`not`](fn.not.html)
/// * [`not_equal`](fn.not_equal.html)
pub fn greater_than<N: Number, D: Dimension>(x: &TVec<N, D>, y: &TVec<N, D>) -> TVec<bool, D>
    where DefaultAllocator: Alloc<N, D> {
    x.zip_map(y, |x, y| x > y)
}

/// Component-wise `>=` comparison.
///
/// # Examples:
///
/// ```
/// # use nalgebra_glm as glm;
/// assert_eq!(glm::vec3(true, true, false),
///            glm::greater_than_equal(&glm::vec3(1.0, 3.0, 4.0),
///                                    &glm::vec3(1.0, 2.0, 5.0)));
/// ```
///
/// # See also:
///
/// * [`equal`](fn.equal.html)
/// * [`greater_than`](fn.greater_than.html)
/// * [`less_than`](fn.less_than.html)
/// * [`less_than_equal`](fn.less_than_equal.html)
/// * [`not`](fn.not.html)
/// * [`not_equal`](fn.not_equal.html)
pub fn greater_than_equal<N: Number, D: Dimension>(x: &TVec<N, D>, y: &TVec<N, D>) -> TVec<bool, D>
    where DefaultAllocator: Alloc<N, D> {
    x.zip_map(y, |x, y| x >= y)
}

/// Component-wise `<` comparison.
///
/// # Examples:
///
/// ```
/// # use nalgebra_glm as glm;
/// assert_eq!(glm::vec3(false, false, true),
///            glm::less_than(&glm::vec3(1.0, 3.0, 4.0),
///                           &glm::vec3(1.0, 2.0, 5.0)));
/// ```
///
/// # See also:
///
/// * [`equal`](fn.equal.html)
/// * [`greater_than`](fn.greater_than.html)
/// * [`greater_than_equal`](fn.greater_than_equal.html)
/// * [`less_than_equal`](fn.less_than_equal.html)
/// * [`not`](fn.not.html)
/// * [`not_equal`](fn.not_equal.html)
pub fn less_than<N: Number, D: Dimension>(x: &TVec<N, D>, y: &TVec<N, D>) -> TVec<bool, D>
    where DefaultAllocator: Alloc<N, D> {
    x.zip_map(y, |x, y| x < y)
}

/// Component-wise `>=` comparison.
///
/// # Examples:
///
/// ```
/// # use nalgebra_glm as glm;
/// assert_eq!(glm::vec3(true, false, true),
///            glm::less_than_equal(&glm::vec3(1.0, 3.0, 4.0),
///                                 &glm::vec3(1.0, 2.0, 5.0)));
/// ```
///
/// # See also:
///
/// * [`equal`](fn.equal.html)
/// * [`greater_than`](fn.greater_than.html)
/// * [`greater_than_equal`](fn.greater_than_equal.html)
/// * [`less_than`](fn.less_than.html)
/// * [`not`](fn.not.html)
/// * [`not_equal`](fn.not_equal.html)
pub fn less_than_equal<N: Number, D: Dimension>(x: &TVec<N, D>, y: &TVec<N, D>) -> TVec<bool, D>
    where DefaultAllocator: Alloc<N, D> {
    x.zip_map(y, |x, y| x <= y)
}

/// Component-wise not `!`.
///
/// # Examples:
///
/// ```
/// # use nalgebra_glm as glm;
/// let vec = glm::vec2(true, false);
/// assert_eq!(glm::vec2(false, true), glm::not(&vec));
/// ```
///
/// # See also:
///
/// * [`all`](fn.all.html)
/// * [`any`](fn.any.html)
/// * [`equal`](fn.equal.html)
/// * [`greater_than`](fn.greater_than.html)
/// * [`greater_than_equal`](fn.greater_than_equal.html)
/// * [`less_than`](fn.less_than.html)
/// * [`less_than_equal`](fn.less_than_equal.html)
/// * [`not_equal`](fn.not_equal.html)
pub fn not<D: Dimension>(v: &TVec<bool, D>) -> TVec<bool, D>
    where DefaultAllocator: Alloc<bool, D> {
    v.map(|x| !x)
}

/// Component-wise not-equality `!=`.
///
/// # Examples:
///
/// ```
/// # use nalgebra_glm as glm;
/// assert_eq!(glm::vec2(false, true),
///            glm::not_equal(&glm::vec2(1.0, 3.0),
///                           &glm::vec2(1.0, 2.0)));
/// ```
///
/// # See also:
///
/// * [`equal`](fn.equal.html)
/// * [`greater_than`](fn.greater_than.html)
/// * [`greater_than_equal`](fn.greater_than_equal.html)
/// * [`less_than`](fn.less_than.html)
/// * [`less_than_equal`](fn.less_than_equal.html)
/// * [`not`](fn.not.html)
pub fn not_equal<N: Number, D: Dimension>(x: &TVec<N, D>, y: &TVec<N, D>) -> TVec<bool, D>
    where DefaultAllocator: Alloc<N, D> {
    x.zip_map(y, |x, y| x != y)
}
<|endoftext|>


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

use std::collections::HashMap;

use syn::{parse_quote, FnArg, PatType, Type, TypePtr};

use crate::conversion::analysis::fun::ReceiverMutability;
use crate::conversion::analysis::pod::PodPhase;
use crate::conversion::api::{FuncToConvert, RustSubclassFnDetails, SubclassName};
use crate::{
    conversion::{
        analysis::fun::function_wrapper::{
            CppFunction, CppFunctionBody, CppFunctionKind, TypeConversionPolicy,
        },
        api::{Api, ApiName},
    },
    types::{make_ident, Namespace, QualifiedName},
};

use super::FnPhase;

pub(super) fn subclasses_by_superclass(
    apis: &[Api<PodPhase>],
) -> HashMap<QualifiedName, Vec<SubclassName>> {
    let mut subclasses_per_superclass: HashMap<QualifiedName, Vec<SubclassName>> = HashMap::new();

    for api in apis.iter() {
        if let Api::Subclass { name, superclass } = api {
            subclasses_per_superclass
                .entry(superclass.clone())
                .or_default()
                .push(name.clone());
        }
    }
    subclasses_per_superclass
}

pub(super) fn create_subclass_fn_wrapper(
    sub: SubclassName,
    super_fn_name: QualifiedName,
    fun: &Box<FuncToConvert>,
) -> (Box<FuncToConvert>, ApiName) {
    let self_ty = Some(sub.cpp());
    let maybe_wrap = Box::new(FuncToConvert {
        virtual_this_type: self_ty.clone(),
        self_ty,
        ident: super_fn_name.get_final_ident(),
        doc_attr: fun.doc_attr.clone(),
        inputs: fun.inputs.clone(),
        output: fun.output.clone(),
        vis: fun.vis.clone(),
        is_pure_virtual: false,
        is_private: false,
        is_move_constructor: false,
        unused_template_param: fun.unused_template_param,
        original_name: None,
        return_type_is_reference: fun.return_type_is_reference,
        reference_args: fun.reference_args.clone(),
    });
    let super_fn_name = ApiName::new_from_qualified_name(super_fn_name);
    (maybe_wrap, super_fn_name)
}

pub(super) fn create_subclass_function(
    sub: &SubclassName,
    analysis: &super::FnAnalysis,
    name: &ApiName,
    receiver_mutability: &ReceiverMutability,
    superclass: &QualifiedName,
    dependency: &QualifiedName,
) -> Api<FnPhase> {
    let cpp = sub.cpp();
    let holder_name = sub.holder();
    let rust_call_name = make_ident(format!(
        "{}_{}",
        sub.0.name.get_final_item(),
        name.name.get_final_item()
    ));
    let params = std::iter::once(parse_quote! {
        me: & #holder_name
    })
    .chain(analysis.params.iter().skip(1).cloned())
    .collect();
    let kind = if matches!(receiver_mutability, ReceiverMutability::Mutable) {
        CppFunctionKind::Method
    } else {
        CppFunctionKind::ConstMethod
    };
    let subclass_function: Api<FnPhase> = Api::RustSubclassFn {
        name: ApiName::new_in_root_namespace(rust_call_name.clone()),
        subclass: sub.clone(),
        details: Box::new(RustSubclassFnDetails {
            params,
            ret: analysis.ret_type.clone(),
            method_name: make_ident(&analysis.rust_name),
            cpp_impl: CppFunction {
                payload: CppFunctionBody::FunctionCall(Namespace::new(), rust_call_name),
                wrapper_function_name: name.name.get_final_ident(),
                return_conversion: analysis.ret_conversion.clone(),
                argument_conversion: analysis
                    .param_details
                    .iter()
                    .skip(1)
                    .map(|p| p.conversion.clone())
                    .collect(),
                kind,
                pass_obs_field: true,
                qualification: Some(cpp),
            },
            superclass: superclass.clone(),
            receiver_mutability: receiver_mutability.clone(),
            dependency: dependency.clone(),
        }),
    };
    subclass_function
}

pub(super) fn create_subclass_constructor_wrapper(
    sub: SubclassName,
    fun: &Box<FuncToConvert>,
) -> (Box<FuncToConvert>, ApiName) {
    let subclass_constructor_name = make_ident(format!(
        "{}_{}",
        sub.cpp().get_final_item(),
        sub.cpp().get_final_item()
    ));
    let mut existing_params = fun.inputs.clone();
    if let Some(FnArg::Typed(PatType { ty, .. })) = existing_params.first_mut() {
        if let Type::Ptr(TypePtr { elem, .. }) = &mut **ty {
            *elem = Box::new(Type::Path(sub.cpp().to_type_path()));
        } else {
            panic!("Unexpected self type parameter when creating subclass constructor");
        }
    } else {
        panic!("Unexpected self type parameter when creating subclass constructor");
    }
    let mut existing_params = existing_params.into_iter();
    let self_param = existing_params.next();
    let holder = sub.holder();
    let boxed_holder_param: FnArg = parse_quote! {
        peer: rust::Box<#holder>
    };
    let inputs = self_param
        .into_iter()
        .chain(std::iter::once(boxed_holder_param))
        .chain(existing_params)
        .collect();
    let self_ty = Some(sub.cpp());
    let maybe_wrap = Box::new(FuncToConvert {
        virtual_this_type: self_ty.clone(),
        self_ty,
        ident: subclass_constructor_name.clone(),
        doc_attr: fun.doc_attr.clone(),
        inputs,
        output: fun.output.clone(),
        vis: fun.vis.clone(),
        is_pure_virtual: false,
        is_private: fun.is_private,
        is_move_constructor: false,
        original_name: None,
        unused_template_param: fun.unused_template_param,
        return_type_is_reference: fun.return_type_is_reference,
        reference_args: fun.reference_args.clone(),
    });
    let mut subclass_constructor_name = ApiName::new_in_root_namespace(subclass_constructor_name);
    subclass_constructor_name.cpp_name = Some(sub.cpp().get_final_item().to_string());
    (maybe_wrap, subclass_constructor_name)
}

pub(super) fn create_subclass_constructor(
    sub: &SubclassName,
    analysis: &super::FnAnalysis,
    superclass: &QualifiedName,
) -> Api<FnPhase> {
    let holder_name = sub.holder();
    let cpp = sub.cpp();
    let wrapper_function_name = cpp.get_final_ident();
    let initial_arg = TypeConversionPolicy::new_unconverted(parse_quote! {
        rust::Box< #holder_name >
    });
    let args = std::iter::once(initial_arg).chain(
        analysis
            .param_details
            .iter()
            .map(|aa| aa.conversion.clone()),
    );
    let cpp_impl = CppFunction {
        payload: CppFunctionBody::ConstructSuperclass(superclass.to_cpp_name()),
        wrapper_function_name,
        return_conversion: None,
        argument_conversion: args.collect(),
        kind: CppFunctionKind::Constructor,
        pass_obs_field: false,
        qualification: Some(cpp.clone()),
    };
    Api::RustSubclassConstructor {
        name: ApiName::new_from_qualified_name(cpp),
        subclass: sub.clone(),
        cpp_impl: Box::new(cpp_impl),
        is_trivial: analysis.param_details.is_empty(),
    }
}
<commit_msg>Clippy fix.<commit_after>// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::collections::HashMap;

use syn::{parse_quote, FnArg, PatType, Type, TypePtr};

use crate::conversion::analysis::fun::ReceiverMutability;
use crate::conversion::analysis::pod::PodPhase;
use crate::conversion::api::{FuncToConvert, RustSubclassFnDetails, SubclassName};
use crate::{
    conversion::{
        analysis::fun::function_wrapper::{
            CppFunction, CppFunctionBody, CppFunctionKind, TypeConversionPolicy,
        },
        api::{Api, ApiName},
    },
    types::{make_ident, Namespace, QualifiedName},
};

use super::FnPhase;

pub(super) fn subclasses_by_superclass(
    apis: &[Api<PodPhase>],
) -> HashMap<QualifiedName, Vec<SubclassName>> {
    let mut subclasses_per_superclass: HashMap<QualifiedName, Vec<SubclassName>> = HashMap::new();

    for api in apis.iter() {
        if let Api::Subclass { name, superclass } = api {
            subclasses_per_superclass
                .entry(superclass.clone())
                .or_default()
                .push(name.clone());
        }
    }
    subclasses_per_superclass
}

pub(super) fn create_subclass_fn_wrapper(
    sub: SubclassName,
    super_fn_name: QualifiedName,
    fun: &FuncToConvert,
) -> (Box<FuncToConvert>, ApiName) {
    let self_ty = Some(sub.cpp());
    let maybe_wrap = Box::new(FuncToConvert {
        virtual_this_type: self_ty.clone(),
        self_ty,
        ident: super_fn_name.get_final_ident(),
        doc_attr: fun.doc_attr.clone(),
        inputs: fun.inputs.clone(),
        output: fun.output.clone(),
        vis: fun.vis.clone(),
        is_pure_virtual: false,
        is_private: false,
        is_move_constructor: false,
        unused_template_param: fun.unused_template_param,
        original_name: None,
        return_type_is_reference: fun.return_type_is_reference,
        reference_args: fun.reference_args.clone(),
    });
    let super_fn_name = ApiName::new_from_qualified_name(super_fn_name);
    (maybe_wrap, super_fn_name)
}

pub(super) fn create_subclass_function(
    sub: &SubclassName,
    analysis: &super::FnAnalysis,
    name: &ApiName,
    receiver_mutability: &ReceiverMutability,
    superclass: &QualifiedName,
    dependency: &QualifiedName,
) -> Api<FnPhase> {
    let cpp = sub.cpp();
    let holder_name = sub.holder();
    let rust_call_name = make_ident(format!(
        "{}_{}",
        sub.0.name.get_final_item(),
        name.name.get_final_item()
    ));
    let params = std::iter::once(parse_quote! {
        me: & #holder_name
    })
    .chain(analysis.params.iter().skip(1).cloned())
    .collect();
    let kind = if matches!(receiver_mutability, ReceiverMutability::Mutable) {
        CppFunctionKind::Method
    } else {
        CppFunctionKind::ConstMethod
    };
    let subclass_function: Api<FnPhase> = Api::RustSubclassFn {
        name: ApiName::new_in_root_namespace(rust_call_name.clone()),
        subclass: sub.clone(),
        details: Box::new(RustSubclassFnDetails {
            params,
            ret: analysis.ret_type.clone(),
            method_name: make_ident(&analysis.rust_name),
            cpp_impl: CppFunction {
                payload: CppFunctionBody::FunctionCall(Namespace::new(), rust_call_name),
                wrapper_function_name: name.name.get_final_ident(),
                return_conversion: analysis.ret_conversion.clone(),
                argument_conversion: analysis
                    .param_details
                    .iter()
                    .skip(1)
                    .map(|p| p.conversion.clone())
                    .collect(),
                kind,
                pass_obs_field: true,
                qualification: Some(cpp),
            },
            superclass: superclass.clone(),
            receiver_mutability: receiver_mutability.clone(),
            dependency: dependency.clone(),
        }),
    };
    subclass_function
}

pub(super) fn create_subclass_constructor_wrapper(
    sub: SubclassName,
    fun: &FuncToConvert,
) -> (Box<FuncToConvert>, ApiName) {
    let subclass_constructor_name = make_ident(format!(
        "{}_{}",
        sub.cpp().get_final_item(),
        sub.cpp().get_final_item()
    ));
    let mut existing_params = fun.inputs.clone();
    if let Some(FnArg::Typed(PatType { ty, .. })) = existing_params.first_mut() {
        if let Type::Ptr(TypePtr { elem, .. }) = &mut **ty {
            *elem = Box::new(Type::Path(sub.cpp().to_type_path()));
        } else {
            panic!("Unexpected self type parameter when creating subclass constructor");
        }
    } else {
        panic!("Unexpected self type parameter when creating subclass constructor");
    }
    let mut existing_params = existing_params.into_iter();
    let self_param = existing_params.next();
    let holder = sub.holder();
    let boxed_holder_param: FnArg = parse_quote! {
        peer: rust::Box<#holder>
    };
    let inputs = self_param
        .into_iter()
        .chain(std::iter::once(boxed_holder_param))
        .chain(existing_params)
        .collect();
    let self_ty = Some(sub.cpp());
    let maybe_wrap = Box::new(FuncToConvert {
        virtual_this_type: self_ty.clone(),
        self_ty,
        ident: subclass_constructor_name.clone(),
        doc_attr: fun.doc_attr.clone(),
        inputs,
        output: fun.output.clone(),
        vis: fun.vis.clone(),
        is_pure_virtual: false,
        is_private: fun.is_private,
        is_move_constructor: false,
        original_name: None,
        unused_template_param: fun.unused_template_param,
        return_type_is_reference: fun.return_type_is_reference,
        reference_args: fun.reference_args.clone(),
    });
    let mut subclass_constructor_name = ApiName::new_in_root_namespace(subclass_constructor_name);
    subclass_constructor_name.cpp_name = Some(sub.cpp().get_final_item().to_string());
    (maybe_wrap, subclass_constructor_name)
}

pub(super) fn create_subclass_constructor(
    sub: &SubclassName,
    analysis: &super::FnAnalysis,
    superclass: &QualifiedName,
) -> Api<FnPhase> {
    let holder_name = sub.holder();
    let cpp = sub.cpp();
    let wrapper_function_name = cpp.get_final_ident();
    let initial_arg = TypeConversionPolicy::new_unconverted(parse_quote! {
        rust::Box< #holder_name >
    });
    let args = std::iter::once(initial_arg).chain(
        analysis
            .param_details
            .iter()
            .map(|aa| aa.conversion.clone()),
    );
    let cpp_impl = CppFunction {
        payload: CppFunctionBody::ConstructSuperclass(superclass.to_cpp_name()),
        wrapper_function_name,
        return_conversion: None,
        argument_conversion: args.collect(),
        kind: CppFunctionKind::Constructor,
        pass_obs_field: false,
        qualification: Some(cpp.clone()),
    };
    Api::RustSubclassConstructor {
        name: ApiName::new_from_qualified_name(cpp),
        subclass: sub.clone(),
        cpp_impl: Box::new(cpp_impl),
        is_trivial: analysis.param_details.is_empty(),
    }
}
<|endoftext|>


<commit_before>use std::pin::{Pin, Unpin};
use std::sync::Arc;
use tab_organizer::ease;
use crate::constants::{DRAG_GAP_PX, TOOLBAR_TOTAL_HEIGHT, GROUP_BORDER_WIDTH, GROUP_PADDING_TOP, GROUP_HEADER_HEIGHT, GROUP_PADDING_BOTTOM, TAB_PADDING, TAB_HEIGHT, TAB_BORDER_WIDTH};
use crate::types::{State, Group, Tab};
use dominator::animation::MutableAnimationSignal;
use futures::{Future, Poll};
use futures::task::LocalWaker;
use futures_signals::signal::{Signal, SignalExt, MutableSignal};
use futures_signals::signal_vec::{SignalVec, SignalVecExt, VecDiff};

struct MutableSink<A> where A: Signal {
    signal: Option<A>,
    value: Option<A::Item>,
}

impl<A> MutableSink<A> where A: Signal + Unpin, A::Item: Copy {
    fn new(signal: A) -> Self {
        Self {
            signal: Some(signal),
            value: None,
        }
    }

    fn value(&self) -> A::Item {
        self.value.unwrap()
    }

    fn is_changed(&mut self, waker: &LocalWaker) -> bool {
        let mut changed = false;

        loop {
            match self.signal.as_mut().map(|signal| signal.poll_change_unpin(waker)) {
                Some(Poll::Ready(Some(value))) => {
                    self.value = Some(value);
                    changed = true;
                    continue;
                },
                Some(Poll::Ready(None)) => {
                    self.signal = None;
                },
                Some(Poll::Pending) => {},
                None => {},
            }

            return changed;
        }
    }
}

struct MutableVecSink<A> where A: SignalVec {
    signal: Option<A>,
    values: Vec<A::Item>,
}

impl<A> MutableVecSink<A> where A: SignalVec + Unpin {
    fn new(signal: A) -> Self {
        Self {
            signal: Some(signal),
            values: vec![],
        }
    }

    fn is_changed<F>(&mut self, waker: &LocalWaker, mut f: F) -> bool where F: FnMut(&mut A::Item) -> bool {
        let mut changed = false;

        loop {
            match self.signal.as_mut().map(|signal| signal.poll_vec_change_unpin(waker)) {
                Some(Poll::Ready(Some(change))) => {
                    changed = true;

                    // TODO move this into futures_signals crate
                    match change {
                        VecDiff::Replace { values } => {
                            self.values = values;
                        },
                        VecDiff::InsertAt { index, value } => {
                            self.values.insert(index, value);
                        },
                        VecDiff::UpdateAt { index, value } => {
                            self.values[index] = value;
                        },
                        VecDiff::RemoveAt { index } => {
                            self.values.remove(index);
                        },
                        VecDiff::Move { old_index, new_index } => {
                            let value = self.values.remove(old_index);
                            self.values.insert(new_index, value);
                        },
                        VecDiff::Push { value } => {
                            self.values.push(value);
                        },
                        VecDiff::Pop {} => {
                            self.values.pop().unwrap();
                        },
                        VecDiff::Clear {} => {
                            self.values.clear();
                        },
                    }

                    continue;
                },
                Some(Poll::Ready(None)) => {
                    self.signal = None;
                },
                Some(Poll::Pending) => {},
                None => {},
            }

            for value in self.values.iter_mut() {
                if f(value) {
                    changed = true;
                }
            }

            return changed;
        }
    }
}

impl State {
    fn hide_tab(&self, tab: &Tab) {
        tab.visible.set_neq(false);
        tab.holding.set_neq(false);
        tab.close_hovered.set_neq(false);
        tab.close_holding.set_neq(false);
        self.unhover_tab(tab);
    }
}

struct CulledTab {
    state: Arc<Tab>,
    is_dragging: MutableSink<MutableSignal<bool>>,
    insert_animation: MutableSink<MutableAnimationSignal>,
}

impl CulledTab {
    fn new(state: Arc<Tab>) -> Self {
        Self {
            is_dragging: MutableSink::new(state.dragging.signal()),
            insert_animation: MutableSink::new(state.insert_animation.signal()),
            state,
        }
    }

    fn is_changed(&mut self, waker: &LocalWaker) -> bool {
        let is_dragging = self.is_dragging.is_changed(waker);
        let insert_animation = self.insert_animation.is_changed(waker);

        is_dragging ||
        insert_animation
    }

    // TODO hacky
    fn height(&self) -> f64 {
        // TODO use range_inclusive ?
        let percentage = ease(self.insert_animation.value()).into_f64();

        (TAB_BORDER_WIDTH * percentage).round() +
        (TAB_PADDING * percentage).round() +
        (TAB_HEIGHT * percentage).round() +
        (TAB_PADDING * percentage).round() +
        (TAB_BORDER_WIDTH * percentage).round()
    }
}

struct CulledGroup<A> where A: SignalVec {
    state: Arc<Group>,
    tabs: MutableVecSink<A>,
    insert_animation: MutableSink<MutableAnimationSignal>,
}

impl<A> CulledGroup<A> where A: SignalVec<Item = CulledTab> + Unpin {
    fn new(state: Arc<Group>, tabs_signal: A) -> Self {
        Self {
            tabs: MutableVecSink::new(tabs_signal),
            insert_animation: MutableSink::new(state.insert_animation.signal()),
            state,
        }
    }

    fn is_changed(&mut self, waker: &LocalWaker) -> bool {
        let tabs = self.tabs.is_changed(waker, |tab| tab.is_changed(waker));
        let insert_animation = self.insert_animation.is_changed(waker);

        tabs ||
        insert_animation
    }

    // TODO hacky
    // TODO what about when it's dragging ?
    fn height(&self) -> (f64, f64) {
        // TODO use range_inclusive
        let percentage = ease(self.insert_animation.value()).into_f64();

        (
            (GROUP_BORDER_WIDTH * percentage).round() +
            (GROUP_PADDING_TOP * percentage).round() +
            (if self.state.show_header { (GROUP_HEADER_HEIGHT * percentage).round() } else { 0.0 }),

            (GROUP_PADDING_BOTTOM * percentage).round()
        )
    }
}

struct Culler<A, B, C, D> where A: SignalVec, B: Signal, C: Signal, D: Signal {
    state: Arc<State>,
    groups: MutableVecSink<A>,
    is_dragging: MutableSink<B>,
    scroll_y: MutableSink<C>,
    window_height: MutableSink<D>,
}

impl<A, B, C, D, E> Culler<A, C, D, E>
    where A: SignalVec<Item = CulledGroup<B>> + Unpin,
          B: SignalVec<Item = CulledTab> + Unpin,
          C: Signal<Item = bool> + Unpin,
          D: Signal<Item = f64> + Unpin,
          E: Signal<Item = f64> + Unpin {

    fn is_changed(&mut self, waker: &LocalWaker) -> bool {
        let groups = self.groups.is_changed(waker, |group| group.is_changed(waker));
        let is_dragging = self.is_dragging.is_changed(waker);
        let scroll_y = self.scroll_y.is_changed(waker);
        let window_height = self.window_height.is_changed(waker);

        groups ||
        is_dragging ||
        scroll_y ||
        window_height
    }

    // TODO debounce this ?
    // TODO make this simpler somehow ?
    // TODO add in stuff to handle tab dragging
    fn update(&mut self) {
        let is_dragging = self.is_dragging.value();

        // TODO is this floor correct ?
        let top_y = self.scroll_y.value().floor();
        // TODO is this ceil correct ?
        let bottom_y = top_y + (self.window_height.value().ceil() - TOOLBAR_TOTAL_HEIGHT);

        let mut padding: Option<f64> = None;
        let mut current_height: f64 = 0.0;

        for group in self.groups.values.iter() {
            let old_height = current_height;

            let mut tabs_padding: Option<f64> = None;

            let (top_height, bottom_height) = group.height();

            current_height += top_height;

            let tabs_height = current_height;

            // TODO what if there aren't any tabs in the group ?
            for tab in group.tabs.values.iter() {
                // TODO is this correct ?
                if !tab.is_dragging.value() {
                    let old_height = current_height;

                    current_height += tab.height();

                    if current_height > old_height && old_height < bottom_y && current_height > top_y {
                        if let None = tabs_padding {
                            tabs_padding = Some(old_height);
                        }

                        tab.state.visible.set_neq(true);

                    } else {
                        self.state.hide_tab(&tab.state);
                    }

                } else {
                    self.state.hide_tab(&tab.state);
                }
            }

            let no_tabs_height = current_height;

            current_height += bottom_height;

            // TODO what if the group has height but the tabs don't ?
            // TODO what if the tabs have height but the group doesn't ?
            if current_height > old_height && old_height < bottom_y && current_height > top_y {
                if let None = padding {
                    padding = Some(old_height);
                }

                group.state.tabs_padding.set_neq(tabs_padding.unwrap_or(no_tabs_height) - tabs_height);
                group.state.visible.set_neq(true);

            } else {
                group.state.visible.set_neq(false);
            }
        }

        if is_dragging {
            // TODO handle this better somehow ?
            current_height += DRAG_GAP_PX;
        }

        self.state.groups_padding.set_neq(padding.unwrap_or(0.0));
        self.state.scrolling.height.set_neq(current_height);
    }
}

impl<A, B, C, D> Unpin for Culler<A, B, C, D> where A: SignalVec, B: Signal, C: Signal, D: Signal {}

impl<A, B, C, D, E> Future for Culler<A, C, D, E>
    where A: SignalVec<Item = CulledGroup<B>> + Unpin,
          B: SignalVec<Item = CulledTab> + Unpin,
          C: Signal<Item = bool> + Unpin,
          D: Signal<Item = f64> + Unpin,
          E: Signal<Item = f64> + Unpin {

    type Output = ();

    fn poll(mut self: Pin<&mut Self>, waker: &LocalWaker) -> Poll<Self::Output> {
        let changed = self.is_changed(waker);

        if changed {
            //time!("Culling", {
                self.update();
            //});
        }

        Poll::Pending
    }
}

pub(crate) fn cull_groups<A>(state: Arc<State>, window_height: A) -> impl Future<Output = ()> where A: Signal<Item = f64> + Unpin {
    Culler {
        groups: MutableVecSink::new(state.groups.signal_vec_cloned()
            // TODO duplication with main.rs
            .delay_remove(|group| group.wait_until_removed())
            .map(|group| {
                let tabs = group.tabs.signal_vec_cloned()
                    // TODO duplication with main.rs
                    .delay_remove(|tab| tab.wait_until_removed())
                    .map(CulledTab::new);
                CulledGroup::new(group, tabs)
            })),
        is_dragging: MutableSink::new(state.is_dragging()),
        scroll_y: MutableSink::new(state.scrolling.y.signal()),
        window_height: MutableSink::new(window_height),
        state,
    }
}
<commit_msg>Minor tweak<commit_after>use std::pin::{Pin, Unpin};
use std::sync::Arc;
use tab_organizer::ease;
use crate::constants::{DRAG_GAP_PX, TOOLBAR_TOTAL_HEIGHT, GROUP_BORDER_WIDTH, GROUP_PADDING_TOP, GROUP_HEADER_HEIGHT, GROUP_PADDING_BOTTOM, TAB_PADDING, TAB_HEIGHT, TAB_BORDER_WIDTH};
use crate::types::{State, Group, Tab};
use dominator::animation::MutableAnimationSignal;
use futures::{Future, Poll};
use futures::task::LocalWaker;
use futures_signals::signal::{Signal, SignalExt, MutableSignal};
use futures_signals::signal_vec::{SignalVec, SignalVecExt, VecDiff};

struct MutableSink<A> where A: Signal {
    signal: Option<A>,
    value: Option<A::Item>,
}

impl<A> MutableSink<A> where A: Signal + Unpin, A::Item: Copy {
    fn new(signal: A) -> Self {
        Self {
            signal: Some(signal),
            value: None,
        }
    }

    fn value(&self) -> A::Item {
        self.value.unwrap()
    }

    fn is_changed(&mut self, waker: &LocalWaker) -> bool {
        let mut changed = false;

        loop {
            match self.signal.as_mut().map(|signal| signal.poll_change_unpin(waker)) {
                Some(Poll::Ready(Some(value))) => {
                    self.value = Some(value);
                    changed = true;
                    continue;
                },
                Some(Poll::Ready(None)) => {
                    self.signal = None;
                },
                Some(Poll::Pending) => {},
                None => {},
            }

            return changed;
        }
    }
}

struct MutableVecSink<A> where A: SignalVec {
    signal: Option<A>,
    values: Vec<A::Item>,
}

impl<A> MutableVecSink<A> where A: SignalVec + Unpin {
    fn new(signal: A) -> Self {
        Self {
            signal: Some(signal),
            values: vec![],
        }
    }

    fn is_changed<F>(&mut self, waker: &LocalWaker, mut f: F) -> bool where F: FnMut(&mut A::Item) -> bool {
        let mut changed = false;

        loop {
            match self.signal.as_mut().map(|signal| signal.poll_vec_change_unpin(waker)) {
                Some(Poll::Ready(Some(change))) => {
                    changed = true;

                    // TODO move this into futures_signals crate
                    match change {
                        VecDiff::Replace { values } => {
                            self.values = values;
                        },
                        VecDiff::InsertAt { index, value } => {
                            self.values.insert(index, value);
                        },
                        VecDiff::UpdateAt { index, value } => {
                            self.values[index] = value;
                        },
                        VecDiff::RemoveAt { index } => {
                            self.values.remove(index);
                        },
                        VecDiff::Move { old_index, new_index } => {
                            let value = self.values.remove(old_index);
                            self.values.insert(new_index, value);
                        },
                        VecDiff::Push { value } => {
                            self.values.push(value);
                        },
                        VecDiff::Pop {} => {
                            self.values.pop().unwrap();
                        },
                        VecDiff::Clear {} => {
                            self.values.clear();
                        },
                    }

                    continue;
                },
                Some(Poll::Ready(None)) => {
                    self.signal = None;
                },
                Some(Poll::Pending) => {},
                None => {},
            }

            for value in self.values.iter_mut() {
                if f(value) {
                    changed = true;
                }
            }

            return changed;
        }
    }
}

impl State {
    fn hide_tab(&self, tab: &Tab) {
        tab.visible.set_neq(false);
        tab.holding.set_neq(false);
        tab.close_hovered.set_neq(false);
        tab.close_holding.set_neq(false);
        self.unhover_tab(tab);
    }
}

struct CulledTab {
    state: Arc<Tab>,
    is_dragging: MutableSink<MutableSignal<bool>>,
    insert_animation: MutableSink<MutableAnimationSignal>,
}

impl CulledTab {
    fn new(state: Arc<Tab>) -> Self {
        Self {
            is_dragging: MutableSink::new(state.dragging.signal()),
            insert_animation: MutableSink::new(state.insert_animation.signal()),
            state,
        }
    }

    fn is_changed(&mut self, waker: &LocalWaker) -> bool {
        let is_dragging = self.is_dragging.is_changed(waker);
        let insert_animation = self.insert_animation.is_changed(waker);

        is_dragging ||
        insert_animation
    }

    // TODO hacky
    fn height(&self) -> f64 {
        // TODO use range_inclusive ?
        let percentage = ease(self.insert_animation.value()).into_f64();

        (TAB_BORDER_WIDTH * percentage).round() +
        (TAB_PADDING * percentage).round() +
        (TAB_HEIGHT * percentage).round() +
        (TAB_PADDING * percentage).round() +
        (TAB_BORDER_WIDTH * percentage).round()
    }
}

struct CulledGroup<A> where A: SignalVec {
    state: Arc<Group>,
    tabs: MutableVecSink<A>,
    insert_animation: MutableSink<MutableAnimationSignal>,
}

impl<A> CulledGroup<A> where A: SignalVec<Item = CulledTab> + Unpin {
    fn new(state: Arc<Group>, tabs_signal: A) -> Self {
        Self {
            tabs: MutableVecSink::new(tabs_signal),
            insert_animation: MutableSink::new(state.insert_animation.signal()),
            state,
        }
    }

    fn is_changed(&mut self, waker: &LocalWaker) -> bool {
        let tabs = self.tabs.is_changed(waker, |tab| tab.is_changed(waker));
        let insert_animation = self.insert_animation.is_changed(waker);

        tabs ||
        insert_animation
    }

    // TODO hacky
    // TODO what about when it's dragging ?
    fn height(&self) -> (f64, f64) {
        // TODO use range_inclusive
        let percentage = ease(self.insert_animation.value()).into_f64();

        (
            (GROUP_BORDER_WIDTH * percentage).round() +
            (GROUP_PADDING_TOP * percentage).round() +
            (if self.state.show_header { (GROUP_HEADER_HEIGHT * percentage).round() } else { 0.0 }),

            (GROUP_PADDING_BOTTOM * percentage).round()
        )
    }
}

struct Culler<A, B, C, D> where A: SignalVec, B: Signal, C: Signal, D: Signal {
    state: Arc<State>,
    groups: MutableVecSink<A>,
    is_dragging: MutableSink<B>,
    scroll_y: MutableSink<C>,
    window_height: MutableSink<D>,
}

impl<A, B, C, D, E> Culler<A, C, D, E>
    where A: SignalVec<Item = CulledGroup<B>> + Unpin,
          B: SignalVec<Item = CulledTab> + Unpin,
          C: Signal<Item = bool> + Unpin,
          D: Signal<Item = f64> + Unpin,
          E: Signal<Item = f64> + Unpin {

    fn is_changed(&mut self, waker: &LocalWaker) -> bool {
        let groups = self.groups.is_changed(waker, |group| group.is_changed(waker));
        let is_dragging = self.is_dragging.is_changed(waker);
        let scroll_y = self.scroll_y.is_changed(waker);
        let window_height = self.window_height.is_changed(waker);

        groups ||
        is_dragging ||
        scroll_y ||
        window_height
    }

    // TODO debounce this ?
    // TODO make this simpler somehow ?
    // TODO add in stuff to handle tab dragging
    fn update(&mut self) {
        let is_dragging = self.is_dragging.value();

        // TODO is this floor correct ?
        let top_y = self.scroll_y.value().floor();
        // TODO is this ceil correct ?
        let bottom_y = top_y + (self.window_height.value().ceil() - TOOLBAR_TOTAL_HEIGHT);

        let mut padding: Option<f64> = None;
        let mut current_height: f64 = 0.0;

        for group in self.groups.values.iter() {
            let old_height = current_height;

            let mut tabs_padding: Option<f64> = None;

            let (top_height, bottom_height) = group.height();

            current_height += top_height;

            let tabs_height = current_height;

            // TODO what if there aren't any tabs in the group ?
            for tab in group.tabs.values.iter() {
                // TODO is this correct ?
                if !tab.is_dragging.value() {
                    let old_height = current_height;

                    current_height += tab.height();

                    if current_height > old_height && old_height < bottom_y && current_height > top_y {
                        if let None = tabs_padding {
                            tabs_padding = Some(old_height);
                        }

                        tab.state.visible.set_neq(true);

                    } else {
                        self.state.hide_tab(&tab.state);
                    }

                } else {
                    self.state.hide_tab(&tab.state);
                }
            }

            let no_tabs_height = current_height;

            current_height += bottom_height;

            // TODO what if the group has height but the tabs don't ?
            // TODO what if the tabs have height but the group doesn't ?
            if current_height > old_height && old_height < bottom_y && current_height > top_y {
                if let None = padding {
                    padding = Some(old_height);
                }

                group.state.tabs_padding.set_neq(tabs_padding.unwrap_or(no_tabs_height) - tabs_height);
                group.state.visible.set_neq(true);

            } else {
                group.state.visible.set_neq(false);
            }
        }

        if is_dragging {
            // TODO handle this better somehow ?
            current_height += DRAG_GAP_PX;
        }

        self.state.groups_padding.set_neq(padding.unwrap_or(0.0));
        self.state.scrolling.height.set_neq(current_height);
    }
}

impl<A, B, C, D> Unpin for Culler<A, B, C, D> where A: SignalVec, B: Signal, C: Signal, D: Signal {}

impl<A, B, C, D, E> Future for Culler<A, C, D, E>
    where A: SignalVec<Item = CulledGroup<B>> + Unpin,
          B: SignalVec<Item = CulledTab> + Unpin,
          C: Signal<Item = bool> + Unpin,
          D: Signal<Item = f64> + Unpin,
          E: Signal<Item = f64> + Unpin {

    type Output = ();

    fn poll(mut self: Pin<&mut Self>, waker: &LocalWaker) -> Poll<Self::Output> {
        //time!("Culling", {
            let changed = self.is_changed(waker);

            if changed {
                self.update();
            }
        //});

        Poll::Pending
    }
}

pub(crate) fn cull_groups<A>(state: Arc<State>, window_height: A) -> impl Future<Output = ()> where A: Signal<Item = f64> + Unpin {
    Culler {
        groups: MutableVecSink::new(state.groups.signal_vec_cloned()
            // TODO duplication with main.rs
            .delay_remove(|group| group.wait_until_removed())
            .map(|group| {
                let tabs = group.tabs.signal_vec_cloned()
                    // TODO duplication with main.rs
                    .delay_remove(|tab| tab.wait_until_removed())
                    .map(CulledTab::new);
                CulledGroup::new(group, tabs)
            })),
        is_dragging: MutableSink::new(state.is_dragging()),
        scroll_y: MutableSink::new(state.scrolling.y.signal()),
        window_height: MutableSink::new(window_height),
        state,
    }
}
<|endoftext|>