<commit_before>use super::{ExecutionStrategy,
            Scope};
use crate::{config,
            error::{Error,
                    Result}};
use futures::stream::StreamExt;
use habitat_common::{cli::FS_ROOT,
                     package_graph::PackageGraph,
                     types::ListenCtlAddr,
                     ui::{Status,
                          UIWriter}};
use habitat_core::{error as herror,
                   fs as hfs,
                   package::{list::temp_package_directory,
                             Identifiable,
                             PackageIdent,
                             PackageInstall}};
use habitat_sup_client::{SrvClient,
                         SrvClientError};
use habitat_sup_protocol;
use std::{fs,
          path::Path,
          str::FromStr};

/// Delete a package and all dependencies which are not used by other packages.
/// We do an ordered traverse of the dependencies, updating the graph as we delete a
/// package. This lets us use simple logic where we continually check if the package
/// we're trying to delete currently has no packages depending on it.
///
/// The full logic is:
/// 1. We find the fully qualified package ident and all its dependencies
/// 2. We find all packages on the filesystem and convert them into a graph
/// 3. We do a BFS on the graph to get the dependencies in order
/// 4. We check if the specified package has any reverse deps
///     4a. If there are, we throw an error
///     4b. If not, we delete the package
/// 5. For each dependency we check if there are any packages which depend on it
///     5a. If there are, we skip it
///     5b. If there are not, we delete it from disk and the graph
///
/// `excludes` is a list of user-supplied `PackageIdent`s.
/// `even_if_running` is a flag indictating that the package should be uninstalled even if it is
/// running.
pub async fn uninstall<U>(ui: &mut U,
                          ident: &PackageIdent,
                          fs_root_path: &Path,
                          execution_strategy: ExecutionStrategy,
                          scope: Scope,
                          excludes: &[PackageIdent],
                          even_if_running: bool)
                          -> Result<()>
    where U: UIWriter
{
    // 1.
    let pkg_install = PackageInstall::load(ident, Some(fs_root_path))?;
    let ident = pkg_install.ident();
    ui.begin(format!("Uninstalling {}", &ident))?;

    let services = supervisor_services().await?;
    if !services.is_empty() {
        ui.status(Status::Determining,
                  "list of running services in supervisor")?;
        for s in services.iter() {
            ui.status(Status::Found, format!("running service {}", s))?;
        }
    }

    // 2.
    let mut graph = PackageGraph::from_root_path(fs_root_path)?;

    // 3.
    let deps = graph.owned_ordered_deps(&ident);

    // 4.
    match graph.count_rdeps(&ident) {
        None => {
            // package not in graph - this shouldn't happen but could be a race condition in Step 2
            // with another hab uninstall. We can continue as what we wanted (package to
            // be removed) has already happened. We're going to continue and try and delete down
            // through the dependency tree anyway
            ui.warn(format!("Tried to find dependant packages of {} but it wasn't in graph.  \
                             Maybe another uninstall command was run at the same time?",
                            &ident))?;
        }
        Some(0) => {
            maybe_delete(ui,
                         &fs_root_path,
                         &pkg_install,
                         execution_strategy,
                         &excludes,
                         &services,
                         even_if_running)?;
            graph.remove(&ident);
        }
        Some(c) => {
            return Err(Error::CannotRemovePackage(ident.clone(), c));
        }
    }

    // 5.
    let mut count = 0;
    match scope {
        Scope::Package => {
            ui.status(Status::Skipping, "dependencies (--no-deps specified)")?;
        }
        Scope::PackageAndDependencies => {
            for p in &deps {
                match graph.count_rdeps(&p) {
                    None => {
                        // package not in graph - this shouldn't happen but could be a race
                        // condition in Step 2 with another hab uninstall. We can
                        // continue as what we wanted (package to be removed) has already happened.
                        // We're going to continue and try and delete down through the
                        // dependency tree anyway
                        ui.warn(format!("Tried to find dependant packages of {} but it wasn't \
                                         in graph.  Maybe another uninstall command was run at \
                                         the same time?",
                                        &p))?;
                    }
                    Some(0) => {
                        let install = PackageInstall::load(&p, Some(fs_root_path))?;
                        maybe_delete(ui,
                                     &fs_root_path,
                                     &install,
                                     execution_strategy,
                                     &excludes,
                                     &services,
                                     even_if_running)?;

                        graph.remove(&p);
                        count += 1;
                    }
                    Some(c) => {
                        ui.status(Status::Skipping,
                                  format!("{}. It is a dependency of {} packages", &p, c))?;
                    }
                }
            }
        }
    }

    match execution_strategy {
        ExecutionStrategy::DryRun => {
            ui.end(format!("Would uninstall {} and {} dependencies (Dry run)",
                           &ident, count))?;
        }
        ExecutionStrategy::Run => {
            ui.end(format!("Uninstall of {} and {} dependencies complete",
                           &ident, count))?;
        }
    };
    Ok(())
}

/// Check if we have a launcher/supervisor running out of this habitat root.
/// If the launcher PID file exists then the supervisor is up and running
fn launcher_is_running(fs_root_path: &Path) -> bool {
    let launcher_root = hfs::launcher_root_path(Some(fs_root_path));
    let pid_file_path = launcher_root.join("PID");

    pid_file_path.is_file()
}

async fn supervisor_services() -> Result<Vec<PackageIdent>> {
    if !launcher_is_running(&*FS_ROOT) {
        return Ok(vec![]);
    }

    let cfg = config::load()?;
    let secret_key = config::ctl_secret_key(&cfg)?;
    let listen_ctl_addr = ListenCtlAddr::default();
    let msg = habitat_sup_protocol::ctl::SvcStatus::default();

    let mut out: Vec<PackageIdent> = vec![];
    let mut response = SrvClient::request(&listen_ctl_addr, &secret_key, msg).await?;
    while let Some(message_result) = response.next().await {
        let reply = message_result?;
        match reply.message_id() {
            "ServiceStatus" => {
                let m = reply.parse::<habitat_sup_protocol::types::ServiceStatus>()
                             .map_err(SrvClientError::Decode)?;
                out.push(m.ident.into());
            }
            "NetOk" => (),
            "NetErr" => {
                let err = reply.parse::<habitat_sup_protocol::net::NetErr>()
                               .map_err(SrvClientError::Decode)?;
                return Err(SrvClientError::from(err).into());
            }
            _ => {
                warn!("Unexpected status message, {:?}", reply);
            }
        }
    }
    Ok(out)
}

/// Delete a package from disk, depending upon the ExecutionStrategy supplied
///
/// Returns:
///   Ok(true) - package is deleted
///   Ok(false) - package would be deleted but it's a dry run
///   Err(_) -  IO problem deleting package from filesystem
fn maybe_delete<U>(ui: &mut U,
                   fs_root_path: &Path,
                   install: &PackageInstall,
                   strategy: ExecutionStrategy,
                   excludes: &[PackageIdent],
                   services: &[PackageIdent],
                   even_if_running: bool)
                   -> Result<bool>
    where U: UIWriter
{
    let ident = install.ident();
    let pkg_root_path = hfs::pkg_root_path(Some(fs_root_path));

    let hab = PackageIdent::from_str("core/hab")?;
    if ident.satisfies(&hab) {
        ui.status(Status::Skipping,
                  format!("{}. You can't uninstall core/hab", &ident))?;
        return Ok(false);
    }

    if !even_if_running {
        let is_running = services.iter().any(|i| i.satisfies(ident));
        if is_running {
            ui.status(Status::Skipping,
                      format!("{}. It is currently running in the supervisor", &ident))?;
            return Ok(false);
        }
    }

    // The excludes list could be looser than the fully qualified idents.  E.g. if core/redis is on
    // the exclude list then we should exclude core/redis/1.1.0/20180608091936.  We use the
    // `Identifiable` trait which supplies this logic for PackageIdents
    let should_exclude = excludes.iter().any(|i| i.satisfies(ident));
    if should_exclude {
        ui.status(Status::Skipping,
                  format!("{}. It is on the exclusion list", &ident))?;
        return Ok(false);
    }

    match strategy {
        ExecutionStrategy::DryRun => {
            ui.status(Status::DryRunDeleting, &ident)?;
            Ok(false)
        }
        ExecutionStrategy::Run => {
            ui.status(Status::Deleting, &ident)?;
            let pkg_dir = install.installed_path();
            do_clean_delete(&pkg_root_path, &pkg_dir)
        }
    }
}

/// Delete empty parent directories from a given path. don't traverse above
/// the `pkg_root_path`
fn do_clean_delete(pkg_root_path: &Path, real_install_path: &Path) -> Result<bool> {
    // This match will always return Ok(Path) as the install path is always 4 levels
    // below the pkg_root_path
    match real_install_path.parent() {
        Some(real_install_base) => {
            let temp_install_path = temp_package_directory(real_install_path)?.path()
                                                                              .to_path_buf();
            fs::rename(&real_install_path, &temp_install_path)?;
            fs::remove_dir_all(&temp_install_path)?;

            for p in real_install_base.ancestors() {
                // Let's be safe and not rm below the package directories
                if p == pkg_root_path {
                    break;
                }
                match p.read_dir() {
                    Ok(contents) => {
                        // This will calculate the amount of items in the directory
                        match contents.count() {
                            0 => fs::remove_dir(&p)?,
                            _ => break,
                        }
                    }
                    Err(e) => return Err(Error::HabitatCore(herror::Error::IO(e))),
                }
            }
            Ok(true)
        }
        None => unreachable!("Install path doesn't have a parent"),
    }
}
<commit_msg>Cleanup even_if_running uninstall flag<commit_after>use super::{ExecutionStrategy,
            Scope};
use crate::{config,
            error::{Error,
                    Result}};
use futures::stream::StreamExt;
use habitat_common::{cli::FS_ROOT,
                     package_graph::PackageGraph,
                     types::ListenCtlAddr,
                     ui::{Status,
                          UIWriter}};
use habitat_core::{error as herror,
                   fs as hfs,
                   package::{list::temp_package_directory,
                             Identifiable,
                             PackageIdent,
                             PackageInstall}};
use habitat_sup_client::{SrvClient,
                         SrvClientError};
use habitat_sup_protocol;
use std::{fs,
          path::Path,
          str::FromStr};

