<commit_before>// Copyright 2015 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#[cfg(any(target_os="linux", target_os="android"))]
pub use platform::linux::channel;
#[cfg(any(target_os="linux", target_os="android"))]
pub use platform::linux::UnixReceiver as OsIpcReceiver;
#[cfg(any(target_os="linux", target_os="android"))]
pub use platform::linux::UnixSender as OsIpcSender;
#[cfg(any(target_os="linux", target_os="android"))]
pub use platform::linux::UnixReceiverSet as OsIpcReceiverSet;
#[cfg(any(target_os="linux", target_os="android"))]
pub use platform::linux::UnixSharedMemory as OsIpcSharedMemory;
#[cfg(any(target_os="linux", target_os="android"))]
pub use platform::linux::UnixChannel as OsIpcChannel;
#[cfg(any(target_os="linux", target_os="android"))]
pub use platform::linux::UnixSelectionResult as OsIpcSelectionResult;
#[cfg(any(target_os="linux", target_os="android"))]
pub use platform::linux::OpaqueUnixChannel as OsOpaqueIpcChannel;
#[cfg(any(target_os="linux", target_os="android"))]
pub use platform::linux::UnixOneShotServer as OsIpcOneShotServer;

#[cfg(target_os="macos")]
pub use platform::macos::channel;
#[cfg(target_os="macos")]
pub use platform::macos::MachReceiver as OsIpcReceiver;
#[cfg(target_os="macos")]
pub use platform::macos::MachSender as OsIpcSender;
#[cfg(target_os="macos")]
pub use platform::macos::MachReceiverSet as OsIpcReceiverSet;
#[cfg(target_os="macos")]
pub use platform::macos::MachSharedMemory as OsIpcSharedMemory;
#[cfg(target_os="macos")]
pub use platform::macos::MachChannel as OsIpcChannel;
#[cfg(target_os="macos")]
pub use platform::macos::MachSelectionResult as OsIpcSelectionResult;
#[cfg(target_os="macos")]
pub use platform::macos::OpaqueMachChannel as OsOpaqueIpcChannel;
#[cfg(target_os="macos")]
pub use platform::macos::MachOneShotServer as OsIpcOneShotServer;

// Windows uses in-process mpsc channels IPC for now
#[cfg(target_os="windows")]
pub use platform::inprocess::channel;
#[cfg(target_os="windows")]
pub use platform::inprocess::MpscReceiver as OsIpcReceiver;
#[cfg(target_os="windows")]
pub use platform::inprocess::MpscSender as OsIpcSender;
#[cfg(target_os="windows")]
pub use platform::inprocess::MpscReceiverSet as OsIpcReceiverSet;
#[cfg(target_os="windows")]
pub use platform::inprocess::MpscSharedMemory as OsIpcSharedMemory;
#[cfg(target_os="windows")]
pub use platform::inprocess::MpscChannel as OsIpcChannel;
#[cfg(target_os="windows")]
pub use platform::inprocess::MpscSelectionResult as OsIpcSelectionResult;
#[cfg(target_os="windows")]
pub use platform::inprocess::OpaqueMpscChannel as OsOpaqueIpcChannel;
#[cfg(target_os="windows")]
pub use platform::inprocess::MpscOneShotServer as OsIpcOneShotServer;

#[cfg(any(target_os="linux", target_os="android"))]
mod linux;
#[cfg(target_os="macos")]
mod macos;
#[cfg(target_os="windows")]
mod inprocess;

#[cfg(test)]
mod test;

<commit_msg>Switch android to use the in-process ipc bypass.<commit_after>// Copyright 2015 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#[cfg(target_os="linux")]
pub use platform::linux::channel;
#[cfg(target_os="linux")]
pub use platform::linux::UnixReceiver as OsIpcReceiver;
#[cfg(target_os="linux")]
pub use platform::linux::UnixSender as OsIpcSender;
#[cfg(target_os="linux")]
pub use platform::linux::UnixReceiverSet as OsIpcReceiverSet;
#[cfg(target_os="linux")]
pub use platform::linux::UnixSharedMemory as OsIpcSharedMemory;
#[cfg(target_os="linux")]
pub use platform::linux::UnixChannel as OsIpcChannel;
#[cfg(target_os="linux")]
pub use platform::linux::UnixSelectionResult as OsIpcSelectionResult;
#[cfg(target_os="linux")]
pub use platform::linux::OpaqueUnixChannel as OsOpaqueIpcChannel;
#[cfg(target_os="linux")]
pub use platform::linux::UnixOneShotServer as OsIpcOneShotServer;

#[cfg(target_os="macos")]
pub use platform::macos::channel;
#[cfg(target_os="macos")]
pub use platform::macos::MachReceiver as OsIpcReceiver;
#[cfg(target_os="macos")]
pub use platform::macos::MachSender as OsIpcSender;
#[cfg(target_os="macos")]
pub use platform::macos::MachReceiverSet as OsIpcReceiverSet;
#[cfg(target_os="macos")]
pub use platform::macos::MachSharedMemory as OsIpcSharedMemory;
#[cfg(target_os="macos")]
pub use platform::macos::MachChannel as OsIpcChannel;
#[cfg(target_os="macos")]
pub use platform::macos::MachSelectionResult as OsIpcSelectionResult;
#[cfg(target_os="macos")]
pub use platform::macos::OpaqueMachChannel as OsOpaqueIpcChannel;
#[cfg(target_os="macos")]
pub use platform::macos::MachOneShotServer as OsIpcOneShotServer;

// Windows and Android use in-process mpsc channels IPC for now
#[cfg(any(target_os="windows", target_os="android"))]
pub use platform::inprocess::channel;
#[cfg(any(target_os="windows", target_os="android"))]
pub use platform::inprocess::MpscReceiver as OsIpcReceiver;
#[cfg(any(target_os="windows", target_os="android"))]
pub use platform::inprocess::MpscSender as OsIpcSender;
#[cfg(any(target_os="windows", target_os="android"))]
pub use platform::inprocess::MpscReceiverSet as OsIpcReceiverSet;
#[cfg(any(target_os="windows", target_os="android"))]
pub use platform::inprocess::MpscSharedMemory as OsIpcSharedMemory;
#[cfg(any(target_os="windows", target_os="android"))]
pub use platform::inprocess::MpscChannel as OsIpcChannel;
#[cfg(any(target_os="windows", target_os="android"))]
pub use platform::inprocess::MpscSelectionResult as OsIpcSelectionResult;
#[cfg(any(target_os="windows", target_os="android"))]
pub use platform::inprocess::OpaqueMpscChannel as OsOpaqueIpcChannel;
#[cfg(any(target_os="windows", target_os="android"))]
pub use platform::inprocess::MpscOneShotServer as OsIpcOneShotServer;

#[cfg(target_os="linux")]
mod linux;
#[cfg(target_os="macos")]
mod macos;
#[cfg(any(target_os="windows", target_os="android"))]
mod inprocess;

#[cfg(test)]
mod test;

<|endoftext|>


<commit_before>use ide_db::RootDatabase;
use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};

use crate::{display::TryToNav, FilePosition, NavigationTarget, RangeInfo};

// Feature: Go to Type Definition
//
// Navigates to the type of an identifier.
//
// |===
// | Editor  | Action Name
//
// | VS Code | **Go to Type Definition*
// |===
//
// image::https://user-images.githubusercontent.com/48062697/113020657-b560f500-917a-11eb-9007-0f809733a338.gif[]
pub(crate) fn goto_type_definition(
    db: &RootDatabase,
    position: FilePosition,
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
    let sema = hir::Semantics::new(db);

    let file: ast::SourceFile = sema.parse(position.file_id);
    let token: SyntaxToken = pick_best(file.syntax().token_at_offset(position.offset))?;
    let token: SyntaxToken = sema.descend_into_macros(token);

    let (ty, node) = sema.token_ancestors_with_macros(token).find_map(|node| {
        let ty = match_ast! {
            match node {
                ast::Expr(it) => sema.type_of_expr(&it)?,
                ast::Pat(it) => sema.type_of_pat(&it)?,
                ast::SelfParam(it) => sema.type_of_self(&it)?,
                ast::Type(it) => sema.resolve_type(&it)?,
                _ => return None,
            }
        };

        Some((ty, node))
    })?;

    let adt_def = ty.autoderef(db).filter_map(|ty| ty.as_adt()).last()?;

    let nav = adt_def.try_to_nav(db)?;
    Some(RangeInfo::new(node.text_range(), vec![nav]))
}

fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
    return tokens.max_by_key(priority);
    fn priority(n: &SyntaxToken) -> usize {
        match n.kind() {
            IDENT | INT_NUMBER | T![self] => 2,
            kind if kind.is_trivia() => 0,
            _ => 1,
        }
    }
}

#[cfg(test)]
mod tests {
    use ide_db::base_db::FileRange;

    use crate::fixture;

    fn check(ra_fixture: &str) {
        let (analysis, position, mut annotations) = fixture::annotations(ra_fixture);
        let (expected, data) = annotations.pop().unwrap();
        assert!(data.is_empty());

        let mut navs = analysis.goto_type_definition(position).unwrap().unwrap().info;
        assert_eq!(navs.len(), 1);
        let nav = navs.pop().unwrap();
        assert_eq!(expected, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() });
    }

    #[test]
    fn goto_type_definition_works_simple() {
        check(
            r#"
struct Foo;
     //^^^
fn foo() {
    let f: Foo; f$0
}
"#,
        );
    }

    #[test]
    fn goto_type_definition_works_simple_ref() {
        check(
            r#"
struct Foo;
     //^^^
fn foo() {
    let f: &Foo; f$0
}
"#,
        );
    }

    #[test]
    fn goto_type_definition_works_through_macro() {
        check(
            r#"
macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
struct Foo {}
     //^^^
id! {
    fn bar() { let f$0 = Foo {}; }
}
"#,
        );
    }

    #[test]
    fn goto_type_definition_for_param() {
        check(
            r#"
struct Foo;
     //^^^
fn foo($0f: Foo) {}
"#,
        );
    }

    #[test]
    fn goto_type_definition_for_tuple_field() {
        check(
            r#"
struct Foo;
     //^^^
struct Bar(Foo);
fn foo() {
    let bar = Bar(Foo);
    bar.$00;
}
"#,
        );
    }

    #[test]
    fn goto_def_for_self_param() {
        check(
            r#"
struct Foo;
     //^^^
impl Foo {
    fn f(&self$0) {}
}
"#,
        )
    }

    #[test]
    fn goto_def_for_type_fallback() {
        check(
            r#"
struct Foo;
     //^^^
impl Foo$0 {}
"#,
        )
    }
}
<commit_msg>Add go to type definition for struct fields within struct<commit_after>use ide_db::base_db::Upcast;
use ide_db::RootDatabase;
use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};

use crate::{display::TryToNav, FilePosition, NavigationTarget, RangeInfo};

// Feature: Go to Type Definition
//
// Navigates to the type of an identifier.
//
// |===
// | Editor  | Action Name
//
// | VS Code | **Go to Type Definition*
// |===
//
// image::https://user-images.githubusercontent.com/48062697/113020657-b560f500-917a-11eb-9007-0f809733a338.gif[]
pub(crate) fn goto_type_definition(
    db: &RootDatabase,
    position: FilePosition,
) -> Option<RangeInfo<Vec<NavigationTarget>>> {
    let sema = hir::Semantics::new(db);

    let file: ast::SourceFile = sema.parse(position.file_id);
    let token: SyntaxToken = pick_best(file.syntax().token_at_offset(position.offset))?;
    let token: SyntaxToken = sema.descend_into_macros(token);

    let (ty, node) = sema.token_ancestors_with_macros(token).find_map(|node| {
        let ty = match_ast! {
            match node {
                ast::Expr(it) => sema.type_of_expr(&it)?,
                ast::Pat(it) => sema.type_of_pat(&it)?,
                ast::SelfParam(it) => sema.type_of_self(&it)?,
                ast::Type(it) => sema.resolve_type(&it)?,
                ast::RecordField(it) => sema.to_def(&it).map(|d| d.ty(db.upcast()))?,
                _ => return None,
            }
        };

        Some((ty, node))
    })?;

    let adt_def = ty.autoderef(db).filter_map(|ty| ty.as_adt()).last()?;

    let nav = adt_def.try_to_nav(db)?;
    Some(RangeInfo::new(node.text_range(), vec![nav]))
}

fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
    return tokens.max_by_key(priority);
    fn priority(n: &SyntaxToken) -> usize {
        match n.kind() {
            IDENT | INT_NUMBER | T![self] => 2,
            kind if kind.is_trivia() => 0,
            _ => 1,
        }
    }
}

#[cfg(test)]
mod tests {
    use ide_db::base_db::FileRange;

    use crate::fixture;

    fn check(ra_fixture: &str) {
        let (analysis, position, mut annotations) = fixture::annotations(ra_fixture);
        let (expected, data) = annotations.pop().unwrap();
        assert!(data.is_empty());

        let mut navs = analysis.goto_type_definition(position).unwrap().unwrap().info;
        assert_eq!(navs.len(), 1);
        let nav = navs.pop().unwrap();
        assert_eq!(expected, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() });
    }

    #[test]
    fn goto_type_definition_works_simple() {
        check(
            r#"
struct Foo;
     //^^^
fn foo() {
    let f: Foo; f$0
}
"#,
        );
    }

    #[test]
    fn goto_type_definition_works_simple_ref() {
        check(
            r#"
struct Foo;
     //^^^
fn foo() {
    let f: &Foo; f$0
}
"#,
        );
    }

    #[test]
    fn goto_type_definition_works_through_macro() {
        check(
            r#"
macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
struct Foo {}
     //^^^
id! {
    fn bar() { let f$0 = Foo {}; }
}
"#,
        );
    }

    #[test]
    fn goto_type_definition_for_param() {
        check(
            r#"
struct Foo;
     //^^^
fn foo($0f: Foo) {}
"#,
        );
    }

    #[test]
    fn goto_type_definition_for_tuple_field() {
        check(
            r#"
struct Foo;
     //^^^
struct Bar(Foo);
fn foo() {
    let bar = Bar(Foo);
    bar.$00;
}
"#,
        );
    }

    #[test]
    fn goto_def_for_self_param() {
        check(
            r#"
struct Foo;
     //^^^
impl Foo {
    fn f(&self$0) {}
}
"#,
        )
    }

    #[test]
    fn goto_def_for_type_fallback() {
        check(
            r#"
struct Foo;
     //^^^
impl Foo$0 {}
"#,
        )
    }

    #[test]
    fn goto_def_for_struct_field() {
        check(
            r#"
struct Bar;
     //^^^

struct Foo {
    bar$0: Bar,
}
"#,
        );
    }

    #[test]
    fn goto_def_for_enum_struct_field() {
        check(
            r#"
struct Bar;
     //^^^

enum Foo {
    Bar {
        bar$0: Bar
    },
}
"#,
        );
    }
}
<|endoftext|>


<commit_before>//! Handles build script specific information

use std::{
    path::PathBuf,
    process::{Command, Stdio},
    sync::Arc,
};

use anyhow::Result;
use cargo_metadata::camino::Utf8Path;
use cargo_metadata::{BuildScript, Message};
use itertools::Itertools;
use paths::{AbsPath, AbsPathBuf};
use rustc_hash::FxHashMap;
use serde::Deserialize;
use stdx::format_to;

use crate::{cfg_flag::CfgFlag, CargoConfig};

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(crate) struct PackageBuildData {
    /// List of config flags defined by this package's build script
    pub(crate) cfgs: Vec<CfgFlag>,
    /// List of cargo-related environment variables with their value
    ///
    /// If the package has a build script which defines environment variables,
    /// they can also be found here.
    pub(crate) envs: Vec<(String, String)>,
    /// Directory where a build script might place its output
    pub(crate) out_dir: Option<AbsPathBuf>,
    /// Path to the proc-macro library file if this package exposes proc-macros
    pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>,
}

#[derive(Debug, Default, PartialEq, Eq, Clone)]
pub(crate) struct WorkspaceBuildData {
    per_package: FxHashMap<String, PackageBuildData>,
    error: Option<String>,
}

#[derive(Debug, Default, PartialEq, Eq, Clone)]
pub struct BuildDataResult {
    per_workspace: FxHashMap<AbsPathBuf, WorkspaceBuildData>,
}

#[derive(Clone, Debug)]
pub(crate) struct BuildDataConfig {
    cargo_toml: AbsPathBuf,
    cargo_features: CargoConfig,
    packages: Arc<Vec<cargo_metadata::Package>>,
}

impl PartialEq for BuildDataConfig {
    fn eq(&self, other: &Self) -> bool {
        Arc::ptr_eq(&self.packages, &other.packages)
    }
}

impl Eq for BuildDataConfig {}

#[derive(Debug)]
pub struct BuildDataCollector {
    wrap_rustc: bool,
    configs: FxHashMap<AbsPathBuf, BuildDataConfig>,
}

impl BuildDataCollector {
    pub fn new(wrap_rustc: bool) -> Self {
        Self { wrap_rustc, configs: FxHashMap::default() }
    }

    pub(crate) fn add_config(&mut self, workspace_root: &AbsPath, config: BuildDataConfig) {
        self.configs.insert(workspace_root.to_path_buf(), config);
    }

    pub fn collect(&mut self, progress: &dyn Fn(String)) -> Result<BuildDataResult> {
        let mut res = BuildDataResult::default();
        for (path, config) in self.configs.iter() {
            let workspace_build_data = WorkspaceBuildData::collect(
                &config.cargo_toml,
                &config.cargo_features,
                &config.packages,
                self.wrap_rustc,
                progress,
            )?;
            res.per_workspace.insert(path.clone(), workspace_build_data);
        }
        Ok(res)
    }
}

impl WorkspaceBuildData {
    pub(crate) fn get(&self, package_id: &str) -> Option<&PackageBuildData> {
        self.per_package.get(package_id)
    }
}

impl BuildDataResult {
    pub(crate) fn get(&self, workspace_root: &AbsPath) -> Option<&WorkspaceBuildData> {
        self.per_workspace.get(workspace_root)
    }
    pub fn error(&self) -> Option<String> {
        let mut buf = String::new();
        for (_workspace_root, build_data) in &self.per_workspace {
            if let Some(err) = &build_data.error {
                format_to!(buf, "cargo check failed:\n{}", err);
            }
        }
        if buf.is_empty() {
            return None;
        }

        Some(buf)
    }
}

impl BuildDataConfig {
    pub(crate) fn new(
        cargo_toml: AbsPathBuf,
        cargo_features: CargoConfig,
        packages: Arc<Vec<cargo_metadata::Package>>,
    ) -> Self {
        Self { cargo_toml, cargo_features, packages }
    }
}

impl WorkspaceBuildData {
    fn collect(
        cargo_toml: &AbsPath,
        cargo_features: &CargoConfig,
        packages: &Vec<cargo_metadata::Package>,
        wrap_rustc: bool,
        progress: &dyn Fn(String),
    ) -> Result<WorkspaceBuildData> {
        let mut cmd = Command::new(toolchain::cargo());

        if wrap_rustc {
            // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use
            // that to compile only proc macros and build scripts during the initial
            // `cargo check`.
            let myself = std::env::current_exe()?;
            cmd.env("RUSTC_WRAPPER", myself);
            cmd.env("RA_RUSTC_WRAPPER", "1");
        }

        cmd.args(&["check", "--workspace", "--message-format=json", "--manifest-path"])
            .arg(cargo_toml.as_ref());

        // --all-targets includes tests, benches and examples in addition to the
        // default lib and bins. This is an independent concept from the --targets
        // flag below.
        cmd.arg("--all-targets");

        if let Some(target) = &cargo_features.target {
            cmd.args(&["--target", target]);
        }

        if cargo_features.all_features {
            cmd.arg("--all-features");
        } else {
            if cargo_features.no_default_features {
                // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
                // https://github.com/oli-obk/cargo_metadata/issues/79
                cmd.arg("--no-default-features");
            }
            if !cargo_features.features.is_empty() {
                cmd.arg("--features");
                cmd.arg(cargo_features.features.join(" "));
            }
        }

        cmd.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null());

        let mut res = WorkspaceBuildData::default();

        let mut callback_err = None;
        let output = stdx::process::streaming_output(
            cmd,
            &mut |line| {
                if callback_err.is_some() {
                    return;
                }

                // Copy-pasted from existing cargo_metadata. It seems like we
                // should be using sered_stacker here?
                let mut deserializer = serde_json::Deserializer::from_str(&line);
                deserializer.disable_recursion_limit();
                let message = Message::deserialize(&mut deserializer)
                    .unwrap_or(Message::TextLine(line.to_string()));

                match message {
                    Message::BuildScriptExecuted(BuildScript {
                        package_id,
                        out_dir,
                        cfgs,
                        env,
                        ..
                    }) => {
                        let cfgs = {
                            let mut acc = Vec::new();
                            for cfg in cfgs {
                                match cfg.parse::<CfgFlag>() {
                                    Ok(it) => acc.push(it),
                                    Err(err) => {
                                        callback_err = Some(anyhow::format_err!(
                                            "invalid cfg from cargo-metadata: {}",
                                            err
                                        ));
                                        return;
                                    }
                                };
                            }
                            acc
                        };
                        let package_build_data =
                            res.per_package.entry(package_id.repr.clone()).or_default();
                        // cargo_metadata crate returns default (empty) path for
                        // older cargos, which is not absolute, so work around that.
                        if !out_dir.as_str().is_empty() {
                            let out_dir =
                                AbsPathBuf::assert(PathBuf::from(out_dir.into_os_string()));
                            package_build_data.out_dir = Some(out_dir);
                            package_build_data.cfgs = cfgs;
                        }

                        package_build_data.envs = env;
                    }
                    Message::CompilerArtifact(message) => {
                        progress(format!("metadata {}", message.target.name));

                        if message.target.kind.contains(&"proc-macro".to_string()) {
                            let package_id = message.package_id;
                            // Skip rmeta file
                            if let Some(filename) =
                                message.filenames.iter().find(|name| is_dylib(name))
                            {
                                let filename = AbsPathBuf::assert(PathBuf::from(&filename));
                                let package_build_data =
                                    res.per_package.entry(package_id.repr.clone()).or_default();
                                package_build_data.proc_macro_dylib_path = Some(filename);
                            }
                        }
                    }
                    Message::CompilerMessage(message) => {
                        progress(message.target.name.clone());
                    }
                    Message::BuildFinished(_) => {}
                    Message::TextLine(_) => {}
                    _ => {}
                }
            },
            &mut |_| (),
        )?;

        for package in packages {
            let package_build_data = res.per_package.entry(package.id.repr.clone()).or_default();
            inject_cargo_env(package, package_build_data);
            if let Some(out_dir) = &package_build_data.out_dir {
                // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
                if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
                    package_build_data.envs.push(("OUT_DIR".to_string(), out_dir));
                }
            }
        }

        if !output.status.success() {
            let mut stderr = String::from_utf8(output.stderr).unwrap_or_default();
            if stderr.is_empty() {
                stderr = "cargo check failed".to_string();
            }
            res.error = Some(stderr)
        }

        Ok(res)
    }
}

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

/// Recreates the compile-time environment variables that Cargo sets.
///
/// Should be synced with <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
fn inject_cargo_env(package: &cargo_metadata::Package, build_data: &mut PackageBuildData) {
    let env = &mut build_data.envs;

    // FIXME: Missing variables:
    // CARGO_PKG_HOMEPAGE, CARGO_CRATE_NAME, CARGO_BIN_NAME, CARGO_BIN_EXE_<name>

    let mut manifest_dir = package.manifest_path.clone();
    manifest_dir.pop();
    env.push(("CARGO_MANIFEST_DIR".into(), manifest_dir.into_string()));

    // Not always right, but works for common cases.
    env.push(("CARGO".into(), "cargo".into()));

    env.push(("CARGO_PKG_VERSION".into(), package.version.to_string()));
    env.push(("CARGO_PKG_VERSION_MAJOR".into(), package.version.major.to_string()));
    env.push(("CARGO_PKG_VERSION_MINOR".into(), package.version.minor.to_string()));
    env.push(("CARGO_PKG_VERSION_PATCH".into(), package.version.patch.to_string()));

    let pre = package.version.pre.iter().map(|id| id.to_string()).format(".");
    env.push(("CARGO_PKG_VERSION_PRE".into(), pre.to_string()));

    let authors = package.authors.join(";");
    env.push(("CARGO_PKG_AUTHORS".into(), authors));

    env.push(("CARGO_PKG_NAME".into(), package.name.clone()));
    env.push(("CARGO_PKG_DESCRIPTION".into(), package.description.clone().unwrap_or_default()));
    //env.push(("CARGO_PKG_HOMEPAGE".into(), package.homepage.clone().unwrap_or_default()));
    env.push(("CARGO_PKG_REPOSITORY".into(), package.repository.clone().unwrap_or_default()));
    env.push(("CARGO_PKG_LICENSE".into(), package.license.clone().unwrap_or_default()));

    let license_file = package.license_file.as_ref().map(|buf| buf.to_string()).unwrap_or_default();
    env.push(("CARGO_PKG_LICENSE_FILE".into(), license_file));
}
<commit_msg>fix: make error message in the status bar more useful<commit_after>//! Handles build script specific information

