<commit_before>use super::{
    function_body_chunk,
    general::{
        cfg_condition, cfg_deprecated, doc_hidden, not_version_condition, version_condition,
    },
    parameter::ToParameter,
    return_value::{out_parameters_as_return, ToReturnValue},
};
use crate::{
    analysis::{
        self,
        bounds::{Bound, Bounds},
        functions::Visibility,
        namespaces,
    },
    chunk::{ffi_function_todo, Chunk},
    env::Env,
    library,
    writer::{primitives::tabs, ToCode},
};
use log::warn;
use std::{
    fmt,
    io::{Result, Write},
    result::Result as StdResult,
};

pub fn generate(
    w: &mut dyn Write,
    env: &Env,
    analysis: &analysis::functions::Info,
    in_trait: bool,
    only_declaration: bool,
    indent: usize,
) -> Result<()> {
    if analysis.is_async_finish(env) {
        return Ok(());
    }

    let mut commented = false;
    let mut comment_prefix = "";
    let mut pub_prefix = if in_trait { "" } else { "pub " };

    match analysis.visibility {
        Visibility::Public => {}
        Visibility::Comment => {
            commented = true;
            comment_prefix = "//";
        }
        Visibility::Private => {
            if in_trait {
                warn!(
                    "Generating trait method for private function {}",
                    analysis.glib_name
                );
            } else {
                pub_prefix = "";
            }
        }
        Visibility::Hidden => return Ok(()),
    }
    let declaration = declaration(env, analysis);
    let suffix = if only_declaration { ";" } else { " {" };

    writeln!(w)?;
    if !in_trait || only_declaration {
        cfg_deprecated(w, env, analysis.deprecated_version, commented, indent)?;
    }
    cfg_condition(w, &analysis.cfg_condition, commented, indent)?;
    version_condition(w, env, analysis.version, commented, indent)?;
    not_version_condition(w, analysis.not_version, commented, indent)?;
    doc_hidden(w, analysis.doc_hidden, comment_prefix, indent)?;
    writeln!(
        w,
        "{}{}{}{}{}",
        tabs(indent),
        comment_prefix,
        pub_prefix,
        declaration,
        suffix,
    )?;

    if !only_declaration {
        let body = body_chunk(env, analysis).to_code(env);
        for s in body {
            writeln!(w, "{}{}", tabs(indent), s)?;
        }
    }

    if analysis.async_future.is_some() {
        let declaration = declaration_futures(env, analysis);
        let suffix = if only_declaration { ";" } else { " {" };

        writeln!(w)?;
        if !in_trait || only_declaration {
            cfg_deprecated(w, env, analysis.deprecated_version, commented, indent)?;
        }

        writeln!(w, "{}{}", tabs(indent), comment_prefix)?;
        cfg_condition(w, &analysis.cfg_condition, commented, indent)?;
        version_condition(w, env, analysis.version, commented, indent)?;
        not_version_condition(w, analysis.not_version, commented, indent)?;
        doc_hidden(w, analysis.doc_hidden, comment_prefix, indent)?;
        writeln!(
            w,
            "{}{}{}{}{}",
            tabs(indent),
            comment_prefix,
            pub_prefix,
            declaration,
            suffix
        )?;

        if !only_declaration {
            let body = body_chunk_futures(env, analysis).unwrap();
            for s in body.lines() {
                if !s.is_empty() {
                    writeln!(w, "{}{}{}", tabs(indent + 1), comment_prefix, s)?;
                } else {
                    writeln!(w)?;
                }
            }
            writeln!(w, "{}{}}}", tabs(indent), comment_prefix)?;
        }
    }

    Ok(())
}

pub fn declaration(env: &Env, analysis: &analysis::functions::Info) -> String {
    let outs_as_return = !analysis.outs.is_empty();
    let return_str = if outs_as_return {
        out_parameters_as_return(env, analysis)
    } else if analysis.ret.bool_return_is_error.is_some() {
        if env.namespaces.glib_ns_id == namespaces::MAIN {
            " -> Result<(), error::BoolError>".into()
        } else {
            " -> Result<(), glib::error::BoolError>".into()
        }
    } else {
        analysis.ret.to_return_value(env, false)
    };
    let mut param_str = String::with_capacity(100);

    let (bounds, _) = bounds(&analysis.bounds, &[], false, false);

    for par in analysis.parameters.rust_parameters.iter() {
        if !param_str.is_empty() {
            param_str.push_str(", ")
        }
        let c_par = &analysis.parameters.c_parameters[par.ind_c];
        let s = c_par.to_parameter(env, &analysis.bounds);
        param_str.push_str(&s);
    }

    format!(
        "fn {}{}({}){}",
        analysis.name, bounds, param_str, return_str,
    )
}

pub fn declaration_futures(env: &Env, analysis: &analysis::functions::Info) -> String {
    let async_future = analysis.async_future.as_ref().unwrap();

    let return_str = format!(
        " -> Pin<Box_<dyn std::future::Future<Output = Result<{}, {}>> + 'static>>",
        async_future.success_parameters, async_future.error_parameters
    );

    let mut param_str = String::with_capacity(100);

    let mut skipped = 0;
    let mut skipped_bounds = vec![];
    for (pos, par) in analysis.parameters.rust_parameters.iter().enumerate() {
        let c_par = &analysis.parameters.c_parameters[par.ind_c];

        if c_par.name == "callback" || c_par.name == "cancellable" {
            skipped += 1;
            if let Some((t, _)) = analysis.bounds.get_parameter_alias_info(&c_par.name) {
                skipped_bounds.push(t);
                if let Some(p) = analysis.bounds.get_base_alias(t) {
                    skipped_bounds.push(p);
                }
            }
            continue;
        }

        if pos - skipped > 0 {
            param_str.push_str(", ")
        }

        let s = c_par.to_parameter(env, &analysis.bounds);
        param_str.push_str(&s);
    }

    let (bounds, _) = bounds(&analysis.bounds, skipped_bounds.as_ref(), true, false);

    format!(
        "fn {}{}({}){}",
        async_future.name, bounds, param_str, return_str,
    )
}

pub fn bound_to_string(bound: &Bound, r#async: bool) -> String {
    use crate::analysis::bounds::BoundType::*;

    match bound.bound_type {
        NoWrapper => format!("{}: {}", bound.alias, bound.type_str),
        IsA(Some(lifetime)) => format!(
            "{}: IsA<{}> + {}",
            bound.alias,
            bound.type_str,
            if r#async {
                "Clone + 'static".into()
            } else {
                format!("'{}", lifetime)
            }
        ),
        IsA(None) => format!(
            "{}: IsA<{}>{}",
            bound.alias,
            bound.type_str,
            if r#async { " + Clone + 'static" } else { "" }
        ),
        // This case should normally never happened
        AsRef(Some(_ /*lifetime*/)) => {
            unreachable!();
            // format!("{}: AsRef<{}> + '{}", bound.alias, bound.type_str, lifetime)
        }
        AsRef(None) => format!("{}: AsRef<{}>", bound.alias, bound.type_str),
    }
}