/// Delete a package and all dependencies which are not used by other packages.
/// We do an ordered traverse of the dependencies, updating the graph as we delete a
/// package. This lets us use simple logic where we continually check if the package
/// we're trying to delete currently has no packages depending on it.
///
/// The full logic is:
/// 1. We find the fully qualified package ident and all its dependencies
/// 2. We find all packages on the filesystem and convert them into a graph
/// 3. We do a BFS on the graph to get the dependencies in order
/// 4. We check if the specified package has any reverse deps
///     4a. If there are, we throw an error
///     4b. If not, we delete the package
/// 5. For each dependency we check if there are any packages which depend on it
///     5a. If there are, we skip it
///     5b. If there are not, we delete it from disk and the graph
///
/// `excludes` is a list of user-supplied `PackageIdent`s.
/// `even_if_running` is a flag indictating that the package should be uninstalled even if it is
/// running.
pub async fn uninstall<U>(ui: &mut U,
                          ident: &PackageIdent,
                          fs_root_path: &Path,
                          execution_strategy: ExecutionStrategy,
                          scope: Scope,
                          excludes: &[PackageIdent],
                          even_if_running: bool)
                          -> Result<()>
    where U: UIWriter
{
    // 1.
    let pkg_install = PackageInstall::load(ident, Some(fs_root_path))?;
    let ident = pkg_install.ident();
    ui.begin(format!("Uninstalling {}", &ident))?;

    let services = if even_if_running {
        // If we want to uninstall the package even if it is running, dont look up the services that
        // are running just use an empty vector.
        Vec::new()
    } else {
        supervisor_services().await?
    };
    if !services.is_empty() {
        ui.status(Status::Determining,
                  "list of running services in supervisor")?;
        for s in services.iter() {
            ui.status(Status::Found, format!("running service {}", s))?;
        }
    }

    // 2.
    let mut graph = PackageGraph::from_root_path(fs_root_path)?;

    // 3.
    let deps = graph.owned_ordered_deps(&ident);

    // 4.
    match graph.count_rdeps(&ident) {
        None => {
            // package not in graph - this shouldn't happen but could be a race condition in Step 2
            // with another hab uninstall. We can continue as what we wanted (package to
            // be removed) has already happened. We're going to continue and try and delete down
            // through the dependency tree anyway
            ui.warn(format!("Tried to find dependant packages of {} but it wasn't in graph.  \
                             Maybe another uninstall command was run at the same time?",
                            &ident))?;
        }
        Some(0) => {
            maybe_delete(ui,
                         &fs_root_path,
                         &pkg_install,
                         execution_strategy,
                         &excludes,
                         &services)?;
            graph.remove(&ident);
        }
        Some(c) => {
            return Err(Error::CannotRemovePackage(ident.clone(), c));
        }
    }

    // 5.
    let mut count = 0;
    match scope {
        Scope::Package => {
            ui.status(Status::Skipping, "dependencies (--no-deps specified)")?;
        }
        Scope::PackageAndDependencies => {
            for p in &deps {
                match graph.count_rdeps(&p) {
                    None => {
                        // package not in graph - this shouldn't happen but could be a race
                        // condition in Step 2 with another hab uninstall. We can
                        // continue as what we wanted (package to be removed) has already happened.
                        // We're going to continue and try and delete down through the
                        // dependency tree anyway
                        ui.warn(format!("Tried to find dependant packages of {} but it wasn't \
                                         in graph.  Maybe another uninstall command was run at \
                                         the same time?",
                                        &p))?;
                    }
                    Some(0) => {
                        let install = PackageInstall::load(&p, Some(fs_root_path))?;
                        maybe_delete(ui,
                                     &fs_root_path,
                                     &install,
                                     execution_strategy,
                                     &excludes,
                                     &services)?;

                        graph.remove(&p);
                        count += 1;
                    }
                    Some(c) => {
                        ui.status(Status::Skipping,
                                  format!("{}. It is a dependency of {} packages", &p, c))?;
                    }
                }
            }
        }
    }

    match execution_strategy {
        ExecutionStrategy::DryRun => {
            ui.end(format!("Would uninstall {} and {} dependencies (Dry run)",
                           &ident, count))?;
        }
        ExecutionStrategy::Run => {
            ui.end(format!("Uninstall of {} and {} dependencies complete",
                           &ident, count))?;
        }
    };
    Ok(())
}

/// Check if we have a launcher/supervisor running out of this habitat root.
/// If the launcher PID file exists then the supervisor is up and running
fn launcher_is_running(fs_root_path: &Path) -> bool {
    let launcher_root = hfs::launcher_root_path(Some(fs_root_path));
    let pid_file_path = launcher_root.join("PID");

    pid_file_path.is_file()
}

async fn supervisor_services() -> Result<Vec<PackageIdent>> {
    if !launcher_is_running(&*FS_ROOT) {
        return Ok(vec![]);
    }

    let cfg = config::load()?;
    let secret_key = config::ctl_secret_key(&cfg)?;
    let listen_ctl_addr = ListenCtlAddr::default();
    let msg = habitat_sup_protocol::ctl::SvcStatus::default();

    let mut out: Vec<PackageIdent> = vec![];
    let mut response = SrvClient::request(&listen_ctl_addr, &secret_key, msg).await?;
    while let Some(message_result) = response.next().await {
        let reply = message_result?;
        match reply.message_id() {
            "ServiceStatus" => {
                let m = reply.parse::<habitat_sup_protocol::types::ServiceStatus>()
                             .map_err(SrvClientError::Decode)?;
                out.push(m.ident.into());
            }
            "NetOk" => (),
            "NetErr" => {
                let err = reply.parse::<habitat_sup_protocol::net::NetErr>()
                               .map_err(SrvClientError::Decode)?;
                return Err(SrvClientError::from(err).into());
            }
            _ => {
                warn!("Unexpected status message, {:?}", reply);
            }
        }
    }
    Ok(out)
}

/// Delete a package from disk, depending upon the ExecutionStrategy supplied
///
/// Returns:
///   Ok(true) - package is deleted
///   Ok(false) - package would be deleted but it's a dry run
///   Err(_) -  IO problem deleting package from filesystem
fn maybe_delete<U>(ui: &mut U,
                   fs_root_path: &Path,
                   install: &PackageInstall,
                   strategy: ExecutionStrategy,
                   excludes: &[PackageIdent],
                   services: &[PackageIdent])
                   -> Result<bool>
    where U: UIWriter
{
    let ident = install.ident();
    let pkg_root_path = hfs::pkg_root_path(Some(fs_root_path));

    let hab = PackageIdent::from_str("core/hab")?;
    if ident.satisfies(&hab) {
        ui.status(Status::Skipping,
                  format!("{}. You can't uninstall core/hab", &ident))?;
        return Ok(false);
    }

    let is_running = services.iter().any(|i| i.satisfies(ident));
    if is_running {
        ui.status(Status::Skipping,
                  format!("{}. It is currently running in the supervisor", &ident))?;
        return Ok(false);
    }

    // The excludes list could be looser than the fully qualified idents.  E.g. if core/redis is on
    // the exclude list then we should exclude core/redis/1.1.0/20180608091936.  We use the
    // `Identifiable` trait which supplies this logic for PackageIdents
    let should_exclude = excludes.iter().any(|i| i.satisfies(ident));
    if should_exclude {
        ui.status(Status::Skipping,
                  format!("{}. It is on the exclusion list", &ident))?;
        return Ok(false);
    }

    match strategy {
        ExecutionStrategy::DryRun => {
            ui.status(Status::DryRunDeleting, &ident)?;
            Ok(false)
        }
        ExecutionStrategy::Run => {
            ui.status(Status::Deleting, &ident)?;
            let pkg_dir = install.installed_path();
            do_clean_delete(&pkg_root_path, &pkg_dir)
        }
    }
}

/// Delete empty parent directories from a given path. don't traverse above
/// the `pkg_root_path`
fn do_clean_delete(pkg_root_path: &Path, real_install_path: &Path) -> Result<bool> {
    // This match will always return Ok(Path) as the install path is always 4 levels
    // below the pkg_root_path
    match real_install_path.parent() {
        Some(real_install_base) => {
            let temp_install_path = temp_package_directory(real_install_path)?.path()
                                                                              .to_path_buf();
            fs::rename(&real_install_path, &temp_install_path)?;
            fs::remove_dir_all(&temp_install_path)?;

            for p in real_install_base.ancestors() {
                // Let's be safe and not rm below the package directories
                if p == pkg_root_path {
                    break;
                }
                match p.read_dir() {
                    Ok(contents) => {
                        // This will calculate the amount of items in the directory
                        match contents.count() {
                            0 => fs::remove_dir(&p)?,
                            _ => break,
                        }
                    }
                    Err(e) => return Err(Error::HabitatCore(herror::Error::IO(e))),
                }
            }
            Ok(true)
        }
        None => unreachable!("Install path doesn't have a parent"),
    }
}
<|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 https://mozilla.org/MPL/2.0/. */

use rustc::hir::{self, HirId};
use rustc::lint::{LateContext, LateLintPass, LintArray, LintContext, LintPass};
use rustc::ty;
use std::boxed;
use std::env;
use std::error::Error;
use std::fmt;
use std::fs;
use std::io;
use std::path;
use syntax::ast;
use weedle;

declare_lint!(
    WEBIDL_INHERIT_CORRECT,
    Deny,
    "Warn and report usage of incorrect webidl inheritance"
);

pub(crate) struct WebIdlPass {
    symbols: crate::Symbols,
}

#[derive(Clone, Debug)]
pub struct ParentMismatchError {
    name: String,
    rust_parent: String,
    webidl_parent: String,
}

impl fmt::Display for ParentMismatchError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "webidl-rust inheritance mismatch, rust: {:?}, rust parent: {:?}, webidl parent: {:?}",
            self.name, self.rust_parent, self.webidl_parent
        )
    }
}

impl Error for ParentMismatchError {
    fn description(&self) -> &str {
        "ParentMismatchError"
    }

    fn cause(&self) -> Option<&Error> {
        None
    }
}

impl WebIdlPass {
    pub fn new(symbols: crate::Symbols) -> WebIdlPass {
        WebIdlPass { symbols }
    }
}

fn get_ty_name(ty: &str) -> &str {
    if let Some(i) = ty.rfind(':') {
        &ty[i + 1..]
    } else {
        ty
    }
}