use std::{
    path::PathBuf,
    process::{Command, Stdio},
    sync::Arc,
};

use anyhow::Result;
use cargo_metadata::camino::Utf8Path;
use cargo_metadata::{BuildScript, Message};
use itertools::Itertools;
use paths::{AbsPath, AbsPathBuf};
use rustc_hash::FxHashMap;
use serde::Deserialize;
use stdx::format_to;

use crate::{cfg_flag::CfgFlag, CargoConfig};

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(crate) struct PackageBuildData {
    /// List of config flags defined by this package's build script
    pub(crate) cfgs: Vec<CfgFlag>,
    /// List of cargo-related environment variables with their value
    ///
    /// If the package has a build script which defines environment variables,
    /// they can also be found here.
    pub(crate) envs: Vec<(String, String)>,
    /// Directory where a build script might place its output
    pub(crate) out_dir: Option<AbsPathBuf>,
    /// Path to the proc-macro library file if this package exposes proc-macros
    pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>,
}

#[derive(Debug, Default, PartialEq, Eq, Clone)]
pub(crate) struct WorkspaceBuildData {
    per_package: FxHashMap<String, PackageBuildData>,
    error: Option<String>,
}

#[derive(Debug, Default, PartialEq, Eq, Clone)]
pub struct BuildDataResult {
    per_workspace: FxHashMap<AbsPathBuf, WorkspaceBuildData>,
}

#[derive(Clone, Debug)]
pub(crate) struct BuildDataConfig {
    cargo_toml: AbsPathBuf,
    cargo_features: CargoConfig,
    packages: Arc<Vec<cargo_metadata::Package>>,
}

impl PartialEq for BuildDataConfig {
    fn eq(&self, other: &Self) -> bool {
        Arc::ptr_eq(&self.packages, &other.packages)
    }
}

impl Eq for BuildDataConfig {}

#[derive(Debug)]
pub struct BuildDataCollector {
    wrap_rustc: bool,
    configs: FxHashMap<AbsPathBuf, BuildDataConfig>,
}

impl BuildDataCollector {
    pub fn new(wrap_rustc: bool) -> Self {
        Self { wrap_rustc, configs: FxHashMap::default() }
    }

    pub(crate) fn add_config(&mut self, workspace_root: &AbsPath, config: BuildDataConfig) {
        self.configs.insert(workspace_root.to_path_buf(), config);
    }

    pub fn collect(&mut self, progress: &dyn Fn(String)) -> Result<BuildDataResult> {
        let mut res = BuildDataResult::default();
        for (path, config) in self.configs.iter() {
            let workspace_build_data = WorkspaceBuildData::collect(
                &config.cargo_toml,
                &config.cargo_features,
                &config.packages,
                self.wrap_rustc,
                progress,
            )?;
            res.per_workspace.insert(path.clone(), workspace_build_data);
        }
        Ok(res)
    }
}

impl WorkspaceBuildData {
    pub(crate) fn get(&self, package_id: &str) -> Option<&PackageBuildData> {
        self.per_package.get(package_id)
    }
}

impl BuildDataResult {
    pub(crate) fn get(&self, workspace_root: &AbsPath) -> Option<&WorkspaceBuildData> {
        self.per_workspace.get(workspace_root)
    }
    pub fn error(&self) -> Option<String> {
        let mut buf = String::new();
        for (_workspace_root, build_data) in &self.per_workspace {
            if let Some(err) = &build_data.error {
                format_to!(buf, "cargo check failed:\n{}", err);
            }
        }
        if buf.is_empty() {
            return None;
        }

        Some(buf)
    }
}

impl BuildDataConfig {
    pub(crate) fn new(
        cargo_toml: AbsPathBuf,
        cargo_features: CargoConfig,
        packages: Arc<Vec<cargo_metadata::Package>>,
    ) -> Self {
        Self { cargo_toml, cargo_features, packages }
    }
}

impl WorkspaceBuildData {
    fn collect(
        cargo_toml: &AbsPath,
        cargo_features: &CargoConfig,
        packages: &Vec<cargo_metadata::Package>,
        wrap_rustc: bool,
        progress: &dyn Fn(String),
    ) -> Result<WorkspaceBuildData> {
        let mut cmd = Command::new(toolchain::cargo());

        if wrap_rustc {
            // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use
            // that to compile only proc macros and build scripts during the initial
            // `cargo check`.
            let myself = std::env::current_exe()?;
            cmd.env("RUSTC_WRAPPER", myself);
            cmd.env("RA_RUSTC_WRAPPER", "1");
        }

        cmd.args(&["check", "--quiet", "--workspace", "--message-format=json", "--manifest-path"])
            .arg(cargo_toml.as_ref());

        // --all-targets includes tests, benches and examples in addition to the
        // default lib and bins. This is an independent concept from the --targets
        // flag below.
        cmd.arg("--all-targets");

        if let Some(target) = &cargo_features.target {
            cmd.args(&["--target", target]);
        }

        if cargo_features.all_features {
            cmd.arg("--all-features");
        } else {
            if cargo_features.no_default_features {
                // FIXME: `NoDefaultFeatures` is mutual exclusive with `SomeFeatures`
                // https://github.com/oli-obk/cargo_metadata/issues/79
                cmd.arg("--no-default-features");
            }
            if !cargo_features.features.is_empty() {
                cmd.arg("--features");
                cmd.arg(cargo_features.features.join(" "));
            }
        }

        cmd.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null());

        let mut res = WorkspaceBuildData::default();

        let mut callback_err = None;
        let output = stdx::process::streaming_output(
            cmd,
            &mut |line| {
                if callback_err.is_some() {
                    return;
                }

                // Copy-pasted from existing cargo_metadata. It seems like we
                // should be using sered_stacker here?
                let mut deserializer = serde_json::Deserializer::from_str(&line);
                deserializer.disable_recursion_limit();
                let message = Message::deserialize(&mut deserializer)
                    .unwrap_or(Message::TextLine(line.to_string()));

                match message {
                    Message::BuildScriptExecuted(BuildScript {
                        package_id,
                        out_dir,
                        cfgs,
                        env,
                        ..
                    }) => {
                        let cfgs = {
                            let mut acc = Vec::new();
                            for cfg in cfgs {
                                match cfg.parse::<CfgFlag>() {
                                    Ok(it) => acc.push(it),
                                    Err(err) => {
                                        callback_err = Some(anyhow::format_err!(
                                            "invalid cfg from cargo-metadata: {}",
                                            err
                                        ));
                                        return;
                                    }
                                };
                            }
                            acc
                        };
                        let package_build_data =
                            res.per_package.entry(package_id.repr.clone()).or_default();
                        // cargo_metadata crate returns default (empty) path for
                        // older cargos, which is not absolute, so work around that.
                        if !out_dir.as_str().is_empty() {
                            let out_dir =
                                AbsPathBuf::assert(PathBuf::from(out_dir.into_os_string()));
                            package_build_data.out_dir = Some(out_dir);
                            package_build_data.cfgs = cfgs;
                        }

                        package_build_data.envs = env;
                    }
                    Message::CompilerArtifact(message) => {
                        progress(format!("metadata {}", message.target.name));

                        if message.target.kind.contains(&"proc-macro".to_string()) {
                            let package_id = message.package_id;
                            // Skip rmeta file
                            if let Some(filename) =
                                message.filenames.iter().find(|name| is_dylib(name))
                            {
                                let filename = AbsPathBuf::assert(PathBuf::from(&filename));
                                let package_build_data =
                                    res.per_package.entry(package_id.repr.clone()).or_default();
                                package_build_data.proc_macro_dylib_path = Some(filename);
                            }
                        }
                    }
                    Message::CompilerMessage(message) => {
                        progress(message.target.name.clone());
                    }
                    Message::BuildFinished(_) => {}
                    Message::TextLine(_) => {}
                    _ => {}
                }
            },
            &mut |_| (),
        )?;

        for package in packages {
            let package_build_data = res.per_package.entry(package.id.repr.clone()).or_default();
            inject_cargo_env(package, package_build_data);
            if let Some(out_dir) = &package_build_data.out_dir {
                // NOTE: cargo and rustc seem to hide non-UTF-8 strings from env! and option_env!()
                if let Some(out_dir) = out_dir.to_str().map(|s| s.to_owned()) {
                    package_build_data.envs.push(("OUT_DIR".to_string(), out_dir));
                }
            }
        }

        if !output.status.success() {
            let mut stderr = String::from_utf8(output.stderr).unwrap_or_default();
            if stderr.is_empty() {
                stderr = "cargo check failed".to_string();
            }
            res.error = Some(stderr)
        }

        Ok(res)
    }
}

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

/// Recreates the compile-time environment variables that Cargo sets.
///
/// Should be synced with <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates>
fn inject_cargo_env(package: &cargo_metadata::Package, build_data: &mut PackageBuildData) {
    let env = &mut build_data.envs;

    // FIXME: Missing variables:
    // CARGO_PKG_HOMEPAGE, CARGO_CRATE_NAME, CARGO_BIN_NAME, CARGO_BIN_EXE_<name>

    let mut manifest_dir = package.manifest_path.clone();
    manifest_dir.pop();
    env.push(("CARGO_MANIFEST_DIR".into(), manifest_dir.into_string()));

    // Not always right, but works for common cases.
    env.push(("CARGO".into(), "cargo".into()));

    env.push(("CARGO_PKG_VERSION".into(), package.version.to_string()));
    env.push(("CARGO_PKG_VERSION_MAJOR".into(), package.version.major.to_string()));
    env.push(("CARGO_PKG_VERSION_MINOR".into(), package.version.minor.to_string()));
    env.push(("CARGO_PKG_VERSION_PATCH".into(), package.version.patch.to_string()));

    let pre = package.version.pre.iter().map(|id| id.to_string()).format(".");
    env.push(("CARGO_PKG_VERSION_PRE".into(), pre.to_string()));

    let authors = package.authors.join(";");
    env.push(("CARGO_PKG_AUTHORS".into(), authors));

    env.push(("CARGO_PKG_NAME".into(), package.name.clone()));
    env.push(("CARGO_PKG_DESCRIPTION".into(), package.description.clone().unwrap_or_default()));
    //env.push(("CARGO_PKG_HOMEPAGE".into(), package.homepage.clone().unwrap_or_default()));
    env.push(("CARGO_PKG_REPOSITORY".into(), package.repository.clone().unwrap_or_default()));
    env.push(("CARGO_PKG_LICENSE".into(), package.license.clone().unwrap_or_default()));

    let license_file = package.license_file.as_ref().map(|buf| buf.to_string()).unwrap_or_default();
    env.push(("CARGO_PKG_LICENSE_FILE".into(), license_file));
}
<|endoftext|>


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

use super::*;

// test param_list
// fn a() {}
// fn b(x: i32) {}
// fn c(x: i32, ) {}
// fn d(x: i32, y: ()) {}
pub(super) fn param_list_fn_def(p: &mut Parser) {
    list_(p, Flavor::FnDef)
}

// test param_list_opt_patterns
// fn foo<F: FnMut(&mut Foo<'a>)>(){}
pub(super) fn param_list_fn_trait(p: &mut Parser) {
    list_(p, Flavor::FnTrait)
}

pub(super) fn param_list_fn_ptr(p: &mut Parser) {
    list_(p, Flavor::FnPointer)
}

pub(super) fn param_list_closure(p: &mut Parser) {
    list_(p, Flavor::Closure)
}

#[derive(Debug, Clone, Copy)]
enum Flavor {
    FnDef,   // Includes trait fn params; omitted param idents are not supported
    FnTrait, // Params for `Fn(...)`/`FnMut(...)`/`FnOnce(...)` annotations
    FnPointer,
    Closure,
}