pub fn bounds(
    bounds: &Bounds,
    skip: &[char],
    r#async: bool,
    filter_callback_modified: bool,
) -> (String, Vec<String>) {
    use crate::analysis::bounds::BoundType::*;

    if bounds.is_empty() {
        return (String::new(), Vec::new());
    }

    let skip_lifetimes = bounds
        .iter()
        .filter(|bound| skip.contains(&bound.alias))
        .filter_map(|bound| match bound.bound_type {
            IsA(Some(lifetime)) | AsRef(Some(lifetime)) => Some(lifetime),
            _ => None,
        })
        .collect::<Vec<_>>();

    let strs: Vec<String> = bounds
        .iter_lifetimes()
        .filter(|s| !skip_lifetimes.contains(s))
        .map(|s| format!("'{}", s))
        .chain(
            bounds
                .iter()
                .filter(|bound| {
                    !skip.contains(&bound.alias)
                        && (!filter_callback_modified || !bound.callback_modified)
                })
                .map(|b| bound_to_string(b, r#async)),
        )
        .collect();

    if strs.is_empty() {
        (String::new(), Vec::new())
    } else {
        let bounds = bounds
            .iter_lifetimes()
            .filter(|s| !skip_lifetimes.contains(s))
            .map(|s| format!("'{}", s))
            .chain(
                bounds
                    .iter()
                    .filter(|bound| {
                        !skip.contains(&bound.alias)
                            && (!filter_callback_modified || !bound.callback_modified)
                    })
                    .map(|b| b.alias.to_string()),
            )
            .collect::<Vec<_>>();
        (format!("<{}>", strs.join(", ")), bounds)
    }
}

pub fn body_chunk(env: &Env, analysis: &analysis::functions::Info) -> Chunk {
    if analysis.visibility == Visibility::Comment {
        return ffi_function_todo(env, &analysis.glib_name);
    }

    let outs_as_return = !analysis.outs.is_empty();

    let mut builder = function_body_chunk::Builder::new();
    builder
        .glib_name(&format!(
            "{}::{}",
            env.main_sys_crate_name(),
            analysis.glib_name
        ))
        .assertion(analysis.assertion)
        .ret(&analysis.ret)
        .transformations(&analysis.parameters.transformations)
        .outs_mode(analysis.outs.mode);

    if analysis.r#async {
        if let Some(ref trampoline) = analysis.trampoline {
            builder.async_trampoline(trampoline);
        } else {
            warn!(
                "Async function {} has no associated _finish function",
                analysis.name
            );
        }
    } else {
        for trampoline in analysis.callbacks.iter() {
            builder.callback(trampoline);
        }
        for trampoline in analysis.destroys.iter() {
            builder.destroy(trampoline);
        }
    }

    for par in &analysis.parameters.c_parameters {
        if outs_as_return && analysis.outs.iter().any(|p| p.name == par.name) {
            builder.out_parameter(env, par);
        } else {
            builder.parameter();
        }
    }

    let (bounds, bounds_names) = bounds(&analysis.bounds, &[], false, true);

    builder.generate(env, bounds, bounds_names.join(", "))
}

pub fn body_chunk_futures(
    env: &Env,
    analysis: &analysis::functions::Info,
) -> StdResult<String, fmt::Error> {
    use crate::analysis::ref_mode::RefMode;
    use std::fmt::Write;

    let async_future = analysis.async_future.as_ref().unwrap();

    let mut body = String::new();

    if env.config.library_name != "Gio" {
        writeln!(body, "use gio::GioFuture;")?;
    } else {
        writeln!(body, "use GioFuture;")?;
    }
    writeln!(body, "use fragile::Fragile;")?;
    writeln!(body)?;

    let skip = if async_future.is_method { 1 } else { 0 };

    // Skip the instance parameter
    for par in analysis.parameters.rust_parameters.iter().skip(skip) {
        if par.name == "cancellable" || par.name == "callback" {
            continue;
        }

        let c_par = &analysis.parameters.c_parameters[par.ind_c];

        let type_ = env.type_(par.typ);
        let is_str = if let library::Type::Fundamental(library::Fundamental::Utf8) = *type_ {
            true
        } else {
            false
        };

        if *c_par.nullable {
            writeln!(
                body,
                "let {} = {}.map(ToOwned::to_owned);",
                par.name, par.name
            )?;
        } else if is_str {
            writeln!(body, "let {} = String::from({});", par.name, par.name)?;
        } else if c_par.ref_mode != RefMode::None {
            writeln!(body, "let {} = {}.clone();", par.name, par.name)?;
        }
    }

    if async_future.is_method {
        writeln!(body, "GioFuture::new(self, move |obj, send| {{")?;
    } else {
        writeln!(body, "GioFuture::new(&(), move |_obj, send| {{")?;
    }

    if env.config.library_name != "Gio" {
        writeln!(body, "\tlet cancellable = gio::Cancellable::new();")?;
    } else {
        writeln!(body, "\tlet cancellable = Cancellable::new();")?;
    }
    writeln!(body, "\tlet send = Fragile::new(send);")?;

    if async_future.is_method {
        writeln!(body, "\tobj.{}(", analysis.name)?;
    } else if analysis.type_name.is_ok() {
        writeln!(body, "\tSelf::{}(", analysis.name)?;
    } else {
        writeln!(body, "\t{}(", analysis.name)?;
    }

    // Skip the instance parameter
    for par in analysis.parameters.rust_parameters.iter().skip(skip) {
        if par.name == "cancellable" {
            writeln!(body, "\t\tSome(&cancellable),")?;
        } else if par.name == "callback" {
            continue;
        } else {
            let c_par = &analysis.parameters.c_parameters[par.ind_c];

            if *c_par.nullable {
                writeln!(
                    body,
                    "\t\t{}.as_ref().map(::std::borrow::Borrow::borrow),",
                    par.name
                )?;
            } else if c_par.ref_mode != RefMode::None {
                writeln!(body, "\t\t&{},", par.name)?;
            } else {
                writeln!(body, "\t\t{},", par.name)?;
            }
        }
    }

    writeln!(body, "\t\tmove |res| {{")?;
    writeln!(body, "\t\t\tlet _ = send.into_inner().send(res);")?;
    writeln!(body, "\t\t}},")?;
    writeln!(body, "\t);")?;
    writeln!(body)?;
    writeln!(body, "\tcancellable")?;
    writeln!(body, "}})")?;

    Ok(body)
}
<commit_msg>Generate GIO futures code a bit more simple and without requiring all crates to directly use the fragile crate<commit_after>use super::{
    function_body_chunk,
    general::{
        cfg_condition, cfg_deprecated, doc_hidden, not_version_condition, version_condition,
    },
    parameter::ToParameter,
    return_value::{out_parameters_as_return, ToReturnValue},
};
use crate::{
    analysis::{
        self,
        bounds::{Bound, Bounds},
        functions::Visibility,
        namespaces,
    },
    chunk::{ffi_function_todo, Chunk},
    env::Env,
    library,
    writer::{primitives::tabs, ToCode},
};
use log::warn;
use std::{
    fmt,
    io::{Result, Write},
    result::Result as StdResult,
};

pub fn generate(
    w: &mut dyn Write,
    env: &Env,
    analysis: &analysis::functions::Info,
    in_trait: bool,
    only_declaration: bool,
    indent: usize,
) -> Result<()> {
    if analysis.is_async_finish(env) {
        return Ok(());
    }

    let mut commented = false;
    let mut comment_prefix = "";
    let mut pub_prefix = if in_trait { "" } else { "pub " };

    match analysis.visibility {
        Visibility::Public => {}
        Visibility::Comment => {
            commented = true;
            comment_prefix = "//";
        }
        Visibility::Private => {
            if in_trait {
                warn!(
                    "Generating trait method for private function {}",
                    analysis.glib_name
                );
            } else {
                pub_prefix = "";
            }
        }
        Visibility::Hidden => return Ok(()),
    }
    let declaration = declaration(env, analysis);
    let suffix = if only_declaration { ";" } else { " {" };

    writeln!(w)?;
    if !in_trait || only_declaration {
        cfg_deprecated(w, env, analysis.deprecated_version, commented, indent)?;
    }
    cfg_condition(w, &analysis.cfg_condition, commented, indent)?;
    version_condition(w, env, analysis.version, commented, indent)?;
    not_version_condition(w, analysis.not_version, commented, indent)?;
    doc_hidden(w, analysis.doc_hidden, comment_prefix, indent)?;
    writeln!(
        w,
        "{}{}{}{}{}",
        tabs(indent),
        comment_prefix,
        pub_prefix,
        declaration,
        suffix,
    )?;

    if !only_declaration {
        let body = body_chunk(env, analysis).to_code(env);
        for s in body {
            writeln!(w, "{}{}", tabs(indent), s)?;
        }
    }

    if analysis.async_future.is_some() {
        let declaration = declaration_futures(env, analysis);
        let suffix = if only_declaration { ";" } else { " {" };

        writeln!(w)?;
        if !in_trait || only_declaration {
            cfg_deprecated(w, env, analysis.deprecated_version, commented, indent)?;
        }

        writeln!(w, "{}{}", tabs(indent), comment_prefix)?;
        cfg_condition(w, &analysis.cfg_condition, commented, indent)?;
        version_condition(w, env, analysis.version, commented, indent)?;
        not_version_condition(w, analysis.not_version, commented, indent)?;
        doc_hidden(w, analysis.doc_hidden, comment_prefix, indent)?;
        writeln!(
            w,
            "{}{}{}{}{}",
            tabs(indent),
            comment_prefix,
            pub_prefix,
            declaration,
            suffix
        )?;

        if !only_declaration {
            let body = body_chunk_futures(env, analysis).unwrap();
            for s in body.lines() {
                if !s.is_empty() {
                    writeln!(w, "{}{}{}", tabs(indent + 1), comment_prefix, s)?;
                } else {
                    writeln!(w)?;
                }
            }
            writeln!(w, "{}{}}}", tabs(indent), comment_prefix)?;
        }
    }

    Ok(())
}

pub fn declaration(env: &Env, analysis: &analysis::functions::Info) -> String {
    let outs_as_return = !analysis.outs.is_empty();
    let return_str = if outs_as_return {
        out_parameters_as_return(env, analysis)
    } else if analysis.ret.bool_return_is_error.is_some() {
        if env.namespaces.glib_ns_id == namespaces::MAIN {
            " -> Result<(), error::BoolError>".into()
        } else {
            " -> Result<(), glib::error::BoolError>".into()
        }
    } else {
        analysis.ret.to_return_value(env, false)
    };
    let mut param_str = String::with_capacity(100);

    let (bounds, _) = bounds(&analysis.bounds, &[], false, false);

    for par in analysis.parameters.rust_parameters.iter() {
        if !param_str.is_empty() {
            param_str.push_str(", ")
        }
        let c_par = &analysis.parameters.c_parameters[par.ind_c];
        let s = c_par.to_parameter(env, &analysis.bounds);
        param_str.push_str(&s);
    }

    format!(
        "fn {}{}({}){}",
        analysis.name, bounds, param_str, return_str,
    )
}

pub fn declaration_futures(env: &Env, analysis: &analysis::functions::Info) -> String {
    let async_future = analysis.async_future.as_ref().unwrap();

    let return_str = format!(
        " -> Pin<Box_<dyn std::future::Future<Output = Result<{}, {}>> + 'static>>",
        async_future.success_parameters, async_future.error_parameters
    );

    let mut param_str = String::with_capacity(100);

    let mut skipped = 0;
    let mut skipped_bounds = vec![];
    for (pos, par) in analysis.parameters.rust_parameters.iter().enumerate() {
        let c_par = &analysis.parameters.c_parameters[par.ind_c];

        if c_par.name == "callback" || c_par.name == "cancellable" {
            skipped += 1;
            if let Some((t, _)) = analysis.bounds.get_parameter_alias_info(&c_par.name) {
                skipped_bounds.push(t);
                if let Some(p) = analysis.bounds.get_base_alias(t) {
                    skipped_bounds.push(p);
                }
            }
            continue;
        }

        if pos - skipped > 0 {
            param_str.push_str(", ")
        }

        let s = c_par.to_parameter(env, &analysis.bounds);
        param_str.push_str(&s);
    }

    let (bounds, _) = bounds(&analysis.bounds, skipped_bounds.as_ref(), true, false);

    format!(
        "fn {}{}({}){}",
        async_future.name, bounds, param_str, return_str,
    )
}

pub fn bound_to_string(bound: &Bound, r#async: bool) -> String {
    use crate::analysis::bounds::BoundType::*;

    match bound.bound_type {
        NoWrapper => format!("{}: {}", bound.alias, bound.type_str),
        IsA(Some(lifetime)) => format!(
            "{}: IsA<{}> + {}",
            bound.alias,
            bound.type_str,
            if r#async {
                "Clone + 'static".into()
            } else {
                format!("'{}", lifetime)
            }
        ),
        IsA(None) => format!(
            "{}: IsA<{}>{}",
            bound.alias,
            bound.type_str,
            if r#async { " + Clone + 'static" } else { "" }
        ),
        // This case should normally never happened
        AsRef(Some(_ /*lifetime*/)) => {
            unreachable!();
            // format!("{}: AsRef<{}> + '{}", bound.alias, bound.type_str, lifetime)
        }
        AsRef(None) => format!("{}: AsRef<{}>", bound.alias, bound.type_str),
    }
}