fn get_webidl_path(struct_name: &str) -> io::Result<path::PathBuf> {
    let mut dir = env::current_dir()?;
    dir.push("components/script/dom/webidls/");
    dir.push(format!("{}.webidl", struct_name));

    Ok(dir)
}

fn is_webidl_ty(symbols: &crate::Symbols, cx: &LateContext, ty: &ty::TyS) -> bool {
    let mut ret = false;
    ty.maybe_walk(|t| {
        match t.sty {
            ty::Adt(did, _substs) => {
                if cx.tcx.has_attr(did.did, symbols.webidl) {
                    ret = true;
                }
                false
            },
            ty::Ref(..) => false,    // don't recurse down &ptrs
            ty::RawPtr(..) => false, // don't recurse down *ptrs
            ty::FnDef(..) | ty::FnPtr(_) => false,
            _ => true,
        }
    });
    ret
}

fn check_inherits(code: &str, name: &str, parent_name: &str) -> Result<(), Box<Error>> {
    let idl = weedle::parse(code).expect("Invalid webidl provided");
    let mut inherits = "";

    for def in idl {
        if let weedle::Definition::Interface(def) = def {
            if let Some(parent) = def.inheritance {
                inherits = parent.identifier.0;
                break;
            }
        } else if let weedle::Definition::CallbackInterface(def) = def {
            if let Some(parent) = def.inheritance {
                inherits = parent.identifier.0;
                break;
            }
        }
    }

    if inherits == parent_name {
        return Ok(());
    }

    // If there is no parent, first field must be of type Reflector.
    if inherits == "" && parent_name == "Reflector" {
        return Ok(());
    }

    if inherits == "" &&
        name == "PaintRenderingContext2D" &&
        parent_name == "CanvasRenderingContext2D"
    {
        // PaintRenderingContext2D embeds a CanvasRenderingContext2D
        // instead of a Reflector as an optimization,
        // but this is fine since CanvasRenderingContext2D
        // also has a reflector
        return Ok(());
    }

    Err(boxed::Box::from(ParentMismatchError {
        name: name.to_string(),
        rust_parent: parent_name.to_string(),
        webidl_parent: inherits.to_string(),
    }))
}

fn check_webidl(name: &str, parent_name: &Option<String>) -> Result<(), Box<Error>> {
    let path = get_webidl_path(&name)?;
    if let Some(parent) = parent_name {
        let code = fs::read_to_string(path)?;
        return check_inherits(&code, name, &parent);
    }

    Ok(())
}

impl LintPass for WebIdlPass {
    fn name(&self) -> &'static str {
        "ServoWebIDLPass"
    }

    fn get_lints(&self) -> LintArray {
        lint_array!(WEBIDL_INHERIT_CORRECT)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for WebIdlPass {
    fn check_struct_def(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        def: &'tcx hir::VariantData,
        n: ast::Name,
        _gen: &'tcx hir::Generics,
        id: HirId,
    ) {
        let def_id = cx.tcx.hir().local_def_id_from_hir_id(id);
        if !is_webidl_ty(&self.symbols, cx, cx.tcx.type_of(def_id)) {
            return;
        }

        let item = match cx.tcx.hir().get_by_hir_id(id) {
            hir::Node::Item(item) => item,
            _ => cx
                .tcx
                .hir()
                .expect_item_by_hir_id(cx.tcx.hir().get_parent_item(id)),
        };

        let parent_name = def.fields().iter().next().map(|field| {
            let def_id = cx.tcx.hir().local_def_id_from_hir_id(field.hir_id);
            let ty = cx.tcx.type_of(def_id).to_string();
            get_ty_name(&ty).to_string()
        });

        let struct_name = n.to_string();
        match check_webidl(&struct_name, &parent_name) {
            Ok(()) => {},
            Err(e) => {
                let description = format!("{}", e);
                cx.span_lint(WEBIDL_INHERIT_CORRECT, item.ident.span, &description)
            },
        };
    }
}
<commit_msg>Auto merge of #23512 - cpud36:fix_incorrect_relative_path_in_webidl_plugin, r=Manishearth<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 https://mozilla.org/MPL/2.0/. */

use rustc::hir::{self, HirId};
use rustc::lint::{LateContext, LateLintPass, LintArray, LintContext, LintPass};
use rustc::ty;
use std::boxed;
use std::env;
use std::error::Error;
use std::fmt;
use std::fs;
use std::io;
use std::path;
use syntax::ast;
use weedle;

declare_lint!(
    WEBIDL_INHERIT_CORRECT,
    Deny,
    "Warn and report usage of incorrect webidl inheritance"
);

pub(crate) struct WebIdlPass {
    symbols: crate::Symbols,
}

#[derive(Clone, Debug)]
pub struct ParentMismatchError {
    name: String,
    rust_parent: String,
    webidl_parent: String,
}

impl fmt::Display for ParentMismatchError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "webidl-rust inheritance mismatch, rust: {:?}, rust parent: {:?}, webidl parent: {:?}",
            self.name, self.rust_parent, self.webidl_parent
        )
    }
}

impl Error for ParentMismatchError {
    fn description(&self) -> &str {
        "ParentMismatchError"
    }

    fn cause(&self) -> Option<&Error> {
        None
    }
}

impl WebIdlPass {
    pub fn new(symbols: crate::Symbols) -> WebIdlPass {
        WebIdlPass { symbols }
    }
}

fn get_ty_name(ty: &str) -> &str {
    if let Some(i) = ty.rfind(':') {
        &ty[i + 1..]
    } else {
        ty
    }
}

fn get_manifest_dir() -> io::Result<path::PathBuf> {
    match env::var("CARGO_MANIFEST_DIR") {
        Ok(var) => {
            let mut dir = path::PathBuf::new();
            dir.push(var);
            Ok(dir)
        },
        Err(env::VarError::NotPresent) => Err(io::Error::new(
            io::ErrorKind::NotFound,
            "CARGO_MANIFEST_DIR environment variable was not found",
        )),
        Err(env::VarError::NotUnicode(_)) => Err(io::Error::new(
            io::ErrorKind::InvalidData,
            "CARGO_MANIFEST_DIR environment variable's contents are non valid UTF-8",
        )),
    }
}

fn get_webidl_path(struct_name: &str) -> io::Result<path::PathBuf> {
    let mut dir = get_manifest_dir()?;
    dir.push("dom/webidls/");
    dir.push(format!("{}.webidl", struct_name));

    Ok(dir)
}

fn is_webidl_ty(symbols: &crate::Symbols, cx: &LateContext, ty: &ty::TyS) -> bool {
    let mut ret = false;
    ty.maybe_walk(|t| {
        match t.sty {
            ty::Adt(did, _substs) => {
                if cx.tcx.has_attr(did.did, symbols.webidl) {
                    ret = true;
                }
                false
            },
            ty::Ref(..) => false,    // don't recurse down &ptrs
            ty::RawPtr(..) => false, // don't recurse down *ptrs
            ty::FnDef(..) | ty::FnPtr(_) => false,
            _ => true,
        }
    });
    ret
}

fn check_inherits(code: &str, name: &str, parent_name: &str) -> Result<(), Box<Error>> {
    let idl = weedle::parse(code).expect("Invalid webidl provided");
    let mut inherits = "";

    for def in idl {
        if let weedle::Definition::Interface(def) = def {
            if let Some(parent) = def.inheritance {
                inherits = parent.identifier.0;
                break;
            }
        } else if let weedle::Definition::CallbackInterface(def) = def {
            if let Some(parent) = def.inheritance {
                inherits = parent.identifier.0;
                break;
            }
        }
    }

    if inherits == parent_name {
        return Ok(());
    }

    // If there is no parent, first field must be of type Reflector.
    if inherits == "" && parent_name == "Reflector" {
        return Ok(());
    }

    if inherits == "" &&
        name == "PaintRenderingContext2D" &&
        parent_name == "CanvasRenderingContext2D"
    {
        // PaintRenderingContext2D embeds a CanvasRenderingContext2D
        // instead of a Reflector as an optimization,
        // but this is fine since CanvasRenderingContext2D
        // also has a reflector
        return Ok(());
    }

    Err(boxed::Box::from(ParentMismatchError {
        name: name.to_string(),
        rust_parent: parent_name.to_string(),
        webidl_parent: inherits.to_string(),
    }))
}

fn check_webidl(name: &str, parent_name: &Option<String>) -> Result<(), Box<Error>> {
    let path = get_webidl_path(&name)?;
    if let Some(parent) = parent_name {
        let code = fs::read_to_string(path)?;
        return check_inherits(&code, name, &parent);
    }

    Ok(())
}