fn list_(p: &mut Parser, flavor: Flavor) {
    use Flavor::*;

    let (bra, ket) = match flavor {
        Closure => (T![|], T![|]),
        FnDef | FnTrait | FnPointer => (T!['('], T![')']),
    };

    let m = p.start();
    p.bump(bra);

    if let FnDef = flavor {
        // test self_param_outer_attr
        // fn f(#[must_use] self) {}
        attributes::outer_attributes(p);
        opt_self_param(p);
    }

    while !p.at(EOF) && !p.at(ket) {
        // test param_outer_arg
        // fn f(#[attr1] pat: Type) {}
        attributes::outer_attributes(p);

        // test param_list_vararg
        // extern "C" { fn printf(format: *const i8, ...) -> i32; }
        match flavor {
            FnDef | FnPointer if p.eat(T![...]) => break,
            _ => (),
        }

        if !p.at_ts(VALUE_PARAMETER_FIRST) {
            p.error("expected value parameter");
            break;
        }
        value_parameter(p, flavor);
        if !p.at(ket) {
            p.expect(T![,]);
        }
    }

    p.expect(ket);
    m.complete(p, PARAM_LIST);
}

const VALUE_PARAMETER_FIRST: TokenSet = patterns::PATTERN_FIRST.union(types::TYPE_FIRST);

fn value_parameter(p: &mut Parser, flavor: Flavor) {
    let m = p.start();
    match flavor {
        // test trait_fn_placeholder_parameter
        // trait Foo {
        //     fn bar(_: u64, mut x: i32);
        // }

        // test trait_fn_patterns
        // trait T {
        //     fn f1((a, b): (usize, usize)) {}
        //     fn f2(S { a, b }: S) {}
        //     fn f3(NewType(a): NewType) {}
        //     fn f4(&&a: &&usize) {}
        // }

        // test fn_patterns
        // impl U {
        //     fn f1((a, b): (usize, usize)) {}
        //     fn f2(S { a, b }: S) {}
        //     fn f3(NewType(a): NewType) {}
        //     fn f4(&&a: &&usize) {}
        // }
        Flavor::FnDef => {
            patterns::pattern(p);
            types::ascription(p);
        }
        // test value_parameters_no_patterns
        // type F = Box<Fn(i32, &i32, &i32, ())>;
        Flavor::FnTrait => {
            types::type_(p);
        }
        // test fn_pointer_param_ident_path
        // type Foo = fn(Bar::Baz);
        // type Qux = fn(baz: Bar::Baz);
        Flavor::FnPointer => {
            if (p.at(IDENT) || p.at(UNDERSCORE)) && p.nth(1) == T![:] && !p.nth_at(1, T![::]) {
                patterns::pattern_single(p);
                types::ascription(p);
            } else {
                types::type_(p);
            }
        }
        // test closure_params
        // fn main() {
        //    let foo = |bar, baz: Baz, qux: Qux::Quux| ();
        // }
        Flavor::Closure => {
            patterns::pattern_single(p);
            if p.at(T![:]) && !p.at(T![::]) {
                types::ascription(p);
            }
        }
    }
    m.complete(p, PARAM);
}

// test self_param
// impl S {
//     fn a(self) {}
//     fn b(&self,) {}
//     fn c(&'a self,) {}
//     fn d(&'a mut self, x: i32) {}
//     fn e(mut self) {}
// }
fn opt_self_param(p: &mut Parser) {
    let m;
    if p.at(T![self]) || p.at(T![mut]) && p.nth(1) == T![self] {
        m = p.start();
        p.eat(T![mut]);
        p.eat(T![self]);
        // test arb_self_types
        // impl S {
        //     fn a(self: &Self) {}
        //     fn b(mut self: Box<Self>) {}
        // }
        if p.at(T![:]) {
            types::ascription(p);
        }
    } else {
        let la1 = p.nth(1);
        let la2 = p.nth(2);
        let la3 = p.nth(3);
        let n_toks = match (p.current(), la1, la2, la3) {
            (T![&], T![self], _, _) => 2,
            (T![&], T![mut], T![self], _) => 3,
            (T![&], LIFETIME, T![self], _) => 3,
            (T![&], LIFETIME, T![mut], T![self]) => 4,
            _ => return,
        };
        m = p.start();
        for _ in 0..n_toks {
            p.bump_any();
        }
    }
    m.complete(p, SELF_PARAM);
    if !p.at(T![')']) {
        p.expect(T![,]);
    }
}
<commit_msg>Add test for unnamed argument in function pointer<commit_after>//! FIXME: write short doc here

use super::*;

// test param_list
// fn a() {}
// fn b(x: i32) {}
// fn c(x: i32, ) {}
// fn d(x: i32, y: ()) {}
pub(super) fn param_list_fn_def(p: &mut Parser) {
    list_(p, Flavor::FnDef)
}

// test param_list_opt_patterns
// fn foo<F: FnMut(&mut Foo<'a>)>(){}
pub(super) fn param_list_fn_trait(p: &mut Parser) {
    list_(p, Flavor::FnTrait)
}

pub(super) fn param_list_fn_ptr(p: &mut Parser) {
    list_(p, Flavor::FnPointer)
}

pub(super) fn param_list_closure(p: &mut Parser) {
    list_(p, Flavor::Closure)
}

#[derive(Debug, Clone, Copy)]
enum Flavor {
    FnDef,   // Includes trait fn params; omitted param idents are not supported
    FnTrait, // Params for `Fn(...)`/`FnMut(...)`/`FnOnce(...)` annotations
    FnPointer,
    Closure,
}

fn list_(p: &mut Parser, flavor: Flavor) {
    use Flavor::*;

    let (bra, ket) = match flavor {
        Closure => (T![|], T![|]),
        FnDef | FnTrait | FnPointer => (T!['('], T![')']),
    };

    let m = p.start();
    p.bump(bra);

    if let FnDef = flavor {
        // test self_param_outer_attr
        // fn f(#[must_use] self) {}
        attributes::outer_attributes(p);
        opt_self_param(p);
    }

    while !p.at(EOF) && !p.at(ket) {
        // test param_outer_arg
        // fn f(#[attr1] pat: Type) {}
        attributes::outer_attributes(p);

        // test param_list_vararg
        // extern "C" { fn printf(format: *const i8, ...) -> i32; }
        match flavor {
            FnDef | FnPointer if p.eat(T![...]) => break,
            _ => (),
        }

        if !p.at_ts(VALUE_PARAMETER_FIRST) {
            p.error("expected value parameter");
            break;
        }
        value_parameter(p, flavor);
        if !p.at(ket) {
            p.expect(T![,]);
        }
    }

    p.expect(ket);
    m.complete(p, PARAM_LIST);
}

const VALUE_PARAMETER_FIRST: TokenSet = patterns::PATTERN_FIRST.union(types::TYPE_FIRST);

fn value_parameter(p: &mut Parser, flavor: Flavor) {
    let m = p.start();
    match flavor {
        // test trait_fn_placeholder_parameter
        // trait Foo {
        //     fn bar(_: u64, mut x: i32);
        // }

        // test trait_fn_patterns
        // trait T {
        //     fn f1((a, b): (usize, usize)) {}
        //     fn f2(S { a, b }: S) {}
        //     fn f3(NewType(a): NewType) {}
        //     fn f4(&&a: &&usize) {}
        // }

        // test fn_patterns
        // impl U {
        //     fn f1((a, b): (usize, usize)) {}
        //     fn f2(S { a, b }: S) {}
        //     fn f3(NewType(a): NewType) {}
        //     fn f4(&&a: &&usize) {}
        // }
        Flavor::FnDef => {
            patterns::pattern(p);
            types::ascription(p);
        }
        // test value_parameters_no_patterns
        // type F = Box<Fn(i32, &i32, &i32, ())>;
        Flavor::FnTrait => {
            types::type_(p);
        }
        // test fn_pointer_param_ident_path
        // type Foo = fn(Bar::Baz);
        // type Qux = fn(baz: Bar::Baz);

        // test fn_pointer_unnamed_arg
        // type Foo = fn(_: bar);
        Flavor::FnPointer => {
            if (p.at(IDENT) || p.at(UNDERSCORE)) && p.nth(1) == T![:] && !p.nth_at(1, T![::]) {
                patterns::pattern_single(p);
                types::ascription(p);
            } else {
                types::type_(p);
            }
        }
        // test closure_params
        // fn main() {
        //    let foo = |bar, baz: Baz, qux: Qux::Quux| ();
        // }
        Flavor::Closure => {
            patterns::pattern_single(p);
            if p.at(T![:]) && !p.at(T![::]) {
                types::ascription(p);
            }
        }
    }
    m.complete(p, PARAM);
}

// test self_param
// impl S {
//     fn a(self) {}
//     fn b(&self,) {}
//     fn c(&'a self,) {}
//     fn d(&'a mut self, x: i32) {}
//     fn e(mut self) {}
// }
fn opt_self_param(p: &mut Parser) {
    let m;
    if p.at(T![self]) || p.at(T![mut]) && p.nth(1) == T![self] {
        m = p.start();
        p.eat(T![mut]);
        p.eat(T![self]);
        // test arb_self_types
        // impl S {
        //     fn a(self: &Self) {}
        //     fn b(mut self: Box<Self>) {}
        // }
        if p.at(T![:]) {
            types::ascription(p);
        }
    } else {
        let la1 = p.nth(1);
        let la2 = p.nth(2);
        let la3 = p.nth(3);
        let n_toks = match (p.current(), la1, la2, la3) {
            (T![&], T![self], _, _) => 2,
            (T![&], T![mut], T![self], _) => 3,
            (T![&], LIFETIME, T![self], _) => 3,
            (T![&], LIFETIME, T![mut], T![self]) => 4,
            _ => return,
        };
        m = p.start();
        for _ in 0..n_toks {
            p.bump_any();
        }
    }
    m.complete(p, SELF_PARAM);
    if !p.at(T![')']) {
        p.expect(T![,]);
    }
}
<|endoftext|>


<commit_before>use super::{
    child_properties::ChildProperties,
    constants::Constants,
    derives::Derives,
    functions::Functions,
    members::Members,
    properties::Properties,
    signals::{Signal, Signals},
};
use crate::{
    analysis::{conversion_type, ref_mode},
    config::{
        error::TomlHelper,
        parsable::{Parsable, Parse},
    },
    library::{self, Library, TypeId, MAIN_NAMESPACE},
    version::Version,
};
use log::warn;
use std::{collections::BTreeMap, str::FromStr};
use toml::Value;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum GStatus {
    Manual,
    Generate,
    Ignore,
}

impl GStatus {
    pub fn ignored(self) -> bool {
        self == GStatus::Ignore
    }
    pub fn need_generate(self) -> bool {
        self == GStatus::Generate
    }
}

impl Default for GStatus {
    fn default() -> GStatus {
        GStatus::Ignore
    }
}

impl FromStr for GStatus {
    type Err = String;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "manual" => Ok(GStatus::Manual),
            "generate" => Ok(GStatus::Generate),
            "ignore" => Ok(GStatus::Ignore),
            e => Err(format!("Wrong object status: \"{}\"", e)),
        }
    }
}

/// Info about `GObject` descendant
#[derive(Clone, Debug)]
pub struct GObject {
    pub name: String,
    pub functions: Functions,
    pub constants: Constants,
    pub signals: Signals,
    pub members: Members,
    pub properties: Properties,
    pub derives: Option<Derives>,
    pub status: GStatus,
    pub module_name: Option<String>,
    pub version: Option<Version>,
    pub cfg_condition: Option<String>,
    pub type_id: Option<TypeId>,
    pub final_type: Option<bool>,
    pub trait_name: Option<String>,
    pub child_properties: Option<ChildProperties>,
    pub concurrency: library::Concurrency,
    pub ref_mode: Option<ref_mode::RefMode>,
    pub must_use: bool,
    pub conversion_type: Option<conversion_type::ConversionType>,
    pub use_boxed_functions: bool,
    pub generate_display_trait: bool,
    pub subclassing: bool,
    pub manual_traits: Vec<String>,
    pub align: Option<u32>,
    pub generate_builder: bool,
}