pub fn bounds(
    bounds: &Bounds,
    skip: &[char],
    r#async: bool,
    filter_callback_modified: bool,
) -> (String, Vec<String>) {
    use crate::analysis::bounds::BoundType::*;

    if bounds.is_empty() {
        return (String::new(), Vec::new());
    }

    let skip_lifetimes = bounds
        .iter()
        .filter(|bound| skip.contains(&bound.alias))
        .filter_map(|bound| match bound.bound_type {
            IsA(Some(lifetime)) | AsRef(Some(lifetime)) => Some(lifetime),
            _ => None,
        })
        .collect::<Vec<_>>();

    let strs: Vec<String> = bounds
        .iter_lifetimes()
        .filter(|s| !skip_lifetimes.contains(s))
        .map(|s| format!("'{}", s))
        .chain(
            bounds
                .iter()
                .filter(|bound| {
                    !skip.contains(&bound.alias)
                        && (!filter_callback_modified || !bound.callback_modified)
                })
                .map(|b| bound_to_string(b, r#async)),
        )
        .collect();

    if strs.is_empty() {
        (String::new(), Vec::new())
    } else {
        let bounds = bounds
            .iter_lifetimes()
            .filter(|s| !skip_lifetimes.contains(s))
            .map(|s| format!("'{}", s))
            .chain(
                bounds
                    .iter()
                    .filter(|bound| {
                        !skip.contains(&bound.alias)
                            && (!filter_callback_modified || !bound.callback_modified)
                    })
                    .map(|b| b.alias.to_string()),
            )
            .collect::<Vec<_>>();
        (format!("<{}>", strs.join(", ")), bounds)
    }
}

pub fn body_chunk(env: &Env, analysis: &analysis::functions::Info) -> Chunk {
    if analysis.visibility == Visibility::Comment {
        return ffi_function_todo(env, &analysis.glib_name);
    }

    let outs_as_return = !analysis.outs.is_empty();

    let mut builder = function_body_chunk::Builder::new();
    builder
        .glib_name(&format!(
            "{}::{}",
            env.main_sys_crate_name(),
            analysis.glib_name
        ))
        .assertion(analysis.assertion)
        .ret(&analysis.ret)
        .transformations(&analysis.parameters.transformations)
        .outs_mode(analysis.outs.mode);

    if analysis.r#async {
        if let Some(ref trampoline) = analysis.trampoline {
            builder.async_trampoline(trampoline);
        } else {
            warn!(
                "Async function {} has no associated _finish function",
                analysis.name
            );
        }
    } else {
        for trampoline in analysis.callbacks.iter() {
            builder.callback(trampoline);
        }
        for trampoline in analysis.destroys.iter() {
            builder.destroy(trampoline);
        }
    }

    for par in &analysis.parameters.c_parameters {
        if outs_as_return && analysis.outs.iter().any(|p| p.name == par.name) {
            builder.out_parameter(env, par);
        } else {
            builder.parameter();
        }
    }

    let (bounds, bounds_names) = bounds(&analysis.bounds, &[], false, true);

    builder.generate(env, bounds, bounds_names.join(", "))
}

pub fn body_chunk_futures(
    env: &Env,
    analysis: &analysis::functions::Info,
) -> StdResult<String, fmt::Error> {
    use crate::analysis::ref_mode::RefMode;
    use std::fmt::Write;

    let async_future = analysis.async_future.as_ref().unwrap();

    let mut body = String::new();

    let gio_future_name = if env.config.library_name != "Gio" {
        "gio::GioFuture"
    } else {
        "crate::GioFuture"
    };
    writeln!(body)?;

    let skip = if async_future.is_method { 1 } else { 0 };

    // Skip the instance parameter
    for par in analysis.parameters.rust_parameters.iter().skip(skip) {
        if par.name == "cancellable" || par.name == "callback" {
            continue;
        }

        let c_par = &analysis.parameters.c_parameters[par.ind_c];

        let type_ = env.type_(par.typ);
        let is_str = if let library::Type::Fundamental(library::Fundamental::Utf8) = *type_ {
            true
        } else {
            false
        };

        if *c_par.nullable {
            writeln!(
                body,
                "let {} = {}.map(ToOwned::to_owned);",
                par.name, par.name
            )?;
        } else if is_str {
            writeln!(body, "let {} = String::from({});", par.name, par.name)?;
        } else if c_par.ref_mode != RefMode::None {
            writeln!(body, "let {} = {}.clone();", par.name, par.name)?;
        }
    }

    if async_future.is_method {
        writeln!(body, "Box_::pin({}::new(self, move |obj, send| {{", gio_future_name)?;
    } else {
        writeln!(body, "Box_::pin({}::new(&(), move |_obj, send| {{", gio_future_name)?;
    }

    if env.config.library_name != "Gio" {
        writeln!(body, "\tlet cancellable = gio::Cancellable::new();")?;
    } else {
        writeln!(body, "\tlet cancellable = Cancellable::new();")?;
    }
    if async_future.is_method {
        writeln!(body, "\tobj.{}(", analysis.name)?;
    } else if analysis.type_name.is_ok() {
        writeln!(body, "\tSelf::{}(", analysis.name)?;
    } else {
        writeln!(body, "\t{}(", analysis.name)?;
    }

    // Skip the instance parameter
    for par in analysis.parameters.rust_parameters.iter().skip(skip) {
        if par.name == "cancellable" {
            writeln!(body, "\t\tSome(&cancellable),")?;
        } else if par.name == "callback" {
            continue;
        } else {
            let c_par = &analysis.parameters.c_parameters[par.ind_c];

            if *c_par.nullable {
                writeln!(
                    body,
                    "\t\t{}.as_ref().map(::std::borrow::Borrow::borrow),",
                    par.name
                )?;
            } else if c_par.ref_mode != RefMode::None {
                writeln!(body, "\t\t&{},", par.name)?;
            } else {
                writeln!(body, "\t\t{},", par.name)?;
            }
        }
    }

    writeln!(body, "\t\tmove |res| {{")?;
    writeln!(body, "\t\t\tsend.resolve(res);")?;
    writeln!(body, "\t\t}},")?;
    writeln!(body, "\t);")?;
    writeln!(body)?;
    writeln!(body, "\tcancellable")?;
    writeln!(body, "}}))")?;

    Ok(body)
}
<|endoftext|>


<commit_before>import syntax::{visit, ast_util};
import syntax::ast::*;
import syntax::codemap::span;
import ty::{kind, kind_copyable, kind_sendable, kind_noncopyable};
import driver::session::session;

// Kind analysis pass. There are three kinds:
//
//  sendable: scalar types, and unique types containing only sendable types
//  copyable: boxes, closures, and uniques containing copyable types
//  noncopyable: resources, or unique types containing resources
//
// This pass ensures that type parameters are only instantiated with types
// whose kinds are equal or less general than the way the type parameter was
// annotated (with the `send` or `copy` keyword).
//
// It also verifies that noncopyable kinds are not copied. Sendability is not
// applied, since none of our language primitives send. Instead, the sending
// primitives in the stdlib are explicitly annotated to only take sendable
// types.

fn kind_to_str(k: kind) -> str {
    alt k {
      kind_sendable { "sendable" }
      kind_copyable { "copyable" }
      kind_noncopyable { "noncopyable" }
    }
}

type rval_map = std::map::hashmap<node_id, ()>;

type ctx = {tcx: ty::ctxt,
            rval_map: rval_map,
            method_map: typeck::method_map,
            last_uses: last_use::last_uses};

fn check_crate(tcx: ty::ctxt, method_map: typeck::method_map,
               last_uses: last_use::last_uses, crate: @crate)
    -> rval_map {
    let ctx = {tcx: tcx,
               rval_map: std::map::new_int_hash(),
               method_map: method_map,
               last_uses: last_uses};
    let visit = visit::mk_vt(@{
        visit_expr: check_expr,
        visit_stmt: check_stmt,
        visit_block: check_block,
        visit_fn: check_fn
        with *visit::default_visitor()
    });
    visit::visit_crate(*crate, ctx, visit);
    tcx.sess.abort_if_errors();
    ret ctx.rval_map;
}

// Yields the appropriate function to check the kind of closed over
// variables. `id` is the node_id for some expression that creates the
// closure.
fn with_appropriate_checker(cx: ctx, id: node_id,
                            b: fn(fn@(ctx, ty::t, sp: span))) {
    let fty = ty::node_id_to_type(cx.tcx, id);
    alt ty::ty_fn_proto(fty) {
      proto_uniq { b(check_send); }
      proto_box { b(check_copy); }
      proto_bare { b(check_none); }
      proto_any | proto_block { /* no check needed */ }
    }
}

// Check that the free variables used in a shared/sendable closure conform
// to the copy/move kind bounds. Then recursively check the function body.
fn check_fn(fk: visit::fn_kind, decl: fn_decl, body: blk, sp: span,
            fn_id: node_id, cx: ctx, v: visit::vt<ctx>) {

    // n.b.: This could be the body of either a fn decl or a fn expr.  In the
    // former case, the prototype will be proto_bare and no check occurs.  In
    // the latter case, we do not check the variables that in the capture
    // clause (as we don't have access to that here) but just those that
    // appear free.  The capture clauses are checked below, in check_expr().
    //
    // We could do this check also in check_expr(), but it seems more
    // "future-proof" to do it this way, as check_fn_body() is supposed to be
    // the common flow point for all functions that appear in the AST.

    with_appropriate_checker(cx, fn_id) { |checker|
        for @{def, span} in *freevars::get_freevars(cx.tcx, fn_id) {
            let id = ast_util::def_id_of_def(def).node;
            if checker == check_copy {
                let last_uses = alt check cx.last_uses.find(fn_id) {
                  some(last_use::closes_over(vars)) { vars }
                  none { [] }
                };
                if option::is_some(vec::position_elt(last_uses, id)) { cont; }
            }
            let ty = ty::node_id_to_type(cx.tcx, id);
            checker(cx, ty, span);
        }
    }

    visit::visit_fn(fk, decl, body, sp, fn_id, cx, v);
}

fn check_fn_cap_clause(cx: ctx,
                       id: node_id,
                       cap_clause: capture_clause) {
    // Check that the variables named in the clause which are not free vars
    // (if any) are also legal.  freevars are checked above in check_fn().
    // This is kind of a degenerate case, as captured variables will generally
    // appear free in the body.
    let freevars = freevars::get_freevars(cx.tcx, id);
    let freevar_ids = vec::map(*freevars, { |freevar|
        ast_util::def_id_of_def(freevar.def).node
    });
    //log("freevar_ids", freevar_ids);
    with_appropriate_checker(cx, id) { |checker|
        let check_var = fn@(&&cap_item: @capture_item) {
            let cap_def = cx.tcx.def_map.get(cap_item.id);
            let cap_def_id = ast_util::def_id_of_def(cap_def).node;
            if !vec::contains(freevar_ids, cap_def_id) {
                let ty = ty::node_id_to_type(cx.tcx, cap_def_id);
                checker(cx, ty, cap_item.span);
            }
        };
        vec::iter(cap_clause.copies, check_var);
        vec::iter(cap_clause.moves, check_var);
    }
}

fn check_block(b: blk, cx: ctx, v: visit::vt<ctx>) {
    alt b.node.expr {
      some(ex) { maybe_copy(cx, ex); }
      _ {}
    }
    visit::visit_block(b, cx, v);
}

fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
    alt e.node {
      expr_assign(_, ex) | expr_assign_op(_, _, ex) |
      expr_unary(box(_), ex) | expr_unary(uniq(_), ex) |
      expr_ret(some(ex)) { maybe_copy(cx, ex); }
      expr_copy(expr) { check_copy_ex(cx, expr, false); }
      // Vector add copies.
      expr_binary(add, ls, rs) { maybe_copy(cx, ls); maybe_copy(cx, rs); }
      expr_rec(fields, def) {
        for field in fields { maybe_copy(cx, field.node.expr); }
        alt def {
          some(ex) {
            // All noncopyable fields must be overridden
            let t = ty::expr_ty(cx.tcx, ex);
            let ty_fields = alt ty::get(t).struct {
              ty::ty_rec(f) { f }
              _ { cx.tcx.sess.span_bug(ex.span, "Bad expr type in record"); }
            };
            for tf in ty_fields {
                if !vec::any(fields, {|f| f.node.ident == tf.ident}) &&
                    !ty::kind_can_be_copied(ty::type_kind(cx.tcx, tf.mt.ty)) {
                    cx.tcx.sess.span_err(ex.span,
                                         "copying a noncopyable value");
                }
            }
          }
          _ {}
        }
      }
      expr_tup(exprs) | expr_vec(exprs, _) {
        for expr in exprs { maybe_copy(cx, expr); }
      }
      expr_bind(_, args) {
        for a in args { alt a { some(ex) { maybe_copy(cx, ex); } _ {} } }
      }
      expr_call(f, args, _) {
        let i = 0u;
        for arg_t in ty::ty_fn_args(ty::expr_ty(cx.tcx, f)) {
            alt ty::arg_mode(cx.tcx, arg_t) {
              by_copy { maybe_copy(cx, args[i]); }
              by_ref | by_val | by_mutbl_ref | by_move { }
            }
            i += 1u;
        }
      }
      expr_path(_) {
        alt cx.tcx.node_type_substs.find(e.id) {
          some(ts) {
            let did = ast_util::def_id_of_def(cx.tcx.def_map.get(e.id));
            let bounds = ty::lookup_item_type(cx.tcx, did).bounds;
            let i = 0u;
            for ty in ts {
                let kind = ty::type_kind(cx.tcx, ty);
                let p_kind = ty::param_bounds_to_kind(bounds[i]);
                if !ty::kind_lteq(p_kind, kind) {
                    cx.tcx.sess.span_err(e.span, "instantiating a " +
                                         kind_to_str(p_kind) +
                                         " type parameter with a "
                                         + kind_to_str(kind) + " type");
                }
                i += 1u;
            }
          }
          none {}
        }
      }
      expr_fn(_, _, _, cap_clause) {
        check_fn_cap_clause(cx, e.id, *cap_clause);
      }
      _ { }
    }
    visit::visit_expr(e, cx, v);
}

fn check_stmt(stmt: @stmt, cx: ctx, v: visit::vt<ctx>) {
    alt stmt.node {
      stmt_decl(@{node: decl_local(locals), _}, _) {
        for local in locals {
            alt local.node.init {
              some({op: init_assign, expr}) { maybe_copy(cx, expr); }
              _ {}
            }
        }
      }
      _ {}
    }
    visit::visit_stmt(stmt, cx, v);
}

fn maybe_copy(cx: ctx, ex: @expr) {
    check_copy_ex(cx, ex, true);
}

fn is_nullary_variant(cx: ctx, ex: @expr) -> bool {
    alt ex.node {
      expr_path(_) {
        alt cx.tcx.def_map.get(ex.id) {
          def_variant(edid, vdid) {
            vec::len(ty::enum_variant_with_id(cx.tcx, edid, vdid).args) == 0u
          }
          _ { false }
        }
      }
      _ { false }
    }
}

fn check_copy_ex(cx: ctx, ex: @expr, _warn: bool) {
    if ty::expr_is_lval(cx.method_map, ex) &&
       !cx.last_uses.contains_key(ex.id) &&
       !is_nullary_variant(cx, ex) {
        let ty = ty::expr_ty(cx.tcx, ex);
        check_copy(cx, ty, ex.span);
        // FIXME turn this on again once vector types are no longer unique.
        // Right now, it is too annoying to be useful.
        /* if warn && ty::type_is_unique(ty) {
            cx.tcx.sess.span_warn(ex.span, "copying a unique value");
        }*/
    }
}

fn check_copy(cx: ctx, ty: ty::t, sp: span) {
    if !ty::kind_can_be_copied(ty::type_kind(cx.tcx, ty)) {
        cx.tcx.sess.span_err(sp, "copying a noncopyable value");
    }
}

fn check_send(cx: ctx, ty: ty::t, sp: span) {
    if !ty::kind_can_be_sent(ty::type_kind(cx.tcx, ty)) {
        cx.tcx.sess.span_err(sp, "not a sendable value");
    }
}