impl LintPass for WebIdlPass {
    fn name(&self) -> &'static str {
        "ServoWebIDLPass"
    }

    fn get_lints(&self) -> LintArray {
        lint_array!(WEBIDL_INHERIT_CORRECT)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for WebIdlPass {
    fn check_struct_def(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        def: &'tcx hir::VariantData,
        n: ast::Name,
        _gen: &'tcx hir::Generics,
        id: HirId,
    ) {
        let def_id = cx.tcx.hir().local_def_id_from_hir_id(id);
        if !is_webidl_ty(&self.symbols, cx, cx.tcx.type_of(def_id)) {
            return;
        }

        let item = match cx.tcx.hir().get_by_hir_id(id) {
            hir::Node::Item(item) => item,
            _ => cx
                .tcx
                .hir()
                .expect_item_by_hir_id(cx.tcx.hir().get_parent_item(id)),
        };

        let parent_name = def.fields().iter().next().map(|field| {
            let def_id = cx.tcx.hir().local_def_id_from_hir_id(field.hir_id);
            let ty = cx.tcx.type_of(def_id).to_string();
            get_ty_name(&ty).to_string()
        });

        let struct_name = n.to_string();
        match check_webidl(&struct_name, &parent_name) {
            Ok(()) => {},
            Err(e) => {
                let description = format!("{}", e);
                cx.span_lint(WEBIDL_INHERIT_CORRECT, item.ident.span, &description)
            },
        };
    }
}
<|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/. */

<%! from data import to_rust_ident %>
<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import ALL_SIZES, PHYSICAL_SIDES, LOGICAL_SIDES %>

<% data.new_style_struct("Position", inherited=False) %>

// "top" / "left" / "bottom" / "right"
% for side in PHYSICAL_SIDES:
    ${helpers.predefined_type(side, "LengthOrPercentageOrAuto",
                              "computed::LengthOrPercentageOrAuto::Auto",
                              spec="https://www.w3.org/TR/CSS2/visuren.html#propdef-%s" % side,
                              animatable=True)}
% endfor
// offset-* logical properties, map to "top" / "left" / "bottom" / "right"
% for side in LOGICAL_SIDES:
    ${helpers.predefined_type("offset-%s" % side, "LengthOrPercentageOrAuto",
                              "computed::LengthOrPercentageOrAuto::Auto",
                              spec="https://drafts.csswg.org/css-logical-props/#propdef-offset-%s" % side,
                              animatable=True, logical=True)}
% endfor

${helpers.predefined_type("z-index", "IntegerOrAuto",
                          "Either::Second(Auto)",
                          spec="https://www.w3.org/TR/CSS2/visuren.html#z-index",
                          creates_stacking_context=True,
                          animatable="True")}

// CSS Flexible Box Layout Module Level 1
// http://www.w3.org/TR/css3-flexbox/

// Flex container properties
${helpers.single_keyword("flex-direction", "row row-reverse column column-reverse",
                         spec="https://drafts.csswg.org/css-flexbox/#flex-direction-property",
                         extra_prefixes="webkit", animatable=False)}

${helpers.single_keyword("flex-wrap", "nowrap wrap wrap-reverse",
                         spec="https://drafts.csswg.org/css-flexbox/#flex-wrap-property",
                         extra_prefixes="webkit", animatable=False)}

% if product == "servo":
    // FIXME: Update Servo to support the same Syntax as Gecko.
    ${helpers.single_keyword("justify-content", "flex-start stretch flex-end center space-between space-around",
                             extra_prefixes="webkit",
                             spec="https://drafts.csswg.org/css-align/#propdef-justify-content",
                             animatable=False)}
% else:
    ${helpers.predefined_type(name="justify-content",
                              type="AlignJustifyContent",
                              initial_value="specified::AlignJustifyContent::normal()",
                              spec="https://drafts.csswg.org/css-align/#propdef-justify-content",
                              extra_prefixes="webkit",
                              animatable=False)}
% endif

% if product == "servo":
    // FIXME: Update Servo to support the same Syntax as Gecko.
    ${helpers.single_keyword("align-content", "stretch flex-start flex-end center space-between space-around",
                             extra_prefixes="webkit",
                             spec="https://drafts.csswg.org/css-align/#propdef-align-content",
                             animatable=False)}

    ${helpers.single_keyword("align-items",
                             "stretch flex-start flex-end center baseline",
                             need_clone=True,
                             extra_prefixes="webkit",
                             spec="https://drafts.csswg.org/css-flexbox/#align-items-property",
                             animatable=False)}
% else:
    ${helpers.predefined_type(name="align-content",
                              type="AlignJustifyContent",
                              initial_value="specified::AlignJustifyContent::normal()",
                              spec="https://drafts.csswg.org/css-align/#propdef-align-content",
                              extra_prefixes="webkit",
                              animatable=False)}

    ${helpers.predefined_type(name="align-items",
                              type="AlignItems",
                              initial_value="specified::AlignItems::normal()",
                              spec="https://drafts.csswg.org/css-align/#propdef-align-items",
                              extra_prefixes="webkit",
                              animatable=False)}

    ${helpers.predefined_type(name="justify-items",
                              type="JustifyItems",
                              initial_value="specified::JustifyItems::auto()",
                              spec="https://drafts.csswg.org/css-align/#propdef-justify-items",
                              animatable=False)}
% endif

// Flex item properties
${helpers.predefined_type("flex-grow", "Number",
                          "0.0", "parse_non_negative",
                          spec="https://drafts.csswg.org/css-flexbox/#flex-grow-property",
                          extra_prefixes="webkit",
                          needs_context=False,
                          animatable=True)}

${helpers.predefined_type("flex-shrink", "Number",
                          "1.0", "parse_non_negative",
                          spec="https://drafts.csswg.org/css-flexbox/#flex-shrink-property",
                          extra_prefixes="webkit",
                          needs_context=False,
                          animatable=True)}

// https://drafts.csswg.org/css-align/#align-self-property
% if product == "servo":
    // FIXME: Update Servo to support the same syntax as Gecko.
    ${helpers.single_keyword("align-self", "auto stretch flex-start flex-end center baseline",
                             need_clone=True,
                             extra_prefixes="webkit",
                             spec="https://drafts.csswg.org/css-flexbox/#propdef-align-self",
                             animatable=False)}
% else:
    ${helpers.predefined_type(name="align-self",
                              type="AlignJustifySelf",
                              initial_value="specified::AlignJustifySelf::auto()",
                              spec="https://drafts.csswg.org/css-align/#align-self-property",
                              extra_prefixes="webkit",
                              animatable=False)}

    ${helpers.predefined_type(name="justify-self",
                              type="AlignJustifySelf",
                              initial_value="specified::AlignJustifySelf::auto()",
                              spec="https://drafts.csswg.org/css-align/#justify-self-property",
                              animatable=False)}
% endif

// https://drafts.csswg.org/css-flexbox/#propdef-order
${helpers.predefined_type("order", "Integer", "0",
                          animatable=True,
                          spec="https://drafts.csswg.org/css-flexbox/#order-property")}

// FIXME: Gecko doesn't support content value yet.
// FIXME: This property should be animatable.
${helpers.predefined_type("flex-basis",
                          "LengthOrPercentageOrAuto" if product == "gecko" else
                          "LengthOrPercentageOrAutoOrContent",
                          "computed::LengthOrPercentageOrAuto::Auto" if product == "gecko" else
                          "computed::LengthOrPercentageOrAutoOrContent::Auto",
                          spec="https://drafts.csswg.org/css-flexbox/#flex-basis-property",
                          extra_prefixes="webkit",
                          animatable=True if product == "gecko" else False)}

% for (size, logical) in ALL_SIZES:
    <%
      spec = "https://drafts.csswg.org/css-box/#propdef-%s"
      if logical:
        spec = "https://drafts.csswg.org/css-logical-props/#propdef-%s"
    %>
    // width, height, block-size, inline-size
    ${helpers.predefined_type("%s" % size,
                              "LengthOrPercentageOrAuto",
                              "computed::LengthOrPercentageOrAuto::Auto",
                              "parse_non_negative",
                              needs_context=False,
                              spec=spec % size,
                              animatable=True, logical = logical)}
    % if product == "gecko":
        % for min_max in ["min", "max"]:
            <%
                MinMax = min_max.title()
                initial = "None" if "max" == min_max else "Auto"
            %>

            // min-width, min-height, min-block-size, min-inline-size,
            // max-width, max-height, max-block-size, max-inline-size
            //
            // Keyword values are only valid in the inline direction; they must
            // be replaced with auto/none in block.
            <%helpers:longhand name="${min_max}-${size}" spec="${spec % ('%s-%s' % (min_max, size))}"
                               animatable="True" logical="${logical}" predefined_type="${MinMax}Length">

                use std::fmt;
                use style_traits::ToCss;
                use values::HasViewportPercentage;
                use values::specified::${MinMax}Length;

                impl HasViewportPercentage for SpecifiedValue {
                    fn has_viewport_percentage(&self) -> bool {
                        self.0.has_viewport_percentage()
                    }
                }

                pub mod computed_value {
                    pub type T = ::values::computed::${MinMax}Length;
                }

                #[derive(PartialEq, Clone, Debug)]
                #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
                pub struct SpecifiedValue(${MinMax}Length);

                #[inline]
                pub fn get_initial_value() -> computed_value::T {
                    use values::computed::${MinMax}Length;
                    ${MinMax}Length::${initial}
                }
                fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
                    ${MinMax}Length::parse(context, input).map(SpecifiedValue)
                }

                impl ToCss for SpecifiedValue {
                    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
                        self.0.to_css(dest)
                    }
                }

                impl ToComputedValue for SpecifiedValue {
                    type ComputedValue = computed_value::T;
                    #[inline]
                    fn to_computed_value(&self, context: &Context) -> computed_value::T {
                        use values::computed::${MinMax}Length;
                        let computed = self.0.to_computed_value(context);

                        // filter out keyword values in the block direction
                        % if logical:
                            % if "block" in size:
                                if let ${MinMax}Length::ExtremumLength(..) = computed {
                                    return get_initial_value()
                                }
                            % endif
                        % else:
                            if let ${MinMax}Length::ExtremumLength(..) = computed {
                                <% is_height = "true" if "height" in size else "false" %>
                                if ${is_height} != context.style().writing_mode.is_vertical() {
                                    return get_initial_value()
                                }
                            }
                        % endif
                        computed
                    }

                    #[inline]
                    fn from_computed_value(computed: &computed_value::T) -> Self {
                        SpecifiedValue(ToComputedValue::from_computed_value(computed))
                    }
                }
            </%helpers:longhand>
        % endfor
    % else:
        // servo versions (no keyword support)
        ${helpers.predefined_type("min-%s" % size,
                                  "LengthOrPercentage",
                                  "computed::LengthOrPercentage::Length(Au(0))",
                                  "parse_non_negative",
                                  needs_context=False,
                                  spec=spec % ("min-%s" % size),
                                  animatable=True, logical = logical)}
        ${helpers.predefined_type("max-%s" % size,
                                  "LengthOrPercentageOrNone",
                                  "computed::LengthOrPercentageOrNone::None",
                                  "parse_non_negative",
                                  needs_context=False,
                                  spec=spec % ("min-%s" % size),
                                  animatable=True, logical = logical)}
    % endif
% endfor

${helpers.single_keyword("box-sizing",
                         "content-box border-box",
                         extra_prefixes="moz webkit",
                         spec="https://drafts.csswg.org/css-ui/#propdef-box-sizing",
                         animatable=False)}

${helpers.single_keyword("object-fit", "fill contain cover none scale-down",
                         products="gecko", animatable=False,
                         spec="https://drafts.csswg.org/css-images/#propdef-object-fit")}

${helpers.predefined_type("object-position",
                          "Position",
                          "computed::Position::zero()",
                          products="gecko",
                          boxed="True",
                          spec="https://drafts.csswg.org/css-images-3/#the-object-position",
                          animatable=True)}