impl Default for GObject {
    fn default() -> GObject {
        GObject {
            name: "Default".into(),
            functions: Functions::new(),
            constants: Constants::new(),
            signals: Signals::new(),
            members: Members::new(),
            properties: Properties::new(),
            derives: None,
            status: Default::default(),
            module_name: None,
            version: None,
            cfg_condition: None,
            type_id: None,
            final_type: None,
            trait_name: None,
            child_properties: None,
            concurrency: Default::default(),
            ref_mode: None,
            must_use: false,
            conversion_type: None,
            use_boxed_functions: false,
            generate_display_trait: true,
            subclassing: false,
            manual_traits: Vec::default(),
            align: None,
            generate_builder: false,
        }
    }
}

//TODO: ?change to HashMap<String, GStatus>
pub type GObjects = BTreeMap<String, GObject>;

pub fn parse_toml(
    toml_objects: &Value,
    concurrency: library::Concurrency,
    generate_display_trait: bool,
) -> GObjects {
    let mut objects = GObjects::new();
    for toml_object in toml_objects.as_array().unwrap() {
        let gobject = parse_object(toml_object, concurrency, generate_display_trait);
        objects.insert(gobject.name.clone(), gobject);
    }
    objects
}

fn ref_mode_from_str(ref_mode: &str) -> Option<ref_mode::RefMode> {
    use crate::analysis::ref_mode::RefMode::*;

    match ref_mode {
        "none" => Some(None),
        "ref" => Some(ByRef),
        "ref-mut" => Some(ByRefMut),
        "ref-immut" => Some(ByRefImmut),
        "ref-fake" => Some(ByRefFake),
        _ => Option::None,
    }
}

fn conversion_type_from_str(conversion_type: &str) -> Option<conversion_type::ConversionType> {
    use crate::analysis::conversion_type::ConversionType::*;

    match conversion_type {
        "direct" => Some(Direct),
        "scalar" => Some(Scalar),
        "pointer" => Some(Pointer),
        "borrow" => Some(Borrow),
        "unknown" => Some(Unknown),
        _ => None,
    }
}

fn parse_object(
    toml_object: &Value,
    concurrency: library::Concurrency,
    default_generate_display_trait: bool,
) -> GObject {
    let name: String = toml_object
        .lookup("name")
        .expect("Object name not defined")
        .as_str()
        .unwrap()
        .into();
    // Also checks for ChildProperties
    toml_object.check_unwanted(
        &[
            "name",
            "status",
            "function",
            "constant",
            "signal",
            "member",
            "property",
            "derive",
            "module_name",
            "version",
            "concurrency",
            "ref_mode",
            "conversion_type",
            "child_prop",
            "child_name",
            "child_type",
            "final_type",
            "trait",
            "trait_name",
            "cfg_condition",
            "must_use",
            "use_boxed_functions",
            "generate_display_trait",
            "subclassing",
            "manual_traits",
            "align",
            "generate_builder",
        ],
        &format!("object {}", name),
    );

    let status = match toml_object.lookup("status") {
        Some(value) => {
            GStatus::from_str(value.as_str().unwrap()).unwrap_or_else(|_| Default::default())
        }
        None => Default::default(),
    };

    let constants = Constants::parse(toml_object.lookup("constant"), &name);
    let functions = Functions::parse(toml_object.lookup("function"), &name);
    let signals = {
        let mut v = Vec::new();
        if let Some(configs) = toml_object.lookup("signal").and_then(Value::as_array) {
            for config in configs {
                if let Some(item) = Signal::parse(config, &name, concurrency) {
                    v.push(item);
                }
            }
        }

        v
    };
    let members = Members::parse(toml_object.lookup("member"), &name);
    let properties = Properties::parse(toml_object.lookup("property"), &name);
    let derives = if let Some(derives) = toml_object.lookup("derive") {
        Some(Derives::parse(Some(derives), &name))
    } else {
        None
    };
    let module_name = toml_object
        .lookup("module_name")
        .and_then(Value::as_str)
        .map(ToOwned::to_owned);
    let version = toml_object
        .lookup("version")
        .and_then(Value::as_str)
        .and_then(|s| s.parse().ok());
    let cfg_condition = toml_object
        .lookup("cfg_condition")
        .and_then(Value::as_str)
        .map(ToOwned::to_owned);
    let generate_trait = toml_object.lookup("trait").and_then(Value::as_bool);
    let final_type = toml_object
        .lookup("final_type")
        .and_then(Value::as_bool)
        .or_else(|| generate_trait.map(|t| !t));
    let trait_name = toml_object
        .lookup("trait_name")
        .and_then(Value::as_str)
        .map(ToOwned::to_owned);
    let concurrency = toml_object
        .lookup("concurrency")
        .and_then(Value::as_str)
        .and_then(|v| v.parse().ok())
        .unwrap_or(concurrency);
    let ref_mode = toml_object
        .lookup("ref_mode")
        .and_then(Value::as_str)
        .and_then(ref_mode_from_str);
    let conversion_type = toml_object
        .lookup("conversion_type")
        .and_then(Value::as_str)
        .and_then(conversion_type_from_str);
    let child_properties = ChildProperties::parse(toml_object, &name);
    let must_use = toml_object
        .lookup("must_use")
        .and_then(Value::as_bool)
        .unwrap_or(false);
    let use_boxed_functions = toml_object
        .lookup("use_boxed_functions")
        .and_then(Value::as_bool)
        .unwrap_or(false);
    let generate_display_trait = toml_object
        .lookup("generate_display_trait")
        .and_then(Value::as_bool)
        .unwrap_or(default_generate_display_trait);
    let subclassing = toml_object
        .lookup("subclassing")
        .and_then(Value::as_bool)
        .unwrap_or(false);
    let manual_traits = toml_object
        .lookup_vec("manual_traits", "IGNORED ERROR")
        .map(|v| {
            v.iter()
                .filter_map(|v| v.as_str().map(String::from))
                .collect()
        })
        .unwrap_or_else(|_| Vec::new());
    let align = toml_object
        .lookup("align")
        .and_then(Value::as_integer)
        .and_then(|v| {
            if v.count_ones() != 1 || v > i64::from(u32::max_value()) || v < 0 {
                warn!(
                    "`align` configuration must be a power of two of type u32, found {}",
                    v
                );
                None
            } else {
                Some(v as u32)
            }
        });
    let generate_builder = toml_object
        .lookup("generate_builder")
        .and_then(Value::as_bool)
        .unwrap_or(false);

    if status != GStatus::Manual && ref_mode.is_some() {
        warn!("ref_mode configuration used for non-manual object {}", name);
    }

    if status != GStatus::Manual && conversion_type.is_some() {
        warn!(
            "conversion_type configuration used for non-manual object {}",
            name
        );
    }

    if generate_trait.is_some() {
        warn!(
            "`trait` configuration is deprecated and replaced by `final_type` for object {}",
            name
        );
    }

    GObject {
        name,
        functions,
        constants,
        signals,
        members,
        properties,
        derives,
        status,
        module_name,
        version,
        cfg_condition,
        type_id: None,
        final_type,
        trait_name,
        child_properties,
        concurrency,
        ref_mode,
        must_use,
        conversion_type,
        use_boxed_functions,
        generate_display_trait,
        subclassing,
        manual_traits,
        align,
        generate_builder,
    }
}

pub fn parse_status_shorthands(
    objects: &mut GObjects,
    toml: &Value,
    concurrency: library::Concurrency,
    generate_display_trait: bool,
) {
    use self::GStatus::*;
    for &status in &[Manual, Generate, Ignore] {
        parse_status_shorthand(objects, status, toml, concurrency, generate_display_trait);
    }
}

fn parse_status_shorthand(
    objects: &mut GObjects,
    status: GStatus,
    toml: &Value,
    concurrency: library::Concurrency,
    generate_display_trait: bool,
) {
    let name = format!("options.{:?}", status).to_ascii_lowercase();
    if let Some(a) = toml.lookup(&name).map(|a| a.as_array().unwrap()) {
        for name_ in a.iter().map(|s| s.as_str().unwrap()) {
            match objects.get(name_) {
                None => {
                    objects.insert(
                        name_.into(),
                        GObject {
                            name: name_.into(),
                            status,
                            concurrency,
                            generate_display_trait,
                            ..Default::default()
                        },
                    );
                }
                Some(_) => panic!("Bad name in {}: {} already defined", name, name_),
            }
        }
    }
}

pub fn parse_builders(objects: &mut GObjects, toml: &Value) {
    let option_name = "options.builders";
    let suffix = "Builder";
    if let Some(a) = toml.lookup(option_name).map(|a| a.as_array().unwrap()) {
        for name in a.iter().map(|s| s.as_str().unwrap()) {
            // Support both object name and builder name
            let obj_name = if name.ends_with(suffix) {
                &name[..name.len() - suffix.len()]
            } else {
                name
            };
            match objects.get_mut(obj_name) {
                Some(obj) => obj.generate_builder = true,
                None => panic!("Bad name in {}: object {} not defined", option_name, name),
            }
        }
    }
}

pub fn resolve_type_ids(objects: &mut GObjects, library: &Library) {
    let ns = library.namespace(MAIN_NAMESPACE);
    let global_functions_name = format!("{}.*", ns.name);

    for (name, object) in objects.iter_mut() {
        let type_id = library.find_type(0, name);
        if type_id.is_none() && name != &global_functions_name {
            warn!("Configured object `{}` missing from the library", name);
        }
        object.type_id = type_id;
    }
}
<commit_msg>Remove unused subclassing configuration<commit_after>use super::{
    child_properties::ChildProperties,
    constants::Constants,
    derives::Derives,
    functions::Functions,
    members::Members,
    properties::Properties,
    signals::{Signal, Signals},
};
use crate::{
    analysis::{conversion_type, ref_mode},
    config::{
        error::TomlHelper,
        parsable::{Parsable, Parse},
    },
    library::{self, Library, TypeId, MAIN_NAMESPACE},
    version::Version,
};
use log::warn;
use std::{collections::BTreeMap, str::FromStr};
use toml::Value;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum GStatus {
    Manual,
    Generate,
    Ignore,
}

impl GStatus {
    pub fn ignored(self) -> bool {
        self == GStatus::Ignore
    }
    pub fn need_generate(self) -> bool {
        self == GStatus::Generate
    }
}

impl Default for GStatus {
    fn default() -> GStatus {
        GStatus::Ignore
    }
}

impl FromStr for GStatus {
    type Err = String;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "manual" => Ok(GStatus::Manual),
            "generate" => Ok(GStatus::Generate),
            "ignore" => Ok(GStatus::Ignore),
            e => Err(format!("Wrong object status: \"{}\"", e)),
        }
    }
}

/// Info about `GObject` descendant
#[derive(Clone, Debug)]
pub struct GObject {
    pub name: String,
    pub functions: Functions,
    pub constants: Constants,
    pub signals: Signals,
    pub members: Members,
    pub properties: Properties,
    pub derives: Option<Derives>,
    pub status: GStatus,
    pub module_name: Option<String>,
    pub version: Option<Version>,
    pub cfg_condition: Option<String>,
    pub type_id: Option<TypeId>,
    pub final_type: Option<bool>,
    pub trait_name: Option<String>,
    pub child_properties: Option<ChildProperties>,
    pub concurrency: library::Concurrency,
    pub ref_mode: Option<ref_mode::RefMode>,
    pub must_use: bool,
    pub conversion_type: Option<conversion_type::ConversionType>,
    pub use_boxed_functions: bool,
    pub generate_display_trait: bool,
    pub manual_traits: Vec<String>,
    pub align: Option<u32>,
    pub generate_builder: bool,
}

impl Default for GObject {
    fn default() -> GObject {
        GObject {
            name: "Default".into(),
            functions: Functions::new(),
            constants: Constants::new(),
            signals: Signals::new(),
            members: Members::new(),
            properties: Properties::new(),
            derives: None,
            status: Default::default(),
            module_name: None,
            version: None,
            cfg_condition: None,
            type_id: None,
            final_type: None,
            trait_name: None,
            child_properties: None,
            concurrency: Default::default(),
            ref_mode: None,
            must_use: false,
            conversion_type: None,
            use_boxed_functions: false,
            generate_display_trait: true,
            manual_traits: Vec::default(),
            align: None,
            generate_builder: false,
        }
    }
}

//TODO: ?change to HashMap<String, GStatus>
pub type GObjects = BTreeMap<String, GObject>;