fn check_none(cx: ctx, _ty: ty::t, sp: span) {
    cx.tcx.sess.span_err(sp, "attempted dynamic environment capture");
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//
<commit_msg>Delete-trailing-whitespace<commit_after>import syntax::{visit, ast_util};
import syntax::ast::*;
import syntax::codemap::span;
import ty::{kind, kind_copyable, kind_sendable, kind_noncopyable};
import driver::session::session;

// Kind analysis pass. There are three kinds:
//
//  sendable: scalar types, and unique types containing only sendable types
//  copyable: boxes, closures, and uniques containing copyable types
//  noncopyable: resources, or unique types containing resources
//
// This pass ensures that type parameters are only instantiated with types
// whose kinds are equal or less general than the way the type parameter was
// annotated (with the `send` or `copy` keyword).
//
// It also verifies that noncopyable kinds are not copied. Sendability is not
// applied, since none of our language primitives send. Instead, the sending
// primitives in the stdlib are explicitly annotated to only take sendable
// types.

fn kind_to_str(k: kind) -> str {
    alt k {
      kind_sendable { "sendable" }
      kind_copyable { "copyable" }
      kind_noncopyable { "noncopyable" }
    }
}

type rval_map = std::map::hashmap<node_id, ()>;

type ctx = {tcx: ty::ctxt,
            rval_map: rval_map,
            method_map: typeck::method_map,
            last_uses: last_use::last_uses};

fn check_crate(tcx: ty::ctxt, method_map: typeck::method_map,
               last_uses: last_use::last_uses, crate: @crate)
    -> rval_map {
    let ctx = {tcx: tcx,
               rval_map: std::map::new_int_hash(),
               method_map: method_map,
               last_uses: last_uses};
    let visit = visit::mk_vt(@{
        visit_expr: check_expr,
        visit_stmt: check_stmt,
        visit_block: check_block,
        visit_fn: check_fn
        with *visit::default_visitor()
    });
    visit::visit_crate(*crate, ctx, visit);
    tcx.sess.abort_if_errors();
    ret ctx.rval_map;
}

// Yields the appropriate function to check the kind of closed over
// variables. `id` is the node_id for some expression that creates the
// closure.
fn with_appropriate_checker(cx: ctx, id: node_id,
                            b: fn(fn@(ctx, ty::t, sp: span))) {
    let fty = ty::node_id_to_type(cx.tcx, id);
    alt ty::ty_fn_proto(fty) {
      proto_uniq { b(check_send); }
      proto_box { b(check_copy); }
      proto_bare { b(check_none); }
      proto_any | proto_block { /* no check needed */ }
    }
}

// Check that the free variables used in a shared/sendable closure conform
// to the copy/move kind bounds. Then recursively check the function body.
fn check_fn(fk: visit::fn_kind, decl: fn_decl, body: blk, sp: span,
            fn_id: node_id, cx: ctx, v: visit::vt<ctx>) {

    // n.b.: This could be the body of either a fn decl or a fn expr.  In the
    // former case, the prototype will be proto_bare and no check occurs.  In
    // the latter case, we do not check the variables that in the capture
    // clause (as we don't have access to that here) but just those that
    // appear free.  The capture clauses are checked below, in check_expr().
    //
    // We could do this check also in check_expr(), but it seems more
    // "future-proof" to do it this way, as check_fn_body() is supposed to be
    // the common flow point for all functions that appear in the AST.

    with_appropriate_checker(cx, fn_id) { |checker|
        for @{def, span} in *freevars::get_freevars(cx.tcx, fn_id) {
            let id = ast_util::def_id_of_def(def).node;
            if checker == check_copy {
                let last_uses = alt check cx.last_uses.find(fn_id) {
                  some(last_use::closes_over(vars)) { vars }
                  none { [] }
                };
                if option::is_some(vec::position_elt(last_uses, id)) { cont; }
            }
            let ty = ty::node_id_to_type(cx.tcx, id);
            checker(cx, ty, span);
        }
    }

    visit::visit_fn(fk, decl, body, sp, fn_id, cx, v);
}

fn check_fn_cap_clause(cx: ctx,
                       id: node_id,
                       cap_clause: capture_clause) {
    // Check that the variables named in the clause which are not free vars
    // (if any) are also legal.  freevars are checked above in check_fn().
    // This is kind of a degenerate case, as captured variables will generally
    // appear free in the body.
    let freevars = freevars::get_freevars(cx.tcx, id);
    let freevar_ids = vec::map(*freevars, { |freevar|
        ast_util::def_id_of_def(freevar.def).node
    });
    //log("freevar_ids", freevar_ids);
    with_appropriate_checker(cx, id) { |checker|
        let check_var = fn@(&&cap_item: @capture_item) {
            let cap_def = cx.tcx.def_map.get(cap_item.id);
            let cap_def_id = ast_util::def_id_of_def(cap_def).node;
            if !vec::contains(freevar_ids, cap_def_id) {
                let ty = ty::node_id_to_type(cx.tcx, cap_def_id);
                checker(cx, ty, cap_item.span);
            }
        };
        vec::iter(cap_clause.copies, check_var);
        vec::iter(cap_clause.moves, check_var);
    }
}

fn check_block(b: blk, cx: ctx, v: visit::vt<ctx>) {
    alt b.node.expr {
      some(ex) { maybe_copy(cx, ex); }
      _ {}
    }
    visit::visit_block(b, cx, v);
}

fn check_expr(e: @expr, cx: ctx, v: visit::vt<ctx>) {
    alt e.node {
      expr_assign(_, ex) | expr_assign_op(_, _, ex) |
      expr_unary(box(_), ex) | expr_unary(uniq(_), ex) |
      expr_ret(some(ex)) { maybe_copy(cx, ex); }
      expr_copy(expr) { check_copy_ex(cx, expr, false); }
      // Vector add copies.
      expr_binary(add, ls, rs) { maybe_copy(cx, ls); maybe_copy(cx, rs); }
      expr_rec(fields, def) {
        for field in fields { maybe_copy(cx, field.node.expr); }
        alt def {
          some(ex) {
            // All noncopyable fields must be overridden
            let t = ty::expr_ty(cx.tcx, ex);
            let ty_fields = alt ty::get(t).struct {
              ty::ty_rec(f) { f }
              _ { cx.tcx.sess.span_bug(ex.span, "Bad expr type in record"); }
            };
            for tf in ty_fields {
                if !vec::any(fields, {|f| f.node.ident == tf.ident}) &&
                    !ty::kind_can_be_copied(ty::type_kind(cx.tcx, tf.mt.ty)) {
                    cx.tcx.sess.span_err(ex.span,
                                         "copying a noncopyable value");
                }
            }
          }
          _ {}
        }
      }
      expr_tup(exprs) | expr_vec(exprs, _) {
        for expr in exprs { maybe_copy(cx, expr); }
      }
      expr_bind(_, args) {
        for a in args { alt a { some(ex) { maybe_copy(cx, ex); } _ {} } }
      }
      expr_call(f, args, _) {
        let i = 0u;
        for arg_t in ty::ty_fn_args(ty::expr_ty(cx.tcx, f)) {
            alt ty::arg_mode(cx.tcx, arg_t) {
              by_copy { maybe_copy(cx, args[i]); }
              by_ref | by_val | by_mutbl_ref | by_move { }
            }
            i += 1u;
        }
      }
      expr_path(_) {
        alt cx.tcx.node_type_substs.find(e.id) {
          some(ts) {
            let did = ast_util::def_id_of_def(cx.tcx.def_map.get(e.id));
            let bounds = ty::lookup_item_type(cx.tcx, did).bounds;
            let i = 0u;
            for ty in ts {
                let kind = ty::type_kind(cx.tcx, ty);
                let p_kind = ty::param_bounds_to_kind(bounds[i]);
                if !ty::kind_lteq(p_kind, kind) {
                    cx.tcx.sess.span_err(e.span, "instantiating a " +
                                         kind_to_str(p_kind) +
                                         " type parameter with a "
                                         + kind_to_str(kind) + " type");
                }
                i += 1u;
            }
          }
          none {}
        }
      }
      expr_fn(_, _, _, cap_clause) {
        check_fn_cap_clause(cx, e.id, *cap_clause);
      }
      _ { }
    }
    visit::visit_expr(e, cx, v);
}

fn check_stmt(stmt: @stmt, cx: ctx, v: visit::vt<ctx>) {
    alt stmt.node {
      stmt_decl(@{node: decl_local(locals), _}, _) {
        for local in locals {
            alt local.node.init {
              some({op: init_assign, expr}) { maybe_copy(cx, expr); }
              _ {}
            }
        }
      }
      _ {}
    }
    visit::visit_stmt(stmt, cx, v);
}

fn maybe_copy(cx: ctx, ex: @expr) {
    check_copy_ex(cx, ex, true);
}

fn is_nullary_variant(cx: ctx, ex: @expr) -> bool {
    alt ex.node {
      expr_path(_) {
        alt cx.tcx.def_map.get(ex.id) {
          def_variant(edid, vdid) {
            vec::len(ty::enum_variant_with_id(cx.tcx, edid, vdid).args) == 0u
          }
          _ { false }
        }
      }
      _ { false }
    }
}

fn check_copy_ex(cx: ctx, ex: @expr, _warn: bool) {
    if ty::expr_is_lval(cx.method_map, ex) &&
       !cx.last_uses.contains_key(ex.id) &&
       !is_nullary_variant(cx, ex) {
        let ty = ty::expr_ty(cx.tcx, ex);
        check_copy(cx, ty, ex.span);
        // FIXME turn this on again once vector types are no longer unique.
        // Right now, it is too annoying to be useful.
        /* if warn && ty::type_is_unique(ty) {
            cx.tcx.sess.span_warn(ex.span, "copying a unique value");
        }*/
    }
}

fn check_copy(cx: ctx, ty: ty::t, sp: span) {
    if !ty::kind_can_be_copied(ty::type_kind(cx.tcx, ty)) {
        cx.tcx.sess.span_err(sp, "copying a noncopyable value");
    }
}

fn check_send(cx: ctx, ty: ty::t, sp: span) {
    if !ty::kind_can_be_sent(ty::type_kind(cx.tcx, ty)) {
        cx.tcx.sess.span_err(sp, "not a sendable value");
    }
}

fn check_none(cx: ctx, _ty: ty::t, sp: span) {
    cx.tcx.sess.span_err(sp, "attempted dynamic environment capture");
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//
<|endoftext|>


<commit_before>use num::{FromPrimitive};
use std::marker::PhantomData;

use log::{debug};
use goblin::{Object};
use failure::{Error};

use super::super::super::arch::Arch;
use super::super::super::workspace::Workspace;
use super::super::{Analyzer, AnalysisError};

pub struct ImportsAnalyzer<A: Arch> {
    // This Analyzer must have a type parameter for it
    //  to implement Analyzer<A>.
    // however, it doesn't actually use this type itself.
    // so, we use a phantom data marker which has zero type,
    //  to ensure there is not an unused type parameter,
    //  which is a compile error.
    _phantom: PhantomData<A>,
}

impl<A: Arch> ImportsAnalyzer<A> {
    pub fn new() -> ImportsAnalyzer<A> {
        ImportsAnalyzer {
            _phantom: PhantomData {},
        }
    }
}

impl<A: Arch + 'static> Analyzer<A> for ImportsAnalyzer<A> {
    fn get_name(&self) -> String {
        "PE imports analyzer".to_string()
    }

    /// ```
    /// use lancelot::rsrc::*;
    /// use lancelot::arch::*;
    /// use lancelot::analysis::Analyzer;
    /// use lancelot::workspace::Workspace;
    /// use lancelot::analysis::pe::ImportsAnalyzer;
    ///
    /// let mut ws = Workspace::<Arch64>::from_bytes("k32.dll", &get_buf(Rsrc::K32))
    ///    .disable_analysis()
    ///    .load().unwrap();
    /// let anal = ImportsAnalyzer::<Arch64>::new();
    /// anal.analyze(&mut ws).unwrap();
    /// // TODO: this currently fails.
    /// assert_eq!(ws.get_symbol(0x78160).unwrap(), "PrivCopyFileExW");
    /// ```
    fn analyze(&self, ws: &mut Workspace<A>) -> Result<(), Error> {
        let pe = match Object::parse(&ws.buf) {
            Ok(Object::PE(pe)) => pe,
            _ => panic!("can't analyze unexpected format"),
        };

        let entry = match A::RVA::from_usize(pe.entry) {
            Some(entry) => entry,
            None => return Err(AnalysisError::NotSupported.into()),
        };

        let symbols: Vec<(usize, String)> = pe.imports.iter()
            .map(|imp| {
                // TODO: what if import is by ordinal?

                /*
                 * TODO: compute FirstThunk address.
                 * its not clear what `imp.rva` or `imp.offset` point to.
                 * unfortunately, its not the FirstThunk entry.
                 * however, i'm not sure if this is a bug expected.
                 *
                 * see: https://github.com/m4b/goblin/issues/161

                println!("import: {}!{} offset: {:#x} rva: {:#x}",
                         imp.dll, &imp.name,
                         imp.offset, imp.rva,
                );
                let name = format!("{}!{}", imp.dll, &imp.name);
                */
                (imp.rva, name)
            })
            .collect();

        for (rva, name) in symbols.iter() {
            let rva = match A::RVA::from_usize(*rva) {
                Some(rva) => rva,
                // we panic here because some work is done, but not all.
                // alternatively, we could warn.
                // but i'd like to bail early and figure out why this can happen.
                None => panic!("failed to convert RVA")
            };

            /*
             * TODO: see note above
            debug!("import: {:#x}: {}", rva, name);
            ws.make_symbol(rva, name)?;
            ws.analyze()?;
            */
        }

        Ok(())
    }
}
<commit_msg>analysis pe imports: fix compilation issue<commit_after>use num::{FromPrimitive};
use std::marker::PhantomData;

use log::{debug};
use goblin::{Object};
use failure::{Error};

use super::super::super::arch::Arch;
use super::super::super::workspace::Workspace;
use super::super::{Analyzer, AnalysisError};

pub struct ImportsAnalyzer<A: Arch> {
    // This Analyzer must have a type parameter for it
    //  to implement Analyzer<A>.
    // however, it doesn't actually use this type itself.
    // so, we use a phantom data marker which has zero type,
    //  to ensure there is not an unused type parameter,
    //  which is a compile error.
    _phantom: PhantomData<A>,
}

impl<A: Arch> ImportsAnalyzer<A> {
    pub fn new() -> ImportsAnalyzer<A> {
        ImportsAnalyzer {
            _phantom: PhantomData {},
        }
    }
}

impl<A: Arch + 'static> Analyzer<A> for ImportsAnalyzer<A> {
    fn get_name(&self) -> String {
        "PE imports analyzer".to_string()
    }

    /// ```
    /// use lancelot::rsrc::*;
    /// use lancelot::arch::*;
    /// use lancelot::analysis::Analyzer;
    /// use lancelot::workspace::Workspace;
    /// use lancelot::analysis::pe::ImportsAnalyzer;
    ///
    /// let mut ws = Workspace::<Arch64>::from_bytes("k32.dll", &get_buf(Rsrc::K32))
    ///    .disable_analysis()
    ///    .load().unwrap();
    /// let anal = ImportsAnalyzer::<Arch64>::new();
    /// anal.analyze(&mut ws).unwrap();
    /// // TODO: this currently fails.
    /// assert_eq!(ws.get_symbol(0x78160).unwrap(), "PrivCopyFileExW");
    /// ```
    fn analyze(&self, ws: &mut Workspace<A>) -> Result<(), Error> {
        let pe = match Object::parse(&ws.buf) {
            Ok(Object::PE(pe)) => pe,
            _ => panic!("can't analyze unexpected format"),
        };

        let entry = match A::RVA::from_usize(pe.entry) {
            Some(entry) => entry,
            None => return Err(AnalysisError::NotSupported.into()),
        };

        let symbols: Vec<(usize, String)> = pe.imports.iter()
            .map(|imp| {
                // TODO: what if import is by ordinal?

                /*
                 * TODO: compute FirstThunk address.
                 * its not clear what `imp.rva` or `imp.offset` point to.
                 * unfortunately, its not the FirstThunk entry.
                 * however, i'm not sure if this is a bug expected.
                 *
                 * see: https://github.com/m4b/goblin/issues/161

                println!("import: {}!{} offset: {:#x} rva: {:#x}",
                         imp.dll, &imp.name,
                         imp.offset, imp.rva,
                );
                 */
                let name = format!("{}!{}", imp.dll, &imp.name);
                (imp.rva, name)
            })
            .collect();

        for (rva, name) in symbols.iter() {
            let rva = match A::RVA::from_usize(*rva) {
                Some(rva) => rva,
                // we panic here because some work is done, but not all.
                // alternatively, we could warn.
                // but i'd like to bail early and figure out why this can happen.
                None => panic!("failed to convert RVA")
            };

            /*
             * TODO: see note above
            debug!("import: {:#x}: {}", rva, name);
            ws.make_symbol(rva, name)?;
            ws.analyze()?;
            */
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>use crate::err::Result;
use crate::file::SaveDir;
use diesel::connection::SimpleConnection;
use diesel::deserialize::{self, FromSql};
use diesel::prelude::*;
use diesel::serialize::{self, Output, ToSql};
use diesel::sql_types::{Nullable, Text};
use smallvec::SmallVec;
use std::io::Write;
use std::ops::Deref;
use std::path::PathBuf;

pub mod schema {
    table! {
        series_configs {
            id -> Integer,
            nickname -> Text,
            path -> Text,
            episode_matcher -> Nullable<Text>,
            player_args -> Nullable<Text>,
        }
    }

    table! {
        series_info {
            id -> Integer,
            title_preferred -> Text,
            title_romaji -> Text,
            episodes -> SmallInt,
            episode_length_mins -> SmallInt,
            sequel -> Nullable<Integer>,
        }
    }

    table! {
        series_entries {
            id -> Integer,
            watched_episodes -> SmallInt,
            score -> Nullable<SmallInt>,
            status -> SmallInt,
            times_rewatched -> SmallInt,
            start_date -> Nullable<Date>,
            end_date -> Nullable<Date>,
            needs_sync -> Bool,
        }
    }
}

pub struct Database(SqliteConnection);

impl Database {
    pub fn open() -> Result<Self> {
        let path = Self::validated_path()?;
        let conn = SqliteConnection::establish(&path.to_string_lossy())?;

        conn.batch_execute(include_str!("../sql/schema.sql"))?;

        Ok(Self(conn))
    }

    pub fn validated_path() -> Result<PathBuf> {
        let mut path = SaveDir::LocalData.validated_dir_path()?.to_path_buf();
        path.push("data.sqlite");
        Ok(path)
    }

    #[inline(always)]
    pub fn conn(&self) -> &SqliteConnection {
        &self.0
    }
}

impl Drop for Database {
    fn drop(&mut self) {
        self.conn().execute("PRAGMA optimize").ok();
    }
}

#[derive(Debug, AsExpression, FromSqlRow)]
#[sql_type = "Text"]
pub struct Path(PathBuf);

impl<DB> FromSql<Text, DB> for Path
where
    DB: diesel::backend::Backend,
    String: FromSql<Text, DB>,
{
    fn from_sql(bytes: Option<&DB::RawValue>) -> deserialize::Result<Self> {
        let path = String::from_sql(bytes)?.into();
        Ok(Self(path))
    }
}

impl<DB> ToSql<Text, DB> for Path
where
    DB: diesel::backend::Backend,
    str: ToSql<Text, DB>,
{
    fn to_sql<W: Write>(&self, out: &mut Output<W, DB>) -> serialize::Result {
        let value = self.0.to_string_lossy();
        value.to_sql(out)
    }
}

impl From<PathBuf> for Path {
    fn from(value: PathBuf) -> Self {
        Self(value)
    }
}

impl Deref for Path {
    type Target = PathBuf;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

#[derive(Debug, Default, AsExpression, FromSqlRow)]
#[sql_type = "Text"]
pub struct PlayerArgs(SmallVec<[String; 3]>);

impl PlayerArgs {
    #[inline(always)]
    pub fn new() -> Self {
        Self(SmallVec::new())
    }
}

impl<DB> FromSql<Nullable<Text>, DB> for PlayerArgs
where
    DB: diesel::backend::Backend,
    String: FromSql<Text, DB>,
{
    fn from_sql(bytes: Option<&DB::RawValue>) -> deserialize::Result<Self> {
        match bytes {
            Some(_) => {
                let args = String::from_sql(bytes)?
                    .split(";;")
                    .map(Into::into)
                    .collect();

                Ok(Self(args))
            }
            None => Ok(Self::new()),
        }
    }
}

impl<DB> ToSql<Text, DB> for PlayerArgs
where
    DB: diesel::backend::Backend,
    String: ToSql<Text, DB>,
{
    fn to_sql<W: Write>(&self, out: &mut Output<W, DB>) -> serialize::Result {
        let value = self.0.join(";;");
        value.to_sql(out)
    }
}

impl AsRef<SmallVec<[String; 3]>> for PlayerArgs {
    fn as_ref(&self) -> &SmallVec<[String; 3]> {
        &self.0
    }
}

impl From<SmallVec<[String; 3]>> for PlayerArgs {
    fn from(value: SmallVec<[String; 3]>) -> Self {
        Self(value)
    }
}

impl Deref for PlayerArgs {
    type Target = SmallVec<[String; 3]>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
<commit_msg>anup: derive Clone for Path<commit_after>use crate::err::Result;
use crate::file::SaveDir;
use diesel::connection::SimpleConnection;
use diesel::deserialize::{self, FromSql};
use diesel::prelude::*;
use diesel::serialize::{self, Output, ToSql};
use diesel::sql_types::{Nullable, Text};
use smallvec::SmallVec;
use std::io::Write;
use std::ops::Deref;
use std::path::PathBuf;

pub mod schema {
    table! {
        series_configs {
            id -> Integer,
            nickname -> Text,
            path -> Text,
            episode_matcher -> Nullable<Text>,
            player_args -> Nullable<Text>,
        }
    }

    table! {
        series_info {
            id -> Integer,
            title_preferred -> Text,
            title_romaji -> Text,
            episodes -> SmallInt,
            episode_length_mins -> SmallInt,
            sequel -> Nullable<Integer>,
        }
    }

    table! {
        series_entries {
            id -> Integer,
            watched_episodes -> SmallInt,
            score -> Nullable<SmallInt>,
            status -> SmallInt,
            times_rewatched -> SmallInt,
            start_date -> Nullable<Date>,
            end_date -> Nullable<Date>,
            needs_sync -> Bool,
        }
    }
}

pub struct Database(SqliteConnection);

impl Database {
    pub fn open() -> Result<Self> {
        let path = Self::validated_path()?;
        let conn = SqliteConnection::establish(&path.to_string_lossy())?;

        conn.batch_execute(include_str!("../sql/schema.sql"))?;

        Ok(Self(conn))
    }

    pub fn validated_path() -> Result<PathBuf> {
        let mut path = SaveDir::LocalData.validated_dir_path()?.to_path_buf();
        path.push("data.sqlite");
        Ok(path)
    }

    #[inline(always)]
    pub fn conn(&self) -> &SqliteConnection {
        &self.0
    }
}

impl Drop for Database {
    fn drop(&mut self) {
        self.conn().execute("PRAGMA optimize").ok();
    }
}

#[derive(Clone, Debug, AsExpression, FromSqlRow)]
#[sql_type = "Text"]
pub struct Path(PathBuf);

impl<DB> FromSql<Text, DB> for Path
where
    DB: diesel::backend::Backend,
    String: FromSql<Text, DB>,
{
    fn from_sql(bytes: Option<&DB::RawValue>) -> deserialize::Result<Self> {
        let path = String::from_sql(bytes)?.into();
        Ok(Self(path))
    }
}

impl<DB> ToSql<Text, DB> for Path
where
    DB: diesel::backend::Backend,
    str: ToSql<Text, DB>,
{
    fn to_sql<W: Write>(&self, out: &mut Output<W, DB>) -> serialize::Result {
        let value = self.0.to_string_lossy();
        value.to_sql(out)
    }
}

impl From<PathBuf> for Path {
    fn from(value: PathBuf) -> Self {
        Self(value)
    }
}

impl Deref for Path {
    type Target = PathBuf;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

#[derive(Debug, Default, AsExpression, FromSqlRow)]
#[sql_type = "Text"]
pub struct PlayerArgs(SmallVec<[String; 3]>);

impl PlayerArgs {
    #[inline(always)]
    pub fn new() -> Self {
        Self(SmallVec::new())
    }
}

impl<DB> FromSql<Nullable<Text>, DB> for PlayerArgs
where
    DB: diesel::backend::Backend,
    String: FromSql<Text, DB>,
{
    fn from_sql(bytes: Option<&DB::RawValue>) -> deserialize::Result<Self> {
        match bytes {
            Some(_) => {
                let args = String::from_sql(bytes)?
                    .split(";;")
                    .map(Into::into)
                    .collect();

                Ok(Self(args))
            }
            None => Ok(Self::new()),
        }
    }
}

impl<DB> ToSql<Text, DB> for PlayerArgs
where
    DB: diesel::backend::Backend,
    String: ToSql<Text, DB>,
{
    fn to_sql<W: Write>(&self, out: &mut Output<W, DB>) -> serialize::Result {
        let value = self.0.join(";;");
        value.to_sql(out)
    }
}

impl AsRef<SmallVec<[String; 3]>> for PlayerArgs {
    fn as_ref(&self) -> &SmallVec<[String; 3]> {
        &self.0
    }
}

impl From<SmallVec<[String; 3]>> for PlayerArgs {
    fn from(value: SmallVec<[String; 3]>) -> Self {
        Self(value)
    }
}

impl Deref for PlayerArgs {
    type Target = SmallVec<[String; 3]>;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
<|endoftext|>


<commit_before>use std::mem;

use edit_types::{BufferEvent, EventDomain};

pub(crate) struct Recorder {
    is_recording: bool,
    recording: Recording,
}

impl Recorder {
    pub(crate) fn new() -> Recorder {
        Recorder {
            is_recording: false,
            recording: Recording {
                events: Vec::new()
            },
        }
    }

    pub(crate) fn is_recording(&self) -> bool {
        self.is_recording
    }

    pub(crate) fn toggle_recording(&mut self) {
        if !self.is_recording && !self.recording.events.is_empty() {
            self.recording.clear();
        } else if self.is_recording {
            self.recording.filter_undos();
        }

        self.is_recording = !self.is_recording;
    }

    pub(crate) fn record(&mut self, cmd: EventDomain) {
        if !self.is_recording {
            // We're not supposed to be recording? Can we log somehow?
            return;
        }

        self.recording.events.push(cmd);
    }

    pub(crate) fn play<F>(&self, action: F)
        where F: FnMut(&EventDomain) -> () {
        self.recording.play(action);
    }

    pub(crate) fn clear(&mut self) {
        self.recording.clear();
    }
}

struct Recording {
    events: Vec<EventDomain>
}

impl Recording {
    pub(crate) fn clear(&mut self) {
        self.events.clear();
    }

    pub(crate) fn play<F>(&self, action: F)
        where F: FnMut(&EventDomain) -> () {
        self.events.iter().for_each(action)
    }

    pub(crate) fn filter_undos(&mut self) {
        let mut saw_undo = false;
        let mut saw_redo = false;

        // Walk the recording backwards and remove any undo / redo events
        let filtered: Vec<EventDomain> = self.events.clone()
            .into_iter()
            .rev()
            .filter(|event| {
                if let EventDomain::Buffer(event) = event {
                    return match event {
                        BufferEvent::Undo => {
                            saw_undo = !saw_redo;
                            saw_redo = false;
                            false
                        }
                        BufferEvent::Redo => {
                            saw_redo = !saw_undo;
                            saw_undo = false;
                            false
                        }
                        _ => {
                            let ret = !saw_undo;
                            saw_undo = false;
                            saw_redo = false;
                            ret
                        }
                    };
                }

                true
            })
            .collect::<Vec<EventDomain>>()
            .into_iter() // Why does rev().filter().rev().collect() cancel out the first rev()????
            .rev()
            .collect();

        mem::replace(&mut self.events, filtered);
    }
}

#[cfg(test)]
mod tests {
    use recorder::Recorder;
    use edit_types::BufferEvent;

    #[test]
    fn undo_filtering_tests() {
        let mut recorder = Recorder::new();

        // Tests for filtering undo / redo from the recording buffer
        // A = Event
        // B = Event
        // U = Undo
        // R = Redo

        // Undo removes last item, redo only affects undo
        // A U B R => B
        recorder.toggle_recording();
        recorder.record(BufferEvent::Transpose.into());
        recorder.record(BufferEvent::Undo.into());
        recorder.record(BufferEvent::DuplicateLine.into());
        recorder.record(BufferEvent::Redo.into());
        recorder.toggle_recording();
        assert_eq!(recorder.recording.events, vec![BufferEvent::DuplicateLine.into()]);

        recorder.clear();

        // Swapping order of undo and redo should give us a different leftover item
        // A R B U => A
        recorder.toggle_recording();
        recorder.record(BufferEvent::Transpose.into());
        recorder.record(BufferEvent::Redo.into());
        recorder.record(BufferEvent::DuplicateLine.into());
        recorder.record(BufferEvent::Undo.into());
        recorder.toggle_recording();
        assert_eq!(recorder.recording.events, vec![BufferEvent::Transpose.into()]);

        recorder.clear();

        // Redo cancels out an undo
        // A U R B => A B
        recorder.toggle_recording();
        recorder.record(BufferEvent::Transpose.into());
        recorder.record(BufferEvent::Undo.into());
        recorder.record(BufferEvent::Redo.into());
        recorder.record(BufferEvent::DuplicateLine.into());
        recorder.toggle_recording();
        assert_eq!(recorder.recording.events, vec![BufferEvent::Transpose.into(), BufferEvent::DuplicateLine.into()]);

        recorder.clear();

        // Undo should cancel a redo, preventing it from canceling another undo
        // A U R U => _
        recorder.toggle_recording();
        recorder.record(BufferEvent::Transpose.into());
        recorder.record(BufferEvent::Undo.into());
        recorder.record(BufferEvent::Redo.into());
        recorder.record(BufferEvent::Undo.into());
        recorder.toggle_recording();
        assert_eq!(recorder.recording.events, vec![]);
    }
}<commit_msg>Split Recording Tests<commit_after>use std::mem;

use edit_types::{BufferEvent, EventDomain};

pub(crate) struct Recorder {
    is_recording: bool,
    recording: Recording,
}

impl Recorder {
    pub(crate) fn new() -> Recorder {
        Recorder {
            is_recording: false,
            recording: Recording {
                events: Vec::new()
            },
        }
    }

    pub(crate) fn is_recording(&self) -> bool {
        self.is_recording
    }

    pub(crate) fn toggle_recording(&mut self) {
        if !self.is_recording && !self.recording.events.is_empty() {
            self.recording.clear();
        } else if self.is_recording {
            self.recording.filter_undos();
        }

        self.is_recording = !self.is_recording;
    }

    pub(crate) fn record(&mut self, cmd: EventDomain) {
        if !self.is_recording {
            // We're not supposed to be recording? Can we log somehow?
            return;
        }

        self.recording.events.push(cmd);
    }

    pub(crate) fn play<F>(&self, action: F)
        where F: FnMut(&EventDomain) -> () {
        self.recording.play(action);
    }

    pub(crate) fn clear(&mut self) {
        self.recording.clear();
    }
}

struct Recording {
    events: Vec<EventDomain>
}

impl Recording {
    pub(crate) fn clear(&mut self) {
        self.events.clear();
    }

    pub(crate) fn play<F>(&self, action: F)
        where F: FnMut(&EventDomain) -> () {
        self.events.iter().for_each(action)
    }

    pub(crate) fn filter_undos(&mut self) {
        let mut saw_undo = false;
        let mut saw_redo = false;

        // Walk the recording backwards and remove any undo / redo events
        let filtered: Vec<EventDomain> = self.events.clone()
            .into_iter()
            .rev()
            .filter(|event| {
                if let EventDomain::Buffer(event) = event {
                    return match event {
                        BufferEvent::Undo => {
                            saw_undo = !saw_redo;
                            saw_redo = false;
                            false
                        }
                        BufferEvent::Redo => {
                            saw_redo = !saw_undo;
                            saw_undo = false;
                            false
                        }
                        _ => {
                            let ret = !saw_undo;
                            saw_undo = false;
                            saw_redo = false;
                            ret
                        }
                    };
                }

                true
            })
            .collect::<Vec<EventDomain>>()
            .into_iter() // Why does rev().filter().rev().collect() cancel out the first rev()????
            .rev()
            .collect();

        mem::replace(&mut self.events, filtered);
    }
}

// Tests for filtering undo / redo from the recording buffer
// A = Event
// B = Event
// U = Undo
// R = Redo
#[cfg(test)]
mod tests {
    use recorder::Recorder;
    use edit_types::{BufferEvent, EventDomain};

    #[test]
    fn play_recording() {
        let mut recorder = Recorder::new();

        let mut expected_events: Vec<EventDomain> = vec![
            BufferEvent::Indent.into(),
            BufferEvent::Outdent.into(),
            BufferEvent::DuplicateLine.into(),
            BufferEvent::Transpose.into(),
        ];

        recorder.toggle_recording();
        for event in expected_events.iter().rev() {
            recorder.record(event.clone());
        }
        recorder.toggle_recording();

        recorder.play(|event| {
            // We shouldn't iterate more times than we added items!
            let expected_event = expected_events.pop();
            assert!(expected_event.is_some());

            // Should be the event we expect
            assert_eq!(*event, expected_event.unwrap());
        });

        // We should have iterated over everything we inserted
        assert_eq!(expected_events.len(), 0);
    }

    #[test]
    fn clear_recording() {
        let mut recorder = Recorder::new();

        recorder.toggle_recording();
        recorder.record(BufferEvent::Transpose.into());
        recorder.record(BufferEvent::DuplicateLine.into());
        recorder.record(BufferEvent::Outdent.into());
        recorder.record(BufferEvent::Indent.into());
        recorder.toggle_recording();

        assert_eq!(recorder.recording.events.len(), 4);

        recorder.clear();

        assert_eq!(recorder.recording.events.len(), 0);
    }

    #[test]
    fn basic_test() {
        let mut recorder = Recorder::new();

        // Undo removes last item, redo only affects undo
        // A U B R => B
        recorder.toggle_recording();
        recorder.record(BufferEvent::Transpose.into());
        recorder.record(BufferEvent::Undo.into());
        recorder.record(BufferEvent::DuplicateLine.into());
        recorder.record(BufferEvent::Redo.into());
        recorder.toggle_recording();
        assert_eq!(recorder.recording.events, vec![BufferEvent::DuplicateLine.into()]);
    }

    #[test]
    fn basic_test_swapped() {
        let mut recorder = Recorder::new();

        // Swapping order of undo and redo from the basic test should give us a different leftover item
        // A R B U => A
        recorder.toggle_recording();
        recorder.record(BufferEvent::Transpose.into());
        recorder.record(BufferEvent::Redo.into());
        recorder.record(BufferEvent::DuplicateLine.into());
        recorder.record(BufferEvent::Undo.into());
        recorder.toggle_recording();
        assert_eq!(recorder.recording.events, vec![BufferEvent::Transpose.into()]);
    }

    #[test]
    fn redo_cancels_undo() {
        let mut recorder = Recorder::new();

        // Redo cancels out an undo
        // A U R B => A B
        recorder.toggle_recording();
        recorder.record(BufferEvent::Transpose.into());
        recorder.record(BufferEvent::Undo.into());
        recorder.record(BufferEvent::Redo.into());
        recorder.record(BufferEvent::DuplicateLine.into());
        recorder.toggle_recording();
        assert_eq!(recorder.recording.events, vec![BufferEvent::Transpose.into(), BufferEvent::DuplicateLine.into()]);
    }

    #[test]
    fn undo_cancels_redo() {
        let mut recorder = Recorder::new();

        // Undo should cancel a redo, preventing it from canceling another undo
        // A U R U => _
        recorder.toggle_recording();
        recorder.record(BufferEvent::Transpose.into());
        recorder.record(BufferEvent::Undo.into());
        recorder.record(BufferEvent::Redo.into());
        recorder.record(BufferEvent::Undo.into());
        recorder.toggle_recording();
        assert_eq!(recorder.recording.events, vec![]);
    }

    #[test]
    fn undo_as_first_item() {
        let mut recorder = Recorder::new();

        // Undo shouldn't do anything as the first item
        // U A B R => A B
        recorder.toggle_recording();
        recorder.record(BufferEvent::Undo.into());
        recorder.record(BufferEvent::Transpose.into());
        recorder.record(BufferEvent::DuplicateLine.into());
        recorder.record(BufferEvent::Redo.into());
        recorder.toggle_recording();
        assert_eq!(recorder.recording.events, vec![BufferEvent::Transpose.into(), BufferEvent::DuplicateLine.into()]);
    }

    #[test]
    fn redo_as_first_item() {
        let mut recorder = Recorder::new();

        // Redo shouldn't do anything as the first item
        // R A B U => A
        recorder.toggle_recording();
        recorder.record(BufferEvent::Redo.into());
        recorder.record(BufferEvent::Transpose.into());
        recorder.record(BufferEvent::DuplicateLine.into());
        recorder.record(BufferEvent::Undo.into());
        recorder.toggle_recording();
        assert_eq!(recorder.recording.events, vec![BufferEvent::Transpose.into()]);
    }
}<|endoftext|>


<commit_before>use super::context::*;
use super::module::*;
use super::signal::*;

use std::cell::RefCell;
use std::collections::BTreeMap;
use std::ptr;

/// Represents an instance of a [`Module`], created by the [`Module`]::[`instance`] method.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// // Inner module (simple pass-through)
/// let inner = c.module("inner");
/// inner.output("o", inner.input("i", 32));
///
/// // Outer module (wraps a single `inner` instance)
/// let outer = c.module("outer");
/// let inner_inst = outer.instance("inner", "inner_inst");
/// inner_inst.drive_input("i", outer.input("i", 32));
/// outer.output("o", inner_inst.output("o"));
/// ```
///
/// [`Module`]: ./struct.Module.html
/// [`instance`]: ./struct.Module.html#method.instance
#[must_use]
pub struct Instance<'a> {
    pub(super) context: &'a Context<'a>,
    pub(super) module: &'a Module<'a>,

    pub(crate) instantiated_module: &'a Module<'a>,
    pub(crate) name: String,
    pub(crate) driven_inputs: RefCell<BTreeMap<String, &'a Signal<'a>>>,
}

impl<'a> Instance<'a> {
    /// Drives the input of this `Module` `Instance` specified by `name` with the given `Signal`.
    ///
    /// # Panics
    ///
    /// Panics if `i` is from a different module than `self`, if `name` specifies an input that doesn't exist on this `Instance`'s `Module`, if this input is already driven on this `Instance`, or if `i`'s bit width differs from that of the input.
    ///
    /// # Examples
    ///
    /// ```
    /// use kaze::*;
    ///
    /// let c = Context::new();
    ///
    /// let inner = c.module("inner");
    /// inner.output("o", inner.input("i", 32));
    ///
    /// let outer = c.module("outer");
    /// let inner_inst = outer.instance("inner", "inner_inst");
    /// // Drive inner_inst's "i" input with a 32-bit literal
    /// inner_inst.drive_input("i", outer.lit(0xfadebabeu32, 32));
    /// ```
    pub fn drive_input<S: Into<String>>(&'a self, name: S, i: &'a Signal<'a>) {
        let name = name.into();
        let mut driven_inputs = self.driven_inputs.borrow_mut();
        if !ptr::eq(self.module, i.module) {
            panic!("Attempted to drive an instance input with a signal from a different module.");
        }
        if !self.instantiated_module.inputs.borrow().contains_key(&name) {
            panic!("Attempted to drive an input called \"{}\" on an instance of \"{}\", but no such input with this name exists on this module.", name, self.instantiated_module.name);
        }
        if driven_inputs.contains_key(&name) {
            panic!("Attempted to drive an input called \"{}\" on an instance of \"{}\", but this input is already driven for this instance.", name, self.instantiated_module.name);
        }
        let input_bit_width = self.instantiated_module.inputs.borrow()[&name].bit_width();
        if input_bit_width != i.bit_width() {
            panic!("Attempted to drive an input called \"{}\" on an instance of \"{}\", but this input and the provided signal have different bit widths ({} and {}, respectively).", name, self.instantiated_module.name, input_bit_width, i.bit_width());
        }
        driven_inputs.insert(name, i);
    }

    /// Creates a [`Signal`] that represents this `Instance`'s output called `name`.
    ///
    /// # Panics
    ///
    /// Panics if `name` specifies an output that doesn't exist on this `Instance`'s `Module`.
    ///
    /// # Examples
    ///
    /// ```
    /// use kaze::*;
    ///
    /// let c = Context::new();
    ///
    /// let inner = c.module("inner");
    /// inner.output("o", inner.lit(true, 1));
    ///
    /// let outer = c.module("outer");
    /// let inner_inst = outer.instance("inner", "inner_inst");
    /// // Forward inner_inst's "o" output to a new output on outer with the same name
    /// outer.output("o", inner_inst.output("o"));
    /// ```
    ///
    /// [`Signal`]: ./struct.Signal.html
    pub fn output<S: Into<String>>(&'a self, name: S) -> &Signal<'a> {
        let name = name.into();
        if !self
            .instantiated_module
            .outputs
            .borrow()
            .contains_key(&name)
        {
            panic!("Attempted to create a signal for an output called \"{}\" on an instance of \"{}\", but no such output with this name exists on this module.", name, self.instantiated_module.name);
        }
        self.context.signal_arena.alloc(Signal {
            context: self.context,
            module: self.module,

            data: SignalData::InstanceOutput {
                instance: self,
                name,
            },
        })
    }
}

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

    #[test]
    #[should_panic(
        expected = "Attempted to drive an instance input with a signal from a different module."
    )]
    fn drive_input_different_module_error() {
        let c = Context::new();

        let inner = c.module("inner");
        let _ = inner.input("a", 1);

        let m1 = c.module("a");
        let i1 = m1.input("a", 1);

        let m2 = c.module("b");
        let inner_inst = m2.instance("inner", "inner_inst");

        // Panic
        inner_inst.drive_input("a", i1);
    }

    #[test]
    #[should_panic(
        expected = "Attempted to drive an input called \"a\" on an instance of \"inner\", but no such input with this name exists on this module."
    )]
    fn drive_input_nonexistent_input_error() {
        let c = Context::new();

        let _ = c.module("inner");

        let m = c.module("a");
        let inner_inst = m.instance("inner", "inner_inst");

        // Panic
        inner_inst.drive_input("a", m.input("i", 1));
    }

    #[test]
    #[should_panic(
        expected = "Attempted to drive an input called \"a\" on an instance of \"inner\", but this input is already driven for this instance."
    )]
    fn drive_input_already_driven_error() {
        let c = Context::new();

        let inner = c.module("inner");
        let _ = inner.input("a", 1);

        let m = c.module("a");
        let inner_inst = m.instance("inner", "inner_inst");

        inner_inst.drive_input("a", m.input("i1", 1));

        // Panic
        inner_inst.drive_input("a", m.input("i2", 1));
    }

    #[test]
    #[should_panic(
        expected = "Attempted to drive an input called \"a\" on an instance of \"inner\", but this input and the provided signal have different bit widths (1 and 32, respectively)."
    )]
    fn drive_input_incompatible_bit_widths_error() {
        let c = Context::new();

        let inner = c.module("inner");
        let _ = inner.input("a", 1);

        let m = c.module("a");
        let inner_inst = m.instance("inner", "inner_inst");

        // Panic
        inner_inst.drive_input("a", m.input("i1", 32));
    }

    #[test]
    #[should_panic(
        expected = "Attempted to create a signal for an output called \"nope\" on an instance of \"inner\", but no such output with this name exists on this module."
    )]
    fn output_nonexistent_output_error() {
        let c = Context::new();

        let _ = c.module("inner");

        let m = c.module("a");
        let inner_inst = m.instance("inner", "inner_inst");

        // Panic
        let _ = inner_inst.output("nope");
    }
}
<commit_msg>Fix up links in Instance docs<commit_after>use super::context::*;
use super::module::*;
use super::signal::*;