% for kind in ["row", "column"]:
    ${helpers.predefined_type("grid-%s-gap" % kind,
                              "LengthOrPercentage",
                              "computed::LengthOrPercentage::Length(Au(0))",
                              spec="https://drafts.csswg.org/css-grid/#propdef-grid-%s-gap" % kind,
                              animatable=True,
                              products="gecko")}

    % for range in ["start", "end"]:
        ${helpers.predefined_type("grid-%s-%s" % (kind, range),
                                  "GridLine",
                                  "Default::default()",
                                  animatable=False,
                                  spec="https://drafts.csswg.org/css-grid/#propdef-grid-%s-%s" % (kind, range),
                                  products="gecko",
                                  boxed=True)}
    % endfor

    // NOTE: According to the spec, this should handle multiple values of `<track-size>`,
    // but gecko supports only a single value
    ${helpers.predefined_type("grid-auto-%ss" % kind,
                              "TrackSize",
                              "Default::default()",
                              animatable=False,
                              spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-%ss" % kind,
                              products="gecko",
                              boxed=True)}
% endfor

<%helpers:longhand name="grid-auto-flow"
        spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-flow"
        products="gecko"
        animatable="False">
    use std::fmt;
    use style_traits::ToCss;
    use values::HasViewportPercentage;
    use values::computed::ComputedValueAsSpecified;

    pub type SpecifiedValue = computed_value::T;

    pub mod computed_value {
        #[derive(PartialEq, Clone, Eq, Copy, Debug)]
        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
        pub enum AutoFlow {
            Row,
            Column,
        }

        #[derive(PartialEq, Clone, Eq, Copy, Debug)]
        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
        pub struct T {
            pub autoflow: AutoFlow,
            pub dense: bool,
        }
    }

    no_viewport_percentage!(SpecifiedValue);
    impl ComputedValueAsSpecified for SpecifiedValue {}

    impl ToCss for computed_value::T {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            dest.write_str(match self.autoflow {
                computed_value::AutoFlow::Column => "column",
                computed_value::AutoFlow::Row => "row"
            })?;

            if self.dense { dest.write_str(" dense")?; }
            Ok(())
        }
    }

    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        computed_value::T {
            autoflow: computed_value::AutoFlow::Row,
            dense: false
        }
    }

    /// [ row | column ] || dense
    pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
        use self::computed_value::AutoFlow;

        let mut value = None;
        let mut dense = false;

        while !input.is_exhausted() {
            match_ignore_ascii_case! { &input.expect_ident()?,
                "row" if value.is_none() => {
                    value = Some(AutoFlow::Row);
                    continue
                },
                "column" if value.is_none() => {
                    value = Some(AutoFlow::Column);
                    continue
                },
                "dense" if !dense => {
                    dense = true;
                    continue
                },
                _ => return Err(())
            }
        }

        if value.is_some() || dense {
            Ok(computed_value::T {
                autoflow: value.unwrap_or(AutoFlow::Row),
                dense: dense,
            })
        } else {
            Err(())
        }
    }
</%helpers:longhand>
<commit_msg>style: Re-introduce the -webkit- prefix for the order property.<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/. */

<%! from data import to_rust_ident %>
<%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import ALL_SIZES, PHYSICAL_SIDES, LOGICAL_SIDES %>

<% data.new_style_struct("Position", inherited=False) %>

// "top" / "left" / "bottom" / "right"
% for side in PHYSICAL_SIDES:
    ${helpers.predefined_type(side, "LengthOrPercentageOrAuto",
                              "computed::LengthOrPercentageOrAuto::Auto",
                              spec="https://www.w3.org/TR/CSS2/visuren.html#propdef-%s" % side,
                              animatable=True)}
% endfor
// offset-* logical properties, map to "top" / "left" / "bottom" / "right"
% for side in LOGICAL_SIDES:
    ${helpers.predefined_type("offset-%s" % side, "LengthOrPercentageOrAuto",
                              "computed::LengthOrPercentageOrAuto::Auto",
                              spec="https://drafts.csswg.org/css-logical-props/#propdef-offset-%s" % side,
                              animatable=True, logical=True)}
% endfor

${helpers.predefined_type("z-index", "IntegerOrAuto",
                          "Either::Second(Auto)",
                          spec="https://www.w3.org/TR/CSS2/visuren.html#z-index",
                          creates_stacking_context=True,
                          animatable="True")}

// CSS Flexible Box Layout Module Level 1
// http://www.w3.org/TR/css3-flexbox/

// Flex container properties
${helpers.single_keyword("flex-direction", "row row-reverse column column-reverse",
                         spec="https://drafts.csswg.org/css-flexbox/#flex-direction-property",
                         extra_prefixes="webkit", animatable=False)}

${helpers.single_keyword("flex-wrap", "nowrap wrap wrap-reverse",
                         spec="https://drafts.csswg.org/css-flexbox/#flex-wrap-property",
                         extra_prefixes="webkit", animatable=False)}

% if product == "servo":
    // FIXME: Update Servo to support the same Syntax as Gecko.
    ${helpers.single_keyword("justify-content", "flex-start stretch flex-end center space-between space-around",
                             extra_prefixes="webkit",
                             spec="https://drafts.csswg.org/css-align/#propdef-justify-content",
                             animatable=False)}
% else:
    ${helpers.predefined_type(name="justify-content",
                              type="AlignJustifyContent",
                              initial_value="specified::AlignJustifyContent::normal()",
                              spec="https://drafts.csswg.org/css-align/#propdef-justify-content",
                              extra_prefixes="webkit",
                              animatable=False)}
% endif

% if product == "servo":
    // FIXME: Update Servo to support the same Syntax as Gecko.
    ${helpers.single_keyword("align-content", "stretch flex-start flex-end center space-between space-around",
                             extra_prefixes="webkit",
                             spec="https://drafts.csswg.org/css-align/#propdef-align-content",
                             animatable=False)}

    ${helpers.single_keyword("align-items",
                             "stretch flex-start flex-end center baseline",
                             need_clone=True,
                             extra_prefixes="webkit",
                             spec="https://drafts.csswg.org/css-flexbox/#align-items-property",
                             animatable=False)}
% else:
    ${helpers.predefined_type(name="align-content",
                              type="AlignJustifyContent",
                              initial_value="specified::AlignJustifyContent::normal()",
                              spec="https://drafts.csswg.org/css-align/#propdef-align-content",
                              extra_prefixes="webkit",
                              animatable=False)}

    ${helpers.predefined_type(name="align-items",
                              type="AlignItems",
                              initial_value="specified::AlignItems::normal()",
                              spec="https://drafts.csswg.org/css-align/#propdef-align-items",
                              extra_prefixes="webkit",
                              animatable=False)}

    ${helpers.predefined_type(name="justify-items",
                              type="JustifyItems",
                              initial_value="specified::JustifyItems::auto()",
                              spec="https://drafts.csswg.org/css-align/#propdef-justify-items",
                              animatable=False)}
% endif

// Flex item properties
${helpers.predefined_type("flex-grow", "Number",
                          "0.0", "parse_non_negative",
                          spec="https://drafts.csswg.org/css-flexbox/#flex-grow-property",
                          extra_prefixes="webkit",
                          needs_context=False,
                          animatable=True)}

${helpers.predefined_type("flex-shrink", "Number",
                          "1.0", "parse_non_negative",
                          spec="https://drafts.csswg.org/css-flexbox/#flex-shrink-property",
                          extra_prefixes="webkit",
                          needs_context=False,
                          animatable=True)}

// https://drafts.csswg.org/css-align/#align-self-property
% if product == "servo":
    // FIXME: Update Servo to support the same syntax as Gecko.
    ${helpers.single_keyword("align-self", "auto stretch flex-start flex-end center baseline",
                             need_clone=True,
                             extra_prefixes="webkit",
                             spec="https://drafts.csswg.org/css-flexbox/#propdef-align-self",
                             animatable=False)}
% else:
    ${helpers.predefined_type(name="align-self",
                              type="AlignJustifySelf",
                              initial_value="specified::AlignJustifySelf::auto()",
                              spec="https://drafts.csswg.org/css-align/#align-self-property",
                              extra_prefixes="webkit",
                              animatable=False)}

    ${helpers.predefined_type(name="justify-self",
                              type="AlignJustifySelf",
                              initial_value="specified::AlignJustifySelf::auto()",
                              spec="https://drafts.csswg.org/css-align/#justify-self-property",
                              animatable=False)}
% endif

// https://drafts.csswg.org/css-flexbox/#propdef-order
${helpers.predefined_type("order", "Integer", "0",
                          extra_prefixes="webkit",
                          animatable=True,
                          spec="https://drafts.csswg.org/css-flexbox/#order-property")}

// FIXME: Gecko doesn't support content value yet.
// FIXME: This property should be animatable.
${helpers.predefined_type("flex-basis",
                          "LengthOrPercentageOrAuto" if product == "gecko" else
                          "LengthOrPercentageOrAutoOrContent",
                          "computed::LengthOrPercentageOrAuto::Auto" if product == "gecko" else
                          "computed::LengthOrPercentageOrAutoOrContent::Auto",
                          spec="https://drafts.csswg.org/css-flexbox/#flex-basis-property",
                          extra_prefixes="webkit",
                          animatable=True if product == "gecko" else False)}