pub fn parse_toml(
    toml_objects: &Value,
    concurrency: library::Concurrency,
    generate_display_trait: bool,
) -> GObjects {
    let mut objects = GObjects::new();
    for toml_object in toml_objects.as_array().unwrap() {
        let gobject = parse_object(toml_object, concurrency, generate_display_trait);
        objects.insert(gobject.name.clone(), gobject);
    }
    objects
}

fn ref_mode_from_str(ref_mode: &str) -> Option<ref_mode::RefMode> {
    use crate::analysis::ref_mode::RefMode::*;

    match ref_mode {
        "none" => Some(None),
        "ref" => Some(ByRef),
        "ref-mut" => Some(ByRefMut),
        "ref-immut" => Some(ByRefImmut),
        "ref-fake" => Some(ByRefFake),
        _ => Option::None,
    }
}

fn conversion_type_from_str(conversion_type: &str) -> Option<conversion_type::ConversionType> {
    use crate::analysis::conversion_type::ConversionType::*;

    match conversion_type {
        "direct" => Some(Direct),
        "scalar" => Some(Scalar),
        "pointer" => Some(Pointer),
        "borrow" => Some(Borrow),
        "unknown" => Some(Unknown),
        _ => None,
    }
}

fn parse_object(
    toml_object: &Value,
    concurrency: library::Concurrency,
    default_generate_display_trait: bool,
) -> GObject {
    let name: String = toml_object
        .lookup("name")
        .expect("Object name not defined")
        .as_str()
        .unwrap()
        .into();
    // Also checks for ChildProperties
    toml_object.check_unwanted(
        &[
            "name",
            "status",
            "function",
            "constant",
            "signal",
            "member",
            "property",
            "derive",
            "module_name",
            "version",
            "concurrency",
            "ref_mode",
            "conversion_type",
            "child_prop",
            "child_name",
            "child_type",
            "final_type",
            "trait",
            "trait_name",
            "cfg_condition",
            "must_use",
            "use_boxed_functions",
            "generate_display_trait",
            "manual_traits",
            "align",
            "generate_builder",
        ],
        &format!("object {}", name),
    );

    let status = match toml_object.lookup("status") {
        Some(value) => {
            GStatus::from_str(value.as_str().unwrap()).unwrap_or_else(|_| Default::default())
        }
        None => Default::default(),
    };

    let constants = Constants::parse(toml_object.lookup("constant"), &name);
    let functions = Functions::parse(toml_object.lookup("function"), &name);
    let signals = {
        let mut v = Vec::new();
        if let Some(configs) = toml_object.lookup("signal").and_then(Value::as_array) {
            for config in configs {
                if let Some(item) = Signal::parse(config, &name, concurrency) {
                    v.push(item);
                }
            }
        }

        v
    };
    let members = Members::parse(toml_object.lookup("member"), &name);
    let properties = Properties::parse(toml_object.lookup("property"), &name);
    let derives = if let Some(derives) = toml_object.lookup("derive") {
        Some(Derives::parse(Some(derives), &name))
    } else {
        None
    };
    let module_name = toml_object
        .lookup("module_name")
        .and_then(Value::as_str)
        .map(ToOwned::to_owned);
    let version = toml_object
        .lookup("version")
        .and_then(Value::as_str)
        .and_then(|s| s.parse().ok());
    let cfg_condition = toml_object
        .lookup("cfg_condition")
        .and_then(Value::as_str)
        .map(ToOwned::to_owned);
    let generate_trait = toml_object.lookup("trait").and_then(Value::as_bool);
    let final_type = toml_object
        .lookup("final_type")
        .and_then(Value::as_bool)
        .or_else(|| generate_trait.map(|t| !t));
    let trait_name = toml_object
        .lookup("trait_name")
        .and_then(Value::as_str)
        .map(ToOwned::to_owned);
    let concurrency = toml_object
        .lookup("concurrency")
        .and_then(Value::as_str)
        .and_then(|v| v.parse().ok())
        .unwrap_or(concurrency);
    let ref_mode = toml_object
        .lookup("ref_mode")
        .and_then(Value::as_str)
        .and_then(ref_mode_from_str);
    let conversion_type = toml_object
        .lookup("conversion_type")
        .and_then(Value::as_str)
        .and_then(conversion_type_from_str);
    let child_properties = ChildProperties::parse(toml_object, &name);
    let must_use = toml_object
        .lookup("must_use")
        .and_then(Value::as_bool)
        .unwrap_or(false);
    let use_boxed_functions = toml_object
        .lookup("use_boxed_functions")
        .and_then(Value::as_bool)
        .unwrap_or(false);
    let generate_display_trait = toml_object
        .lookup("generate_display_trait")
        .and_then(Value::as_bool)
        .unwrap_or(default_generate_display_trait);
    let manual_traits = toml_object
        .lookup_vec("manual_traits", "IGNORED ERROR")
        .map(|v| {
            v.iter()
                .filter_map(|v| v.as_str().map(String::from))
                .collect()
        })
        .unwrap_or_else(|_| Vec::new());
    let align = toml_object
        .lookup("align")
        .and_then(Value::as_integer)
        .and_then(|v| {
            if v.count_ones() != 1 || v > i64::from(u32::max_value()) || v < 0 {
                warn!(
                    "`align` configuration must be a power of two of type u32, found {}",
                    v
                );
                None
            } else {
                Some(v as u32)
            }
        });
    let generate_builder = toml_object
        .lookup("generate_builder")
        .and_then(Value::as_bool)
        .unwrap_or(false);

    if status != GStatus::Manual && ref_mode.is_some() {
        warn!("ref_mode configuration used for non-manual object {}", name);
    }

    if status != GStatus::Manual && conversion_type.is_some() {
        warn!(
            "conversion_type configuration used for non-manual object {}",
            name
        );
    }

    if generate_trait.is_some() {
        warn!(
            "`trait` configuration is deprecated and replaced by `final_type` for object {}",
            name
        );
    }

    GObject {
        name,
        functions,
        constants,
        signals,
        members,
        properties,
        derives,
        status,
        module_name,
        version,
        cfg_condition,
        type_id: None,
        final_type,
        trait_name,
        child_properties,
        concurrency,
        ref_mode,
        must_use,
        conversion_type,
        use_boxed_functions,
        generate_display_trait,
        manual_traits,
        align,
        generate_builder,
    }
}

pub fn parse_status_shorthands(
    objects: &mut GObjects,
    toml: &Value,
    concurrency: library::Concurrency,
    generate_display_trait: bool,
) {
    use self::GStatus::*;
    for &status in &[Manual, Generate, Ignore] {
        parse_status_shorthand(objects, status, toml, concurrency, generate_display_trait);
    }
}

fn parse_status_shorthand(
    objects: &mut GObjects,
    status: GStatus,
    toml: &Value,
    concurrency: library::Concurrency,
    generate_display_trait: bool,
) {
    let name = format!("options.{:?}", status).to_ascii_lowercase();
    if let Some(a) = toml.lookup(&name).map(|a| a.as_array().unwrap()) {
        for name_ in a.iter().map(|s| s.as_str().unwrap()) {
            match objects.get(name_) {
                None => {
                    objects.insert(
                        name_.into(),
                        GObject {
                            name: name_.into(),
                            status,
                            concurrency,
                            generate_display_trait,
                            ..Default::default()
                        },
                    );
                }
                Some(_) => panic!("Bad name in {}: {} already defined", name, name_),
            }
        }
    }
}

pub fn parse_builders(objects: &mut GObjects, toml: &Value) {
    let option_name = "options.builders";
    let suffix = "Builder";
    if let Some(a) = toml.lookup(option_name).map(|a| a.as_array().unwrap()) {
        for name in a.iter().map(|s| s.as_str().unwrap()) {
            // Support both object name and builder name
            let obj_name = if name.ends_with(suffix) {
                &name[..name.len() - suffix.len()]
            } else {
                name
            };
            match objects.get_mut(obj_name) {
                Some(obj) => obj.generate_builder = true,
                None => panic!("Bad name in {}: object {} not defined", option_name, name),
            }
        }
    }
}