use std::cell::RefCell;
use std::collections::BTreeMap;
use std::ptr;

/// Represents an instance of a [`Module`], created by the [`Module`]::[`instance`] method.
///
/// # Examples
///
/// ```
/// use kaze::*;
///
/// let c = Context::new();
///
/// // Inner module (simple pass-through)
/// let inner = c.module("inner");
/// inner.output("o", inner.input("i", 32));
///
/// // Outer module (wraps a single `inner` instance)
/// let outer = c.module("outer");
/// let inner_inst = outer.instance("inner", "inner_inst");
/// inner_inst.drive_input("i", outer.input("i", 32));
/// outer.output("o", inner_inst.output("o"));
/// ```
///
/// [`instance`]: ./struct.Module.html#method.instance
/// [`Module`]: ./struct.Module.html
#[must_use]
pub struct Instance<'a> {
    pub(super) context: &'a Context<'a>,
    pub(super) module: &'a Module<'a>,

    pub(crate) instantiated_module: &'a Module<'a>,
    pub(crate) name: String,
    pub(crate) driven_inputs: RefCell<BTreeMap<String, &'a Signal<'a>>>,
}

impl<'a> Instance<'a> {
    /// Drives the input of this [`Module`] `Instance` specified by `name` with the given [`Signal`].
    ///
    /// # Panics
    ///
    /// Panics if `i` is from a different [`Module`] than `self`, if `name` specifies an input that doesn't exist on this `Instance`'s [`Module`], if this input is already driven on this `Instance`, or if `i`'s bit width differs from that of the input.
    ///
    /// # Examples
    ///
    /// ```
    /// use kaze::*;
    ///
    /// let c = Context::new();
    ///
    /// let inner = c.module("inner");
    /// inner.output("o", inner.input("i", 32));
    ///
    /// let outer = c.module("outer");
    /// let inner_inst = outer.instance("inner", "inner_inst");
    /// // Drive inner_inst's "i" input with a 32-bit literal
    /// inner_inst.drive_input("i", outer.lit(0xfadebabeu32, 32));
    /// ```
    ///
    /// [`Module`]: ./struct.Module.html
    /// [`Signal`]: ./struct.Signal.html
    pub fn drive_input<S: Into<String>>(&'a self, name: S, i: &'a Signal<'a>) {
        let name = name.into();
        let mut driven_inputs = self.driven_inputs.borrow_mut();
        if !ptr::eq(self.module, i.module) {
            panic!("Attempted to drive an instance input with a signal from a different module.");
        }
        if !self.instantiated_module.inputs.borrow().contains_key(&name) {
            panic!("Attempted to drive an input called \"{}\" on an instance of \"{}\", but no such input with this name exists on this module.", name, self.instantiated_module.name);
        }
        if driven_inputs.contains_key(&name) {
            panic!("Attempted to drive an input called \"{}\" on an instance of \"{}\", but this input is already driven for this instance.", name, self.instantiated_module.name);
        }
        let input_bit_width = self.instantiated_module.inputs.borrow()[&name].bit_width();
        if input_bit_width != i.bit_width() {
            panic!("Attempted to drive an input called \"{}\" on an instance of \"{}\", but this input and the provided signal have different bit widths ({} and {}, respectively).", name, self.instantiated_module.name, input_bit_width, i.bit_width());
        }
        driven_inputs.insert(name, i);
    }