% for (size, logical) in ALL_SIZES:
    <%
      spec = "https://drafts.csswg.org/css-box/#propdef-%s"
      if logical:
        spec = "https://drafts.csswg.org/css-logical-props/#propdef-%s"
    %>
    // width, height, block-size, inline-size
    ${helpers.predefined_type("%s" % size,
                              "LengthOrPercentageOrAuto",
                              "computed::LengthOrPercentageOrAuto::Auto",
                              "parse_non_negative",
                              needs_context=False,
                              spec=spec % size,
                              animatable=True, logical = logical)}
    % if product == "gecko":
        % for min_max in ["min", "max"]:
            <%
                MinMax = min_max.title()
                initial = "None" if "max" == min_max else "Auto"
            %>

            // min-width, min-height, min-block-size, min-inline-size,
            // max-width, max-height, max-block-size, max-inline-size
            //
            // Keyword values are only valid in the inline direction; they must
            // be replaced with auto/none in block.
            <%helpers:longhand name="${min_max}-${size}" spec="${spec % ('%s-%s' % (min_max, size))}"
                               animatable="True" logical="${logical}" predefined_type="${MinMax}Length">

                use std::fmt;
                use style_traits::ToCss;
                use values::HasViewportPercentage;
                use values::specified::${MinMax}Length;

                impl HasViewportPercentage for SpecifiedValue {
                    fn has_viewport_percentage(&self) -> bool {
                        self.0.has_viewport_percentage()
                    }
                }

                pub mod computed_value {
                    pub type T = ::values::computed::${MinMax}Length;
                }

                #[derive(PartialEq, Clone, Debug)]
                #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
                pub struct SpecifiedValue(${MinMax}Length);

                #[inline]
                pub fn get_initial_value() -> computed_value::T {
                    use values::computed::${MinMax}Length;
                    ${MinMax}Length::${initial}
                }
                fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
                    ${MinMax}Length::parse(context, input).map(SpecifiedValue)
                }

                impl ToCss for SpecifiedValue {
                    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
                        self.0.to_css(dest)
                    }
                }

                impl ToComputedValue for SpecifiedValue {
                    type ComputedValue = computed_value::T;
                    #[inline]
                    fn to_computed_value(&self, context: &Context) -> computed_value::T {
                        use values::computed::${MinMax}Length;
                        let computed = self.0.to_computed_value(context);

                        // filter out keyword values in the block direction
                        % if logical:
                            % if "block" in size:
                                if let ${MinMax}Length::ExtremumLength(..) = computed {
                                    return get_initial_value()
                                }
                            % endif
                        % else:
                            if let ${MinMax}Length::ExtremumLength(..) = computed {
                                <% is_height = "true" if "height" in size else "false" %>
                                if ${is_height} != context.style().writing_mode.is_vertical() {
                                    return get_initial_value()
                                }
                            }
                        % endif
                        computed
                    }

                    #[inline]
                    fn from_computed_value(computed: &computed_value::T) -> Self {
                        SpecifiedValue(ToComputedValue::from_computed_value(computed))
                    }
                }
            </%helpers:longhand>
        % endfor
    % else:
        // servo versions (no keyword support)
        ${helpers.predefined_type("min-%s" % size,
                                  "LengthOrPercentage",
                                  "computed::LengthOrPercentage::Length(Au(0))",
                                  "parse_non_negative",
                                  needs_context=False,
                                  spec=spec % ("min-%s" % size),
                                  animatable=True, logical = logical)}
        ${helpers.predefined_type("max-%s" % size,
                                  "LengthOrPercentageOrNone",
                                  "computed::LengthOrPercentageOrNone::None",
                                  "parse_non_negative",
                                  needs_context=False,
                                  spec=spec % ("min-%s" % size),
                                  animatable=True, logical = logical)}
    % endif
% endfor

${helpers.single_keyword("box-sizing",
                         "content-box border-box",
                         extra_prefixes="moz webkit",
                         spec="https://drafts.csswg.org/css-ui/#propdef-box-sizing",
                         animatable=False)}

${helpers.single_keyword("object-fit", "fill contain cover none scale-down",
                         products="gecko", animatable=False,
                         spec="https://drafts.csswg.org/css-images/#propdef-object-fit")}

${helpers.predefined_type("object-position",
                          "Position",
                          "computed::Position::zero()",
                          products="gecko",
                          boxed="True",
                          spec="https://drafts.csswg.org/css-images-3/#the-object-position",
                          animatable=True)}

% for kind in ["row", "column"]:
    ${helpers.predefined_type("grid-%s-gap" % kind,
                              "LengthOrPercentage",
                              "computed::LengthOrPercentage::Length(Au(0))",
                              spec="https://drafts.csswg.org/css-grid/#propdef-grid-%s-gap" % kind,
                              animatable=True,
                              products="gecko")}

    % for range in ["start", "end"]:
        ${helpers.predefined_type("grid-%s-%s" % (kind, range),
                                  "GridLine",
                                  "Default::default()",
                                  animatable=False,
                                  spec="https://drafts.csswg.org/css-grid/#propdef-grid-%s-%s" % (kind, range),
                                  products="gecko",
                                  boxed=True)}
    % endfor

    // NOTE: According to the spec, this should handle multiple values of `<track-size>`,
    // but gecko supports only a single value
    ${helpers.predefined_type("grid-auto-%ss" % kind,
                              "TrackSize",
                              "Default::default()",
                              animatable=False,
                              spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-%ss" % kind,
                              products="gecko",
                              boxed=True)}
% endfor

<%helpers:longhand name="grid-auto-flow"
        spec="https://drafts.csswg.org/css-grid/#propdef-grid-auto-flow"
        products="gecko"
        animatable="False">
    use std::fmt;
    use style_traits::ToCss;
    use values::HasViewportPercentage;
    use values::computed::ComputedValueAsSpecified;

    pub type SpecifiedValue = computed_value::T;

    pub mod computed_value {
        #[derive(PartialEq, Clone, Eq, Copy, Debug)]
        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
        pub enum AutoFlow {
            Row,
            Column,
        }

        #[derive(PartialEq, Clone, Eq, Copy, Debug)]
        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
        pub struct T {
            pub autoflow: AutoFlow,
            pub dense: bool,
        }
    }

    no_viewport_percentage!(SpecifiedValue);
    impl ComputedValueAsSpecified for SpecifiedValue {}

    impl ToCss for computed_value::T {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            dest.write_str(match self.autoflow {
                computed_value::AutoFlow::Column => "column",
                computed_value::AutoFlow::Row => "row"
            })?;

            if self.dense { dest.write_str(" dense")?; }
            Ok(())
        }
    }

    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        computed_value::T {
            autoflow: computed_value::AutoFlow::Row,
            dense: false
        }
    }

    /// [ row | column ] || dense
    pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
        use self::computed_value::AutoFlow;

        let mut value = None;
        let mut dense = false;

        while !input.is_exhausted() {
            match_ignore_ascii_case! { &input.expect_ident()?,
                "row" if value.is_none() => {
                    value = Some(AutoFlow::Row);
                    continue
                },
                "column" if value.is_none() => {
                    value = Some(AutoFlow::Column);
                    continue
                },
                "dense" if !dense => {
                    dense = true;
                    continue
                },
                _ => return Err(())
            }
        }

        if value.is_some() || dense {
            Ok(computed_value::T {
                autoflow: value.unwrap_or(AutoFlow::Row),
                dense: dense,
            })
        } else {
            Err(())
        }
    }
</%helpers:longhand>
<|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/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<% data.new_style_struct("SVG", inherited=False, gecko_name="SVGReset") %>

${helpers.single_keyword("dominant-baseline",
                 """auto use-script no-change reset-size ideographic alphabetic hanging
                    mathematical central middle text-after-edge text-before-edge""",
                 products="gecko",
                 animation_value_type="discrete",
                 spec="https://www.w3.org/TR/SVG11/text.html#DominantBaselineProperty")}

${helpers.single_keyword("vector-effect", "none non-scaling-stroke",
                         products="gecko", animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVGTiny12/painting.html#VectorEffectProperty")}

// Section 13 - Gradients and Patterns

${helpers.predefined_type(
    "stop-color", "RGBAColor",
    "RGBA::new(0, 0, 0, 255)",
    products="gecko",
    animation_value_type="none",
    spec="https://www.w3.org/TR/SVGTiny12/painting.html#StopColorProperty")}

${helpers.predefined_type("stop-opacity", "Opacity", "1.0",
                          products="gecko",
                          animation_value_type="none",
                          spec="https://www.w3.org/TR/SVGTiny12/painting.html#propdef-stop-opacity")}

// Section 15 - Filter Effects

${helpers.predefined_type(
    "flood-color", "RGBAColor",
    "RGBA::new(0, 0, 0, 255)",
    products="gecko",
    animation_value_type="none",
    spec="https://www.w3.org/TR/SVG/filters.html#FloodColorProperty")}

${helpers.predefined_type("flood-opacity", "Opacity",
                          "1.0", products="gecko", animation_value_type="none",
                          spec="https://www.w3.org/TR/SVG/filters.html#FloodOpacityProperty")}

${helpers.predefined_type(
    "lighting-color", "RGBAColor",
    "RGBA::new(255, 255, 255, 255)",
    products="gecko",
    animation_value_type="none",
    spec="https://www.w3.org/TR/SVG/filters.html#LightingColorProperty")}

// CSS Masking Module Level 1
// https://drafts.fxtf.org/css-masking
${helpers.single_keyword("mask-type", "luminance alpha",
                         products="gecko", animation_value_type="discrete",
                         spec="https://drafts.fxtf.org/css-masking/#propdef-mask-type")}

${helpers.predefined_type("clip-path", "basic_shape::ClippingShape",
                          "generics::basic_shape::ShapeSource::None",
                          products="gecko", boxed="True",
                          animation_value_type="none", flags="CREATES_STACKING_CONTEXT",
                          spec="https://drafts.fxtf.org/css-masking/#propdef-clip-path")}

${helpers.single_keyword("mask-mode",
                         "match-source alpha luminance",
                         vector=True,
                         products="gecko",
                         animation_value_type="discrete",
                         spec="https://drafts.fxtf.org/css-masking/#propdef-mask-mode")}

<%helpers:vector_longhand name="mask-repeat" products="gecko" animation_value_type="none" extra_prefixes="webkit"
                          spec="https://drafts.fxtf.org/css-masking/#propdef-mask-repeat">
    pub use properties::longhands::background_repeat::single_value::parse;
    pub use properties::longhands::background_repeat::single_value::SpecifiedValue;
    pub use properties::longhands::background_repeat::single_value::computed_value;
    pub use properties::longhands::background_repeat::single_value::RepeatKeyword;

    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        computed_value::T(RepeatKeyword::Repeat, RepeatKeyword::Repeat)
    }

    #[inline]
    pub fn get_initial_specified_value() -> SpecifiedValue {
        SpecifiedValue::Other(RepeatKeyword::Repeat, None)
    }
</%helpers:vector_longhand>

% for (axis, direction) in [("x", "Horizontal"), ("y", "Vertical")]:
    ${helpers.predefined_type("mask-position-" + axis, "position::" + direction + "Position",
                              products="gecko", extra_prefixes="webkit",
                              initial_value="computed::LengthOrPercentage::zero()",
                              initial_specified_value="specified::PositionComponent::Center",
                              spec="https://drafts.fxtf.org/css-masking/#propdef-mask-position",
                              animation_value_type="ComputedValue", vector=True, delegate_animate=True)}
% endfor

${helpers.single_keyword("mask-clip",
                         "border-box content-box padding-box",
                         extra_gecko_values="fill-box stroke-box view-box no-clip",
                         vector=True,
                         products="gecko",
                         extra_prefixes="webkit",
                         gecko_enum_prefix="StyleGeometryBox",
                         animation_value_type="discrete",
                         spec="https://drafts.fxtf.org/css-masking/#propdef-mask-clip")}

${helpers.single_keyword("mask-origin",
                         "border-box content-box padding-box",
                         extra_gecko_values="fill-box stroke-box view-box",
                         vector=True,
                         products="gecko",
                         extra_prefixes="webkit",
                         gecko_enum_prefix="StyleGeometryBox",
                         animation_value_type="discrete",
                         spec="https://drafts.fxtf.org/css-masking/#propdef-mask-origin")}

<%helpers:longhand name="mask-size" products="gecko" animation_value_type="ComputedValue" extra_prefixes="webkit"
                   spec="https://drafts.fxtf.org/css-masking/#propdef-mask-size">
    use properties::longhands::background_size;
    pub use ::properties::longhands::background_size::SpecifiedValue;
    pub use ::properties::longhands::background_size::single_value as single_value;
    pub use ::properties::longhands::background_size::computed_value as computed_value;

    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        background_size::get_initial_value()
    }

    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
        background_size::parse(context, input)
    }