pub fn resolve_type_ids(objects: &mut GObjects, library: &Library) {
    let ns = library.namespace(MAIN_NAMESPACE);
    let global_functions_name = format!("{}.*", ns.name);

    for (name, object) in objects.iter_mut() {
        let type_id = library.find_type(0, name);
        if type_id.is_none() && name != &global_functions_name {
            warn!("Configured object `{}` missing from the library", name);
        }
        object.type_id = type_id;
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
use std::ffi::CString;
use std::fs::{read_link};
use std::io::{ErrorKind, Error as IoError};
use std::path::{Path};
use std::ptr::null;

use libc::{c_ulong, c_int};
use libmount::{BindMount, Tmpfs};

use file_util::Dir;
use path_util::ToCString;

// sys/mount.h
static MS_RDONLY: c_ulong = 1;                /* Mount read-only.  */
static MS_NOSUID: c_ulong = 2;                /* Ignore suid and sgid bits.  */
static MS_NODEV: c_ulong = 4;                 /* Disallow access to device special files.  */
static MS_NOEXEC: c_ulong = 8;                /* Disallow program execution.  */
static MS_SYNCHRONOUS: c_ulong = 16;          /* Writes are synced at once.  */
static MS_REMOUNT: c_ulong = 32;              /* Alter flags of a mounted FS.  */
static MS_MANDLOCK: c_ulong = 64;             /* Allow mandatory locks on an FS.  */
static MS_DIRSYNC: c_ulong = 128;             /* Directory modifications are synchronous.  */
static MS_NOATIME: c_ulong = 1024;            /* Do not update access times.  */
static MS_NODIRATIME: c_ulong = 2048;         /* Do not update directory access times.  */
static MS_BIND: c_ulong = 4096;               /* Bind directory at different place.  */
static MS_MOVE: c_ulong = 8192;
static MS_REC: c_ulong = 16384;
static MS_SILENT: c_ulong = 32768;
static MS_POSIXACL: c_ulong = 1 << 16;        /* VFS does not apply the umask.  */
static MS_UNBINDABLE: c_ulong = 1 << 17;      /* Change to unbindable.  */
static MS_PRIVATE: c_ulong = 1 << 18;         /* Change to private.  */
static MS_SLAVE: c_ulong = 1 << 19;           /* Change to slave.  */
static MS_SHARED: c_ulong = 1 << 20;          /* Change to shared.  */
static MS_RELATIME: c_ulong = 1 << 21;        /* Update atime relative to mtime/ctime.  */
static MS_KERNMOUNT: c_ulong = 1 << 22;       /* This is a kern_mount call.  */
static MS_I_VERSION: c_ulong =  1 << 23;      /* Update inode I_version field.  */
static MS_STRICTATIME: c_ulong = 1 << 24;     /* Always perform atime updates.  */
static MS_ACTIVE: c_ulong = 1 << 30;
static MS_NOUSER: c_ulong = 1 << 31;

static MNT_FORCE: c_int = 1;           /* Force unmounting.  */
static MNT_DETACH: c_int = 2;          /* Just detach from the tree.  */
static MNT_EXPIRE: c_int = 4;          /* Mark for expiry.  */
static UMOUNT_NOFOLLOW: c_int = 8;     /* Don't follow symlink on umount.  */

extern {
    fn mount(source: *const u8, target: *const u8,
        filesystemtype: *const u8, flags: c_ulong,
        data: *const u8) -> c_int;
    fn umount(target: *const u8) -> c_int;
    fn umount2(target: *const u8, flags: c_int) -> c_int;
}

pub fn mount_private(target: &Path) -> Result<(), String> {
    let none = CString::new("none").unwrap();
    let c_target = target.to_cstring();
    debug!("Making private {:?}", target);
    let rc = unsafe { mount(
        none.as_bytes().as_ptr(),
        c_target.as_bytes().as_ptr(),
        null(), MS_PRIVATE, null()) };
    if rc == 0 {
        return Ok(());
    } else {
        let err = IoError::last_os_error();
        return Err(format!("Can't make {:?} a slave: {}", target, err));
    }
}

pub fn mount_proc(target: &Path) -> Result<(), String>
{
    let c_target = target.to_cstring();

    // I don't know why we need this flag for mounting proc, but it's somehow
    // works (see https://github.com/tailhook/vagga/issues/12 for the
    // error it fixes)
    let flags = 0xC0ED0000; //MS_MGC_VAL

    debug!("Procfs mount {:?}", target);
    let rc = unsafe { mount(
        b"proc\x00".as_ptr(), c_target.as_bytes().as_ptr(),
        b"proc\x00".as_ptr(), flags, null()) };
    if rc == 0 {
        return Ok(());
    } else {
        let err = IoError::last_os_error();
        return Err(format!("Can't mount proc at {:?}: {}",
            target, err));
    }
}

pub fn mount_pseudo(target: &Path, name: &str, options: &str)
    -> Result<(), String>
{
    let c_name = name.to_cstring();
    let c_target = target.to_cstring();
    let c_opts = options.to_cstring();
    // Seems this is similar to why proc is mounted with the flag
    let flags = 0xC0ED0000; //MS_MGC_VAL

    debug!("Pseusofs mount {:?} {} {}", target, name, options);
    let rc = unsafe { mount(
        c_name.as_bytes().as_ptr(),
        c_target.as_bytes().as_ptr(),
        c_name.as_bytes().as_ptr(),
        flags,
        c_opts.as_bytes().as_ptr()) };
    if rc == 0 {
        return Ok(());
    } else {
        let err = IoError::last_os_error();
        return Err(format!("Can't mount pseudofs {:?} ({}, options: {}): {}",
            target, options, name, err));
    }
}

#[cfg(not(feature="containers"))]
pub fn unmount(target: &Path) -> Result<(), String> {
    unimplemented!();
}

#[cfg(feature="containers")]
pub fn unmount(target: &Path) -> Result<(), String> {
    let c_target = target.to_cstring();
    let rc = unsafe { umount2(c_target.as_bytes().as_ptr(), MNT_DETACH) };
    if rc == 0 {
        return Ok(());
    } else {
        let err = IoError::last_os_error();
        return Err(format!("Can't unmount {:?} : {}", target, err));
    }
}

pub fn mount_system_dirs() -> Result<(), String> {
    mount_dev(&Path::new("/vagga/root/dev"))?;
    BindMount::new("/sys", "/vagga/root/sys").mount()
        .map_err(|e| e.to_string())?;
    mount_proc(&Path::new("/vagga/root/proc"))?;
    BindMount::new("/work", "/vagga/root/work").mount()
        .map_err(|e| e.to_string())?;
    return Ok(())
}

pub fn unmount_system_dirs() -> Result<(), String> {
    unmount(Path::new("/vagga/root/work"))?;
    unmount(Path::new("/vagga/root/proc"))?;
    unmount(Path::new("/vagga/root/sys"))?;
    unmount(Path::new("/vagga/root/dev"))?;
    Ok(())
}

pub fn mount_dev(dev_dir: &Path) -> Result<(), String> {
    BindMount::new("/dev", &dev_dir).mount()
        .map_err(|e| e.to_string())?;

    let pts_dir = dev_dir.join("pts");
    mount_pseudo(&pts_dir, "devpts", "newinstance")?;

    let ptmx_path = dev_dir.join("ptmx");
    match read_link(&ptmx_path) {
        Ok(x) => {
            if Path::new(&x) != Path::new("/dev/pts/ptmx")
               && Path::new(&x) != Path::new("pts/ptmx")
            {
                warn!("The /dev/ptmx refers to {:?}. We need /dev/pts/ptmx \
                    to operate properly. \
                    Probably pseudo-ttys will not work", x);
            }
        }
        Err(ref e) if e.kind() == ErrorKind::InvalidInput => {
            // It's just a device. Let's try bind mount
            BindMount::new(pts_dir.join("ptmx"), &ptmx_path).mount()
                .map_err(|e| e.to_string())?;
        }
        Err(e) => {
            warn!("Can't stat /dev/ptmx: {}. \
                Probably pseudo-ttys will not work", e);
        }
    }
    Ok(())
}

pub fn mount_run(run_dir: &Path) -> Result<(), String> {
    Tmpfs::new(&run_dir)
        .size_bytes(100 << 20)
        .mode(0o755)
        .mount().map_err(|e| format!("{}", e))?;
    try_msg!(Dir::new(&run_dir.join("shm")).mode(0o1777).create(),
        "Error creating /vagga/root/run/shm: {err}");
    Ok(())
}
<commit_msg>Fixed building containers based on ubuntu xenial<commit_after>#![allow(dead_code)]
use std::ffi::CString;
use std::fs::{read_link};
use std::io::{ErrorKind, Error as IoError};
use std::path::{Path};
use std::ptr::null;

use libc::{c_ulong, c_int};
use libmount::{BindMount, Tmpfs};

use path_util::ToCString;

// sys/mount.h
static MS_RDONLY: c_ulong = 1;                /* Mount read-only.  */
static MS_NOSUID: c_ulong = 2;                /* Ignore suid and sgid bits.  */
static MS_NODEV: c_ulong = 4;                 /* Disallow access to device special files.  */
static MS_NOEXEC: c_ulong = 8;                /* Disallow program execution.  */
static MS_SYNCHRONOUS: c_ulong = 16;          /* Writes are synced at once.  */
static MS_REMOUNT: c_ulong = 32;              /* Alter flags of a mounted FS.  */
static MS_MANDLOCK: c_ulong = 64;             /* Allow mandatory locks on an FS.  */
static MS_DIRSYNC: c_ulong = 128;             /* Directory modifications are synchronous.  */
static MS_NOATIME: c_ulong = 1024;            /* Do not update access times.  */
static MS_NODIRATIME: c_ulong = 2048;         /* Do not update directory access times.  */
static MS_BIND: c_ulong = 4096;               /* Bind directory at different place.  */
static MS_MOVE: c_ulong = 8192;
static MS_REC: c_ulong = 16384;
static MS_SILENT: c_ulong = 32768;
static MS_POSIXACL: c_ulong = 1 << 16;        /* VFS does not apply the umask.  */
static MS_UNBINDABLE: c_ulong = 1 << 17;      /* Change to unbindable.  */
static MS_PRIVATE: c_ulong = 1 << 18;         /* Change to private.  */
static MS_SLAVE: c_ulong = 1 << 19;           /* Change to slave.  */
static MS_SHARED: c_ulong = 1 << 20;          /* Change to shared.  */
static MS_RELATIME: c_ulong = 1 << 21;        /* Update atime relative to mtime/ctime.  */
static MS_KERNMOUNT: c_ulong = 1 << 22;       /* This is a kern_mount call.  */
static MS_I_VERSION: c_ulong =  1 << 23;      /* Update inode I_version field.  */
static MS_STRICTATIME: c_ulong = 1 << 24;     /* Always perform atime updates.  */
static MS_ACTIVE: c_ulong = 1 << 30;
static MS_NOUSER: c_ulong = 1 << 31;

static MNT_FORCE: c_int = 1;           /* Force unmounting.  */
static MNT_DETACH: c_int = 2;          /* Just detach from the tree.  */
static MNT_EXPIRE: c_int = 4;          /* Mark for expiry.  */
static UMOUNT_NOFOLLOW: c_int = 8;     /* Don't follow symlink on umount.  */

extern {
    fn mount(source: *const u8, target: *const u8,
        filesystemtype: *const u8, flags: c_ulong,
        data: *const u8) -> c_int;
    fn umount(target: *const u8) -> c_int;
    fn umount2(target: *const u8, flags: c_int) -> c_int;
}

pub fn mount_private(target: &Path) -> Result<(), String> {
    let none = CString::new("none").unwrap();
    let c_target = target.to_cstring();
    debug!("Making private {:?}", target);
    let rc = unsafe { mount(
        none.as_bytes().as_ptr(),
        c_target.as_bytes().as_ptr(),
        null(), MS_PRIVATE, null()) };
    if rc == 0 {
        return Ok(());
    } else {
        let err = IoError::last_os_error();
        return Err(format!("Can't make {:?} a slave: {}", target, err));
    }
}

pub fn mount_proc(target: &Path) -> Result<(), String>
{
    let c_target = target.to_cstring();

    // I don't know why we need this flag for mounting proc, but it's somehow
    // works (see https://github.com/tailhook/vagga/issues/12 for the
    // error it fixes)
    let flags = 0xC0ED0000; //MS_MGC_VAL

    debug!("Procfs mount {:?}", target);
    let rc = unsafe { mount(
        b"proc\x00".as_ptr(), c_target.as_bytes().as_ptr(),
        b"proc\x00".as_ptr(), flags, null()) };
    if rc == 0 {
        return Ok(());
    } else {
        let err = IoError::last_os_error();
        return Err(format!("Can't mount proc at {:?}: {}",
            target, err));
    }
}

pub fn mount_pseudo(target: &Path, name: &str, options: &str)
    -> Result<(), String>
{
    let c_name = name.to_cstring();
    let c_target = target.to_cstring();
    let c_opts = options.to_cstring();
    // Seems this is similar to why proc is mounted with the flag
    let flags = 0xC0ED0000; //MS_MGC_VAL

    debug!("Pseusofs mount {:?} {} {}", target, name, options);
    let rc = unsafe { mount(
        c_name.as_bytes().as_ptr(),
        c_target.as_bytes().as_ptr(),
        c_name.as_bytes().as_ptr(),
        flags,
        c_opts.as_bytes().as_ptr()) };
    if rc == 0 {
        return Ok(());
    } else {
        let err = IoError::last_os_error();
        return Err(format!("Can't mount pseudofs {:?} ({}, options: {}): {}",
            target, options, name, err));
    }
}

#[cfg(not(feature="containers"))]
pub fn unmount(target: &Path) -> Result<(), String> {
    unimplemented!();
}

#[cfg(feature="containers")]
pub fn unmount(target: &Path) -> Result<(), String> {
    let c_target = target.to_cstring();
    let rc = unsafe { umount2(c_target.as_bytes().as_ptr(), MNT_DETACH) };
    if rc == 0 {
        return Ok(());
    } else {
        let err = IoError::last_os_error();
        return Err(format!("Can't unmount {:?} : {}", target, err));
    }
}

pub fn mount_system_dirs() -> Result<(), String> {
    mount_dev(&Path::new("/vagga/root/dev"))?;
    BindMount::new("/sys", "/vagga/root/sys").mount()
        .map_err(|e| e.to_string())?;
    mount_proc(&Path::new("/vagga/root/proc"))?;
    BindMount::new("/work", "/vagga/root/work").mount()
        .map_err(|e| e.to_string())?;
    return Ok(())
}

pub fn unmount_system_dirs() -> Result<(), String> {
    unmount(Path::new("/vagga/root/work"))?;
    unmount(Path::new("/vagga/root/proc"))?;
    unmount(Path::new("/vagga/root/sys"))?;
    unmount(Path::new("/vagga/root/dev"))?;
    Ok(())
}

pub fn mount_dev(dev_dir: &Path) -> Result<(), String> {
    BindMount::new("/dev", &dev_dir).mount()
        .map_err(|e| e.to_string())?;

    let pts_dir = dev_dir.join("pts");
    mount_pseudo(&pts_dir, "devpts", "newinstance")?;

    let ptmx_path = dev_dir.join("ptmx");
    match read_link(&ptmx_path) {
        Ok(x) => {
            if Path::new(&x) != Path::new("/dev/pts/ptmx")
               && Path::new(&x) != Path::new("pts/ptmx")
            {
                warn!("The /dev/ptmx refers to {:?}. We need /dev/pts/ptmx \
                    to operate properly. \
                    Probably pseudo-ttys will not work", x);
            }
        }
        Err(ref e) if e.kind() == ErrorKind::InvalidInput => {
            // It's just a device. Let's try bind mount
            BindMount::new(pts_dir.join("ptmx"), &ptmx_path).mount()
                .map_err(|e| e.to_string())?;
        }
        Err(e) => {
            warn!("Can't stat /dev/ptmx: {}. \
                Probably pseudo-ttys will not work", e);
        }
    }
    Ok(())
}

pub fn mount_run(run_dir: &Path) -> Result<(), String> {
    Tmpfs::new(&run_dir)
        .size_bytes(100 << 20)
        .mode(0o755)
        .mount().map_err(|e| format!("{}", e))?;
    Ok(())
}
<|endoftext|>


<commit_before>use af;
use af::{Array};
use num::Complex;

use utils;
use error::HALError;

/// Returns the tanh activated value
pub fn tanh(x: &Array) -> Array {
  af::tanh(x)
}

/// Returns the sigmoid activated value
/// 1.0/(1.0 + exp(-1.0 * e))
pub fn sigmoid(x: &Array) -> Array {
  let neg_one = utils::constant(x.dims(), x.get_type(), -1.0f32);
  let one = utils::constant(x.dims(), x.get_type(), 1.0f32);
  let exp_m_e = af::exp(&af::mul(&neg_one, x, false));
  let denominator = af::add(&one, &exp_m_e, false);
  let a = af::div(&one, &denominator, false);
  utils::assert_types(vec![x, &a]);
  a
}

/// Return the softmax activated value [numerical stable]
/// exp(x_i) / sum(exp(x))
pub fn softmax(x: &Array) -> Array {
  // http://www.deeplearningbook.org/contents/mlp.html page 185
  let z = match x.numdims() {
    1 => x.clone(),
    _ => af::sub(x, &af::max(x, 1), true),
  };

  let exponentiated = af::exp(&z);
  let sum_epx_x = af::sum_all(&exponentiated).0 as f32;
  let sum_exp_x_vec = utils::constant(x.dims(), x.get_type(), sum_epx_x);
  let a = af::div(&exponentiated, &sum_exp_x_vec, false);
  utils::assert_types(vec![x, &a]);
  a
}

/// Return the lrelu activated value
/// max(0.01*x, x)
pub fn lrelu(x: &Array) -> Array {
  let zero_one = utils::constant(x.dims(), x.get_type(), 0.01f32);
  let scaled = af::mul(x, &zero_one, false);
  let a = af::select(&scaled                               // return 0.01x
                     , &af::lt(x, &0.0f32, false) // if x > 0.0
                     , x);                        // else x
  utils::assert_types(vec![x, &a]);
  a
}

/// Return the derivative of relu [assumes that relu has already been applied]
/// 0.01     for x <= 0
/// 1        otherwise
pub fn lrelu_derivative(x: &Array) -> Array {
  let x_lt_zero = utils::constant(x.dims(), x.get_type(), 0.01f32);
  let one = utils::constant(x.dims(), x.get_type(), 1.0f32);
  let grad = af::select(&one, &af::gt(x, &0.0f32, false), &x_lt_zero);
  utils::assert_types(vec![x, &grad]);
  grad
}

/// Return the relu activated value
/// max(0, x)
pub fn relu(x: &Array) -> Array {
  let zero = utils::constant(x.dims(), x.get_type(), 0.0f32);
  let a = af::select(&zero, &af::lt(x, &0.0, false), x);
  utils::assert_types(vec![x, &a]);
  a
}

/// Return the derivative of relu [assumes that relu has already been applied]
/// 0 for x <= 0
/// 1 otherwise
pub fn relu_derivative(x: &Array) -> Array {
  let zero = utils::constant(x.dims(), x.get_type(), 0.0f32);
  let one = utils::constant(x.dims(), x.get_type(), 1.0f32);
  let grad = af::select(&one, &af::gt(x, &0.0f32, false), &zero);
  utils::assert_types(vec![x, &grad]);
  grad
}

/// Return the derivative of tanh [assumes that tanh has already been applied]
/// 1 - tanh(x)*tanh(x)
pub fn tanh_derivative(x: &Array) -> Array {
  let one = utils::constant(x.dims(), x.get_type(), 1.0f32);
  let grad = af::sub(&one, &af::mul(x, x, false), false);
  utils::assert_types(vec![x, &grad]);
  grad
}

/// Returns the derivative of sigmoid [assumes that sigmoid is already applied]
/// x * (1 - x)
pub fn sigmoid_derivative(x: &Array) -> Array {
  let one = utils::constant(x.dims(), x.get_type(), 1.0f32);
  let grad = af::mul(x, &af::sub(&one, x, false), false);
  utils::assert_types(vec![x, &grad]);
  grad
}

/// Returns the derivative of softmax [assumes that it was already applied]
/// x * (1 - x)
pub fn softmax_derivative(x: &Array) -> Array {
  // let orig_dims = x.dims();
  // let slide_dims = orig_dims;
  // slide_dims[2] = slide_dims[1];
  // slide_dims[1] = 1;
  // let j = af::mul(x, &af::moddims(x, slide_dims));
  // let diag = sigmoid_derivative(x);
  // af::print(&af::diag_extract(&j, 1))
  sigmoid_derivative(x)
}

/// Returns a linear activation [no non-linearity]
pub fn ones(x: &Array) -> Array {
  x.clone()
}

/// Returns a derivative of a linear activation (1's)
pub fn ones_derivative(x: &Array) -> Array {
  let grad = utils::constant(x.dims(), x.get_type(), 1.0f32);
  utils::assert_types(vec![x, &grad]);
  grad
}

/// Helper to determine whether function is smooth or non-smooth
pub fn is_smooth(name: &str) -> bool {
  match name {
    "softmax" => true,
    "sigmoid" => true,
    "relu"    => false,
    "lrelu"   => false,
    "tanh"    => true,
    "ones"    => true,
    "linear"  => true,
    _         => panic!("unknown function name provided"),
  }
}

/// Helper to get the correct activation using a string
pub fn get_activation(name: &str, x: &Array) -> Result<Array, HALError> {
  match name {
    "softmax" => Ok(softmax(x)),
    "sigmoid" => Ok(sigmoid(x)),
    "relu"    => Ok(relu(x)),
    "lrelu"   => Ok(lrelu(x)),
    "tanh"    => Ok(tanh(x)),
    "ones"    => Ok(ones(x)),
    "linear"  => Ok(ones(x)),
    _         => Err(HALError::UNKNOWN),
  }
}

/// Helper to get the correct activation derivative using a string
pub fn get_derivative(name: &str, x: &Array) -> Result<Array, HALError> {
  match name {
    "softmax" => Ok(softmax_derivative(x)),
    "sigmoid" => Ok(sigmoid_derivative(x)),
    "relu"    => Ok(relu_derivative(x)),
    "lrelu"   => Ok(lrelu_derivative(x)),
    "tanh"    => Ok(tanh_derivative(x)),
    "ones"    => Ok(ones_derivative(x)),
    "linear"  => Ok(ones_derivative(x)),
    _         => Err(HALError::UNKNOWN),
  }
}
<commit_msg>fix softmax<commit_after>use af;
use af::{Array};
use num::Complex;

use utils;
use error::HALError;

/// Returns the tanh activated value
pub fn tanh(x: &Array) -> Array {
  af::tanh(x)
}

/// Returns the sigmoid activated value
/// 1.0/(1.0 + exp(-1.0 * e))
pub fn sigmoid(x: &Array) -> Array {
  let neg_one = utils::constant(x.dims(), x.get_type(), -1.0f32);
  let one = utils::constant(x.dims(), x.get_type(), 1.0f32);
  let exp_m_e = af::exp(&af::mul(&neg_one, x, false));
  let denominator = af::add(&one, &exp_m_e, false);
  let a = af::div(&one, &denominator, false);
  utils::assert_types(vec![x, &a]);
  a
}

/// Return the softmax activated value [numerical stable]
/// exp(x_i) / sum(exp(x))
pub fn softmax(x: &Array) -> Array {
  // http://www.deeplearningbook.org/contents/mlp.html page 185
  let z = match x.numdims() {
    1 => x.clone(),
    _ => af::sub(x, &af::max(x, 1), true),
  };

  let exponentiated = af::exp(&z);
  let sum_exp_x_vec = af::sum(&exponentiated, 1);
  let a = af::div(&exponentiated, &sum_exp_x_vec, true);
  utils::assert_types(vec![x, &a]);
  a
}

/// Return the lrelu activated value
/// max(0.01*x, x)
pub fn lrelu(x: &Array) -> Array {
  let zero_one = utils::constant(x.dims(), x.get_type(), 0.01f32);
  let scaled = af::mul(x, &zero_one, false);
  let a = af::select(&scaled                               // return 0.01x
                     , &af::lt(x, &0.0f32, false) // if x > 0.0
                     , x);                        // else x
  utils::assert_types(vec![x, &a]);
  a
}

/// Return the derivative of relu [assumes that relu has already been applied]
/// 0.01     for x <= 0
/// 1        otherwise
pub fn lrelu_derivative(x: &Array) -> Array {
  let x_lt_zero = utils::constant(x.dims(), x.get_type(), 0.01f32);
  let one = utils::constant(x.dims(), x.get_type(), 1.0f32);
  let grad = af::select(&one, &af::gt(x, &0.0f32, false), &x_lt_zero);
  utils::assert_types(vec![x, &grad]);
  grad
}

/// Return the relu activated value
/// max(0, x)
pub fn relu(x: &Array) -> Array {
  let zero = utils::constant(x.dims(), x.get_type(), 0.0f32);
  let a = af::select(&zero, &af::lt(x, &0.0, false), x);
  utils::assert_types(vec![x, &a]);
  a
}

/// Return the derivative of relu [assumes that relu has already been applied]
/// 0 for x <= 0
/// 1 otherwise
pub fn relu_derivative(x: &Array) -> Array {
  let zero = utils::constant(x.dims(), x.get_type(), 0.0f32);
  let one = utils::constant(x.dims(), x.get_type(), 1.0f32);
  let grad = af::select(&one, &af::gt(x, &0.0f32, false), &zero);
  utils::assert_types(vec![x, &grad]);
  grad
}

/// Return the derivative of tanh [assumes that tanh has already been applied]
/// 1 - tanh(x)*tanh(x)
pub fn tanh_derivative(x: &Array) -> Array {
  let one = utils::constant(x.dims(), x.get_type(), 1.0f32);
  let grad = af::sub(&one, &af::mul(x, x, false), false);
  utils::assert_types(vec![x, &grad]);
  grad
}

/// Returns the derivative of sigmoid [assumes that sigmoid is already applied]
/// x * (1 - x)
pub fn sigmoid_derivative(x: &Array) -> Array {
  let one = utils::constant(x.dims(), x.get_type(), 1.0f32);
  let grad = af::mul(x, &af::sub(&one, x, false), false);
  utils::assert_types(vec![x, &grad]);
  grad
}

/// Returns the derivative of softmax [assumes that it was already applied]
/// x * (1 - x)
pub fn softmax_derivative(x: &Array) -> Array {
  // let orig_dims = x.dims();
  // let slide_dims = orig_dims;
  // slide_dims[2] = slide_dims[1];
  // slide_dims[1] = 1;
  // let j = af::mul(x, &af::moddims(x, slide_dims));
  // let diag = sigmoid_derivative(x);
  // af::print(&af::diag_extract(&j, 1))
  sigmoid_derivative(x)
}

/// Returns a linear activation [no non-linearity]
pub fn ones(x: &Array) -> Array {
  x.clone()
}

/// Returns a derivative of a linear activation (1's)
pub fn ones_derivative(x: &Array) -> Array {
  let grad = utils::constant(x.dims(), x.get_type(), 1.0f32);
  utils::assert_types(vec![x, &grad]);
  grad
}

/// Helper to determine whether function is smooth or non-smooth
pub fn is_smooth(name: &str) -> bool {
  match name {
    "softmax" => true,
    "sigmoid" => true,
    "relu"    => false,
    "lrelu"   => false,
    "tanh"    => true,
    "ones"    => true,
    "linear"  => true,
    _         => panic!("unknown function name provided"),
  }
}

/// Helper to get the correct activation using a string
pub fn get_activation(name: &str, x: &Array) -> Result<Array, HALError> {
  match name {
    "softmax" => Ok(softmax(x)),
    "sigmoid" => Ok(sigmoid(x)),
    "relu"    => Ok(relu(x)),
    "lrelu"   => Ok(lrelu(x)),
    "tanh"    => Ok(tanh(x)),
    "ones"    => Ok(ones(x)),
    "linear"  => Ok(ones(x)),
    _         => Err(HALError::UNKNOWN),
  }
}

/// Helper to get the correct activation derivative using a string
pub fn get_derivative(name: &str, x: &Array) -> Result<Array, HALError> {
  match name {
    "softmax" => Ok(softmax_derivative(x)),
    "sigmoid" => Ok(sigmoid_derivative(x)),
    "relu"    => Ok(relu_derivative(x)),
    "lrelu"   => Ok(lrelu_derivative(x)),
    "tanh"    => Ok(tanh_derivative(x)),
    "ones"    => Ok(ones_derivative(x)),
    "linear"  => Ok(ones_derivative(x)),
    _         => Err(HALError::UNKNOWN),
  }
}
<|endoftext|>