    /// Creates a [`Signal`] that represents this `Instance`'s output called `name`.
    ///
    /// # Panics
    ///
    /// Panics if `name` specifies an output that doesn't exist on this `Instance`'s [`Module`].
    ///
    /// # Examples
    ///
    /// ```
    /// use kaze::*;
    ///
    /// let c = Context::new();
    ///
    /// let inner = c.module("inner");
    /// inner.output("o", inner.lit(true, 1));
    ///
    /// let outer = c.module("outer");
    /// let inner_inst = outer.instance("inner", "inner_inst");
    /// // Forward inner_inst's "o" output to a new output on outer with the same name
    /// outer.output("o", inner_inst.output("o"));
    /// ```
    ///
    /// [`Module`]: ./struct.Module.html
    /// [`Signal`]: ./struct.Signal.html
    pub fn output<S: Into<String>>(&'a self, name: S) -> &Signal<'a> {
        let name = name.into();
        if !self
            .instantiated_module
            .outputs
            .borrow()
            .contains_key(&name)
        {
            panic!("Attempted to create a signal for an output called \"{}\" on an instance of \"{}\", but no such output with this name exists on this module.", name, self.instantiated_module.name);
        }
        self.context.signal_arena.alloc(Signal {
            context: self.context,
            module: self.module,

            data: SignalData::InstanceOutput {
                instance: self,
                name,
            },
        })
    }
}

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