</%helpers:longhand>

${helpers.single_keyword("mask-composite",
                         "add subtract intersect exclude",
                         vector=True,
                         products="gecko",
                         extra_prefixes="webkit",
                         animation_value_type="discrete",
                         spec="https://drafts.fxtf.org/css-masking/#propdef-mask-composite")}

${helpers.predefined_type("mask-image", "ImageLayer",
    initial_value="Either::First(None_)",
    initial_specified_value="Either::First(None_)",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-image",
    vector=True,
    products="gecko",
    extra_prefixes="webkit",
    animation_value_type="none",
    flags="CREATES_STACKING_CONTEXT",
    has_uncacheable_values="True" if product == "gecko" else "False")}
<commit_msg>Bug 1360133 - stylo: make {stop, flood}-opacity animatable.<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/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<% data.new_style_struct("SVG", inherited=False, gecko_name="SVGReset") %>

${helpers.single_keyword("dominant-baseline",
                 """auto use-script no-change reset-size ideographic alphabetic hanging
                    mathematical central middle text-after-edge text-before-edge""",
                 products="gecko",
                 animation_value_type="discrete",
                 spec="https://www.w3.org/TR/SVG11/text.html#DominantBaselineProperty")}

${helpers.single_keyword("vector-effect", "none non-scaling-stroke",
                         products="gecko", animation_value_type="discrete",
                         spec="https://www.w3.org/TR/SVGTiny12/painting.html#VectorEffectProperty")}

// Section 13 - Gradients and Patterns

${helpers.predefined_type(
    "stop-color", "RGBAColor",
    "RGBA::new(0, 0, 0, 255)",
    products="gecko",
    animation_value_type="none",
    spec="https://www.w3.org/TR/SVGTiny12/painting.html#StopColorProperty")}

${helpers.predefined_type("stop-opacity", "Opacity", "1.0",
                          products="gecko",
                          animation_value_type="ComputedValue",
                          spec="https://www.w3.org/TR/SVGTiny12/painting.html#propdef-stop-opacity")}

// Section 15 - Filter Effects

${helpers.predefined_type(
    "flood-color", "RGBAColor",
    "RGBA::new(0, 0, 0, 255)",
    products="gecko",
    animation_value_type="none",
    spec="https://www.w3.org/TR/SVG/filters.html#FloodColorProperty")}

${helpers.predefined_type("flood-opacity", "Opacity",
                          "1.0", products="gecko", animation_value_type="ComputedValue",
                          spec="https://www.w3.org/TR/SVG/filters.html#FloodOpacityProperty")}

${helpers.predefined_type(
    "lighting-color", "RGBAColor",
    "RGBA::new(255, 255, 255, 255)",
    products="gecko",
    animation_value_type="none",
    spec="https://www.w3.org/TR/SVG/filters.html#LightingColorProperty")}

// CSS Masking Module Level 1
// https://drafts.fxtf.org/css-masking
${helpers.single_keyword("mask-type", "luminance alpha",
                         products="gecko", animation_value_type="discrete",
                         spec="https://drafts.fxtf.org/css-masking/#propdef-mask-type")}

${helpers.predefined_type("clip-path", "basic_shape::ClippingShape",
                          "generics::basic_shape::ShapeSource::None",
                          products="gecko", boxed="True",
                          animation_value_type="none", flags="CREATES_STACKING_CONTEXT",
                          spec="https://drafts.fxtf.org/css-masking/#propdef-clip-path")}

${helpers.single_keyword("mask-mode",
                         "match-source alpha luminance",
                         vector=True,
                         products="gecko",
                         animation_value_type="discrete",
                         spec="https://drafts.fxtf.org/css-masking/#propdef-mask-mode")}

<%helpers:vector_longhand name="mask-repeat" products="gecko" animation_value_type="none" extra_prefixes="webkit"
                          spec="https://drafts.fxtf.org/css-masking/#propdef-mask-repeat">
    pub use properties::longhands::background_repeat::single_value::parse;
    pub use properties::longhands::background_repeat::single_value::SpecifiedValue;
    pub use properties::longhands::background_repeat::single_value::computed_value;
    pub use properties::longhands::background_repeat::single_value::RepeatKeyword;

    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        computed_value::T(RepeatKeyword::Repeat, RepeatKeyword::Repeat)
    }

    #[inline]
    pub fn get_initial_specified_value() -> SpecifiedValue {
        SpecifiedValue::Other(RepeatKeyword::Repeat, None)
    }
</%helpers:vector_longhand>

% for (axis, direction) in [("x", "Horizontal"), ("y", "Vertical")]:
    ${helpers.predefined_type("mask-position-" + axis, "position::" + direction + "Position",
                              products="gecko", extra_prefixes="webkit",
                              initial_value="computed::LengthOrPercentage::zero()",
                              initial_specified_value="specified::PositionComponent::Center",
                              spec="https://drafts.fxtf.org/css-masking/#propdef-mask-position",
                              animation_value_type="ComputedValue", vector=True, delegate_animate=True)}
% endfor

${helpers.single_keyword("mask-clip",
                         "border-box content-box padding-box",
                         extra_gecko_values="fill-box stroke-box view-box no-clip",
                         vector=True,
                         products="gecko",
                         extra_prefixes="webkit",
                         gecko_enum_prefix="StyleGeometryBox",
                         animation_value_type="discrete",
                         spec="https://drafts.fxtf.org/css-masking/#propdef-mask-clip")}

${helpers.single_keyword("mask-origin",
                         "border-box content-box padding-box",
                         extra_gecko_values="fill-box stroke-box view-box",
                         vector=True,
                         products="gecko",
                         extra_prefixes="webkit",
                         gecko_enum_prefix="StyleGeometryBox",
                         animation_value_type="discrete",
                         spec="https://drafts.fxtf.org/css-masking/#propdef-mask-origin")}

<%helpers:longhand name="mask-size" products="gecko" animation_value_type="ComputedValue" extra_prefixes="webkit"
                   spec="https://drafts.fxtf.org/css-masking/#propdef-mask-size">
    use properties::longhands::background_size;
    pub use ::properties::longhands::background_size::SpecifiedValue;
    pub use ::properties::longhands::background_size::single_value as single_value;
    pub use ::properties::longhands::background_size::computed_value as computed_value;

    #[inline]
    pub fn get_initial_value() -> computed_value::T {
        background_size::get_initial_value()
    }

    pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue,()> {
        background_size::parse(context, input)
    }
</%helpers:longhand>

${helpers.single_keyword("mask-composite",
                         "add subtract intersect exclude",
                         vector=True,
                         products="gecko",
                         extra_prefixes="webkit",
                         animation_value_type="discrete",
                         spec="https://drafts.fxtf.org/css-masking/#propdef-mask-composite")}

${helpers.predefined_type("mask-image", "ImageLayer",
    initial_value="Either::First(None_)",
    initial_specified_value="Either::First(None_)",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-image",
    vector=True,
    products="gecko",
    extra_prefixes="webkit",
    animation_value_type="none",
    flags="CREATES_STACKING_CONTEXT",
    has_uncacheable_values="True" if product == "gecko" else "False")}
<|endoftext|>


<commit_before>//!
//! An example of using dsp-chain to create a simple Synthesiser with 3 sine wave oscillators.
//!

extern crate dsp;

use dsp::{
    Event,
    Node,
    SoundStream,
    Settings,
};

/// The number of frames processed per second.
const SAMPLE_HZ: u32 = 44100;
/// The number of channels to use for output. We'll use two for stereo.
const CHANNELS: u16 = 2;
/// This value is equal to (fixed-size buffer length / CHANNELS).
const FRAMES: u16 = 256;

const SETTINGS: Settings = Settings { sample_hz: SAMPLE_HZ, frames: FRAMES, channels: CHANNELS };

const BUFFER_SIZE: uint = (FRAMES * CHANNELS) as uint;

type Input = f32;
type Output = f32;
type OutputBuffer = [f32, ..BUFFER_SIZE];

type Phase = f64;
type Frequency = f64;

const A5_HZ: Frequency = 440.0;
const D5_HZ: Frequency = 587.33;
const F5_HZ: Frequency = 698.46;