    #[test]
    #[should_panic(
        expected = "Attempted to drive an instance input with a signal from a different module."
    )]
    fn drive_input_different_module_error() {
        let c = Context::new();

        let inner = c.module("inner");
        let _ = inner.input("a", 1);

        let m1 = c.module("a");
        let i1 = m1.input("a", 1);

        let m2 = c.module("b");
        let inner_inst = m2.instance("inner", "inner_inst");

        // Panic
        inner_inst.drive_input("a", i1);
    }

    #[test]
    #[should_panic(
        expected = "Attempted to drive an input called \"a\" on an instance of \"inner\", but no such input with this name exists on this module."
    )]
    fn drive_input_nonexistent_input_error() {
        let c = Context::new();

        let _ = c.module("inner");

        let m = c.module("a");
        let inner_inst = m.instance("inner", "inner_inst");

        // Panic
        inner_inst.drive_input("a", m.input("i", 1));
    }

    #[test]
    #[should_panic(
        expected = "Attempted to drive an input called \"a\" on an instance of \"inner\", but this input is already driven for this instance."
    )]
    fn drive_input_already_driven_error() {
        let c = Context::new();

        let inner = c.module("inner");
        let _ = inner.input("a", 1);

        let m = c.module("a");
        let inner_inst = m.instance("inner", "inner_inst");

        inner_inst.drive_input("a", m.input("i1", 1));

        // Panic
        inner_inst.drive_input("a", m.input("i2", 1));
    }

    #[test]
    #[should_panic(
        expected = "Attempted to drive an input called \"a\" on an instance of \"inner\", but this input and the provided signal have different bit widths (1 and 32, respectively)."
    )]
    fn drive_input_incompatible_bit_widths_error() {
        let c = Context::new();

        let inner = c.module("inner");
        let _ = inner.input("a", 1);

        let m = c.module("a");
        let inner_inst = m.instance("inner", "inner_inst");

        // Panic
        inner_inst.drive_input("a", m.input("i1", 32));
    }

    #[test]
    #[should_panic(
        expected = "Attempted to create a signal for an output called \"nope\" on an instance of \"inner\", but no such output with this name exists on this module."
    )]
    fn output_nonexistent_output_error() {
        let c = Context::new();

        let _ = c.module("inner");

        let m = c.module("a");
        let inner_inst = m.instance("inner", "inner_inst");

        // Panic
        let _ = inner_inst.output("nope");
    }
}
<|endoftext|>


<commit_before>use nom::{self, IResult};

use std::str;

/// list-wildcards = "%" / "*"
pub fn list_wildcards(c: u8) -> bool {
    c == b'%' || c == b'*'
}

/// quoted-specials = DQUOTE / "\"
pub fn quoted_specials(c: u8) -> bool {
    c == b'"' || c == b'\\'
}

/// resp-specials = "]"
pub fn resp_specials(c: u8) -> bool {
    c == b']'
}

/// atom-specials = "(" / ")" / "{" / SP / CTL / list-wildcards / quoted-specials / resp-specials
pub fn atom_specials(c: u8) -> bool {
    c == b'('
        || c == b')'
        || c == b'{'
        || c == b' '
        || c < 32
        || list_wildcards(c)
        || quoted_specials(c)
        || resp_specials(c)
}

/// ATOM-CHAR = <any CHAR except atom-specials>
pub fn atom_char(c: u8) -> bool {
    !atom_specials(c)
}

/// nil = "NIL"
named!(pub nil, tag_s!("NIL"));

/// ASTRING-CHAR = ATOM-CHAR / resp-specials
pub fn astring_char(c: u8) -> bool {
    atom_char(c) || resp_specials(c)
}

/// QUOTED-CHAR = <any TEXT-CHAR except quoted-specials> / "\" quoted-specials
pub fn quoted_data(i: &[u8]) -> IResult<&[u8], &[u8]> {
    // Ideally this should use nom's `escaped` macro, but it suffers from broken
    // type inference unless compiled with the verbose-errors feature enabled.
    let mut escape = false;
    let mut len = 0;
    for c in i {
        if *c == b'"' && !escape {
            break;
        }
        len += 1;
        if *c == b'\\' && !escape {
            escape = true
        } else if escape {
            escape = false;
        }
    }
    Ok((&i[len..], &i[..len]))
}

/// quoted = DQUOTE *QUOTED-CHAR DQUOTE
named!(pub quoted<&[u8]>, do_parse!(
    tag_s!("\"") >>
    data: quoted_data >>
    tag_s!("\"") >>
    (data)
));

/// quoted bytes as as utf8
named!(pub quoted_utf8<&str>, map_res!(quoted, str::from_utf8));

/// literal = "{" number "}" CRLF *CHAR8
///            ; Number represents the number of CHAR8s
named!(pub literal<&[u8]>, do_parse!(
    tag_s!("{") >>
    len: number >>
    tag_s!("}") >>
    tag_s!("\r\n") >>
    data: take!(len) >>
    (data)
));

/// string = quoted / literal
named!(pub string<&[u8]>, alt!(quoted | literal));

/// string bytes as as utf8
named!(pub string_utf8<&str>, map_res!(string, str::from_utf8));

/// nstring = string / nil
named!(pub nstring<Option<&[u8]>>, alt!(
    map!(nil, |_| None) |
    map!(string, |s| Some(s))
));

/// nstring bytes as utf8
named!(pub nstring_utf8<Option<&str>>, alt!(
    map!(nil, |_| None) |
    map!(string_utf8, |s| Some(s))
));

/// number          = 1*DIGIT
///                    ; Unsigned 32-bit integer
///                    ; (0 <= n < 4,294,967,296)
named!(pub number<u32>, map_res!(
    map_res!(nom::digit, str::from_utf8),
    str::parse
));

/// same as `number` but 64-bit
named!(pub number_64<u64>, map_res!(
    map_res!(nom::digit, str::from_utf8),
    str::parse
));

/// atom = 1*ATOM-CHAR
named!(pub atom<&str>, map_res!(take_while1_s!(atom_char),
    str::from_utf8
));

/// astring = 1*ASTRING-CHAR / string
named!(pub astring<&[u8]>, alt!(
    take_while1!(astring_char) |
    string
));

/// astring bytes as as utf8
named!(pub astring_utf8<&str>, map_res!(astring, str::from_utf8));

/// text = 1*TEXT-CHAR
named!(pub text<&str>, map_res!(take_while_s!(text_char),
    str::from_utf8
));

/// TEXT-CHAR = <any CHAR except CR and LF>
pub fn text_char(c: u8) -> bool {
    c != b'\r' && c != b'\n'
}

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

    #[test]
    fn test_string_literal() {
        match string(b"{3}\r\nXYZ") {
            Ok((_, value)) => {
                assert_eq!(value, "XYZ".as_bytes());
            }
            rsp => panic!("unexpected response {:?}", rsp),
        }
    }

    #[test]
    fn test_astring() {
        match astring(b"text ") {
            Ok((_, value)) => {
                assert_eq!(value, "text".as_bytes());
            }
            rsp => panic!("unexpected response {:?}", rsp),
        }
    }
}
<commit_msg>Simplify number and number_64<commit_after>use nom::{self, IResult};

use std::str;

/// list-wildcards = "%" / "*"
pub fn list_wildcards(c: u8) -> bool {
    c == b'%' || c == b'*'
}

/// quoted-specials = DQUOTE / "\"
pub fn quoted_specials(c: u8) -> bool {
    c == b'"' || c == b'\\'
}

/// resp-specials = "]"
pub fn resp_specials(c: u8) -> bool {
    c == b']'
}

/// atom-specials = "(" / ")" / "{" / SP / CTL / list-wildcards / quoted-specials / resp-specials
pub fn atom_specials(c: u8) -> bool {
    c == b'('
        || c == b')'
        || c == b'{'
        || c == b' '
        || c < 32
        || list_wildcards(c)
        || quoted_specials(c)
        || resp_specials(c)
}

/// ATOM-CHAR = <any CHAR except atom-specials>
pub fn atom_char(c: u8) -> bool {
    !atom_specials(c)
}

/// nil = "NIL"
named!(pub nil, tag_s!("NIL"));

/// ASTRING-CHAR = ATOM-CHAR / resp-specials
pub fn astring_char(c: u8) -> bool {
    atom_char(c) || resp_specials(c)
}

/// QUOTED-CHAR = <any TEXT-CHAR except quoted-specials> / "\" quoted-specials
pub fn quoted_data(i: &[u8]) -> IResult<&[u8], &[u8]> {
    // Ideally this should use nom's `escaped` macro, but it suffers from broken
    // type inference unless compiled with the verbose-errors feature enabled.
    let mut escape = false;
    let mut len = 0;
    for c in i {
        if *c == b'"' && !escape {
            break;
        }
        len += 1;
        if *c == b'\\' && !escape {
            escape = true
        } else if escape {
            escape = false;
        }
    }
    Ok((&i[len..], &i[..len]))
}

/// quoted = DQUOTE *QUOTED-CHAR DQUOTE
named!(pub quoted<&[u8]>, do_parse!(
    tag_s!("\"") >>
    data: quoted_data >>
    tag_s!("\"") >>
    (data)
));

/// quoted bytes as as utf8
named!(pub quoted_utf8<&str>, map_res!(quoted, str::from_utf8));

/// literal = "{" number "}" CRLF *CHAR8
///            ; Number represents the number of CHAR8s
named!(pub literal<&[u8]>, do_parse!(
    tag_s!("{") >>
    len: number >>
    tag_s!("}") >>
    tag_s!("\r\n") >>
    data: take!(len) >>
    (data)
));

/// string = quoted / literal
named!(pub string<&[u8]>, alt!(quoted | literal));

/// string bytes as as utf8
named!(pub string_utf8<&str>, map_res!(string, str::from_utf8));

/// nstring = string / nil
named!(pub nstring<Option<&[u8]>>, alt!(
    map!(nil, |_| None) |
    map!(string, |s| Some(s))
));

/// nstring bytes as utf8
named!(pub nstring_utf8<Option<&str>>, alt!(
    map!(nil, |_| None) |
    map!(string_utf8, |s| Some(s))
));

/// number          = 1*DIGIT
///                    ; Unsigned 32-bit integer
///                    ; (0 <= n < 4,294,967,296)
named!(pub number<u32>, flat_map!(nom::digit, parse_to!(u32)));

/// same as `number` but 64-bit
named!(pub number_64<u64>, flat_map!(nom::digit, parse_to!(u64)));

/// atom = 1*ATOM-CHAR
named!(pub atom<&str>, map_res!(take_while1_s!(atom_char),
    str::from_utf8
));

/// astring = 1*ASTRING-CHAR / string
named!(pub astring<&[u8]>, alt!(
    take_while1!(astring_char) |
    string
));

/// astring bytes as as utf8
named!(pub astring_utf8<&str>, map_res!(astring, str::from_utf8));

/// text = 1*TEXT-CHAR
named!(pub text<&str>, map_res!(take_while_s!(text_char),
    str::from_utf8
));

/// TEXT-CHAR = <any CHAR except CR and LF>
pub fn text_char(c: u8) -> bool {
    c != b'\r' && c != b'\n'
}

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

    #[test]
    fn test_string_literal() {
        match string(b"{3}\r\nXYZ") {
            Ok((_, value)) => {
                assert_eq!(value, "XYZ".as_bytes());
            }
            rsp => panic!("unexpected response {:?}", rsp),
        }
    }

    #[test]
    fn test_astring() {
        match astring(b"text ") {
            Ok((_, value)) => {
                assert_eq!(value, "text".as_bytes());
            }
            rsp => panic!("unexpected response {:?}", rsp),
        }
    }
}
<|endoftext|>