fn main() {

    // Construct the stream and handle any errors that may have occurred.
    let mut stream = match SoundStream::<OutputBuffer, Input, Output>::new(SETTINGS) {
        Ok(stream) => { println!("It begins!"); stream },
        Err(err) => panic!("An error occurred while constructing SoundStream: {}", err),
    };

    // Construct our fancy Synth!
    let mut synth = Synth([Oscillator(0.0, A5_HZ), Oscillator(0.0, D5_HZ), Oscillator(0.0, F5_HZ)]);

    // We'll use this to count down from three seconds and then break from the loop.
    let mut timer: f64 = 3.0;

    // The SoundStream iterator will automatically return these events in this order.
    for event in stream {
        match event {
            Event::Out(buffer) => synth.audio_requested(buffer, SETTINGS),
            Event::Update(dt) => {
                timer -= dt as f64 / 1_000_000_000.0;
                if timer <= 0.0 { break }
            }
            _ => (),
        }
    }

    // Close the stream and shut down PortAudio.
    match stream.close() {
        Ok(()) => println!("Great success!"),
        Err(err) => println!("An error occurred while closing SoundStream: {}", err),
    }

}

/// Synth will be our demonstration of a parent DspNode where the Oscillators
/// that it owns are it's children.
struct Synth([Oscillator, ..3]);

impl Node<OutputBuffer, Output> for Synth {
    /// Here we return a reference to each of our Oscillators as our `inputs`.
    /// This allows the default `audio_requested` method to draw input from
    /// each of our oscillators automatically.
    fn inputs(&mut self) -> Vec<&mut Node<OutputBuffer, Output>> {
        let Synth(ref mut oscillators) = *self;
        oscillators.iter_mut().map(|osc| osc as &mut Node<OutputBuffer, Output>).collect()
    }
}

/// Oscillator will be our generator type of node, meaning that we will override
/// the way it provides audio via its `audio_requested` method.
struct Oscillator(Phase, Frequency);

impl Node<OutputBuffer, Output> for Oscillator {
    /// Here we'll override the audio_requested method and generate a sine wave.
    fn audio_requested(&mut self, buffer: &mut OutputBuffer, settings: Settings) {
        let (frames, channels) = (settings.frames as uint, settings.channels as uint);
        let Oscillator(ref mut phase, frequency) = *self;
        // For every frame in the buffer.
        for i in range(0u, frames) {
            *phase += frequency / settings.sample_hz as f64;
            let val = sine_wave(*phase);
            // For each channel in the frame.
            for j in range(0u, channels) {
                let idx = i * channels + j;
                buffer[idx] = val;
            }
        }
    }
}

/// Return a sine wave for the given phase.
fn sine_wave(phase: Phase) -> Output {
    use std::f64::consts::PI_2;
    use std::num::FloatMath;
    ((phase * PI_2).sin() * 0.2) as Output
}

<commit_msg>Added volume to example<commit_after>//!
//! An example of using dsp-chain to create a simple Synthesiser with 3 sine wave oscillators.
//!

extern crate dsp;

use dsp::{
    Event,
    Node,
    SoundStream,
    Settings,
};

/// The number of frames processed per second.
const SAMPLE_HZ: u32 = 44100;
/// The number of channels to use for output. We'll use two for stereo.
const CHANNELS: u16 = 2;
/// This value is equal to (fixed-size buffer length / CHANNELS).
const FRAMES: u16 = 256;

const SETTINGS: Settings = Settings { sample_hz: SAMPLE_HZ, frames: FRAMES, channels: CHANNELS };

const BUFFER_SIZE: uint = (FRAMES * CHANNELS) as uint;

type Input = f32;
type Output = f32;
type OutputBuffer = [f32, ..BUFFER_SIZE];

type Phase = f64;
type Frequency = f64;
type Volume = f64;

const A5_HZ: Frequency = 440.0;
const D5_HZ: Frequency = 587.33;
const F5_HZ: Frequency = 698.46;

fn main() {

    // Construct the stream and handle any errors that may have occurred.
    let mut stream = match SoundStream::<OutputBuffer, Input, Output>::new(SETTINGS) {
        Ok(stream) => { println!("It begins!"); stream },
        Err(err) => panic!("An error occurred while constructing SoundStream: {}", err),
    };

    // Construct our fancy Synth!
    let mut synth = Synth([
            Oscillator(0.0, A5_HZ, 0.2),
            Oscillator(0.0, D5_HZ, 0.1),
            Oscillator(0.0, F5_HZ, 0.15)
        ]);

    // We'll use this to count down from three seconds and then break from the loop.
    let mut timer: f64 = 3.0;

    // The SoundStream iterator will automatically return these events in this order.
    for event in stream {
        match event {
            Event::Out(buffer) => synth.audio_requested(buffer, SETTINGS),
            Event::Update(dt) => {
                timer -= dt as f64 / 1_000_000_000.0;
                if timer <= 0.0 { break }
            }
            _ => (),
        }
    }

    // Close the stream and shut down PortAudio.
    match stream.close() {
        Ok(()) => println!("Great success!"),
        Err(err) => println!("An error occurred while closing SoundStream: {}", err),
    }

}

/// Synth will be our demonstration of a parent DspNode where the Oscillators
/// that it owns are it's children.
struct Synth([Oscillator, ..3]);

impl Node<OutputBuffer, Output> for Synth {
    /// Here we return a reference to each of our Oscillators as our `inputs`.
    /// This allows the default `audio_requested` method to draw input from
    /// each of our oscillators automatically.
    fn inputs(&mut self) -> Vec<&mut Node<OutputBuffer, Output>> {
        let Synth(ref mut oscillators) = *self;
        oscillators.iter_mut().map(|osc| osc as &mut Node<OutputBuffer, Output>).collect()
    }
}

/// Oscillator will be our generator type of node, meaning that we will override
/// the way it provides audio via its `audio_requested` method.
struct Oscillator(Phase, Frequency, Volume);

impl Node<OutputBuffer, Output> for Oscillator {
    /// Here we'll override the audio_requested method and generate a sine wave.
    fn audio_requested(&mut self, buffer: &mut OutputBuffer, settings: Settings) {
        let (frames, channels) = (settings.frames as uint, settings.channels as uint);
        let Oscillator(ref mut phase, frequency, volume) = *self;
        // For every frame in the buffer.
        for i in range(0u, frames) {
            *phase += frequency / settings.sample_hz as f64;
            let val = sine_wave(*phase, volume);
            // For each channel in the frame.
            for j in range(0u, channels) {
                let idx = i * channels + j;
                buffer[idx] = val;
            }
        }
    }
}

/// Return a sine wave for the given phase.
fn sine_wave(phase: Phase, volume: Volume) -> Output {
    use std::f64::consts::PI_2;
    use std::num::FloatMath;
    ((phase * PI_2).sin() * volume) as Output
}

<|endoftext|>


<commit_before>use anyhow::{anyhow, Error};

fn error() -> Error {
    anyhow!(0).context(1).context(2).context(3)
}

#[test]
fn test_iter() {
    let e = error();
    let mut chain = e.chain();
    assert_eq!("3", chain.next().unwrap().to_string());
    assert_eq!("2", chain.next().unwrap().to_string());
    assert_eq!("1", chain.next().unwrap().to_string());
    assert_eq!("0", chain.next().unwrap().to_string());
    assert!(chain.next().is_none());
    assert!(chain.next_back().is_none());
}

#[test]
fn test_rev() {
    let e = error();
    let mut chain = e.chain().rev();
    assert_eq!("0", chain.next().unwrap().to_string());
    assert_eq!("1", chain.next().unwrap().to_string());
    assert_eq!("2", chain.next().unwrap().to_string());
    assert_eq!("3", chain.next().unwrap().to_string());
    assert!(chain.next().is_none());
    assert!(chain.next_back().is_none());
}

#[test]
fn test_len() {
    let e = error();
    let mut chain = e.chain();
    assert_eq!(4, chain.len());
    assert_eq!("3", chain.next().unwrap().to_string());
    assert_eq!(3, chain.len());
    assert_eq!("0", chain.next_back().unwrap().to_string());
    assert_eq!(2, chain.len());
    assert_eq!("2", chain.next().unwrap().to_string());
    assert_eq!(1, chain.len());
    assert_eq!("1", chain.next_back().unwrap().to_string());
    assert_eq!(0, chain.len());
    assert!(chain.next().is_none());
}
<commit_msg>Increase Chain test coverage.<commit_after>use anyhow::{anyhow, Chain, Error};

fn error() -> Error {
    anyhow!(0).context(1).context(2).context(3)
}

#[test]
fn test_iter() {
    let e = error();
    let mut chain = e.chain();
    assert_eq!("3", chain.next().unwrap().to_string());
    assert_eq!("2", chain.next().unwrap().to_string());
    assert_eq!("1", chain.next().unwrap().to_string());
    assert_eq!("0", chain.next().unwrap().to_string());
    assert!(chain.next().is_none());
    assert!(chain.next_back().is_none());
}

#[test]
fn test_rev() {
    let e = error();
    let mut chain = e.chain().rev();
    assert_eq!("0", chain.next().unwrap().to_string());
    assert_eq!("1", chain.next().unwrap().to_string());
    assert_eq!("2", chain.next().unwrap().to_string());
    assert_eq!("3", chain.next().unwrap().to_string());
    assert!(chain.next().is_none());
    assert!(chain.next_back().is_none());
}

#[test]
fn test_len() {
    let e = error();
    let mut chain = e.chain();
    assert_eq!(4, chain.len());
    assert_eq!((4, Some(4)), chain.size_hint());
    assert_eq!("3", chain.next().unwrap().to_string());
    assert_eq!(3, chain.len());
    assert_eq!((3, Some(3)), chain.size_hint());
    assert_eq!("0", chain.next_back().unwrap().to_string());
    assert_eq!(2, chain.len());
    assert_eq!((2, Some(2)), chain.size_hint());
    assert_eq!("2", chain.next().unwrap().to_string());
    assert_eq!(1, chain.len());
    assert_eq!((1, Some(1)), chain.size_hint());
    assert_eq!("1", chain.next_back().unwrap().to_string());
    assert_eq!(0, chain.len());
    assert_eq!((0, Some(0)), chain.size_hint());
    assert!(chain.next().is_none());
}

#[test]
fn test_default() {
    let mut c: Chain = Default::default();
    assert!(c.next().is_none());
}

#[test]
fn test_clone() {
    let e = error();
    let mut chain = e.chain().clone();
    assert_eq!("3", chain.next().unwrap().to_string());
    assert_eq!("2", chain.next().unwrap().to_string());
    assert_eq!("1", chain.next().unwrap().to_string());
    assert_eq!("0", chain.next().unwrap().to_string());
    assert!(chain.next().is_none());
    assert!(chain.next_back().is_none());
}
<|endoftext|>