<commit_before>use super::{
    function_body_chunk,
    general::{
        cfg_condition, cfg_deprecated, doc_alias, doc_hidden, not_version_condition,
        version_condition,
    },
    parameter::ToParameter,
    return_value::{out_parameters_as_return, ToReturnValue},
    special_functions,
};
use crate::{
    analysis::{
        self, bounds::Bounds, functions::Visibility, namespaces, try_from_glib::TryFromGlib,
    },
    chunk::{ffi_function_todo, Chunk},
    env::Env,
    library,
    version::Version,
    writer::{primitives::tabs, safety_assertion_mode_to_str, 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,
    special_functions: Option<&analysis::special_functions::Infos>,
    scope_version: Option<Version>,
    in_trait: bool,
    only_declaration: bool,
    indent: usize,
) -> Result<()> {
    if !analysis.status.need_generate() {
        return Ok(());
    }

    if analysis.is_async_finish(env) {
        return Ok(());
    }

    if let Some(special_functions) = special_functions {
        if special_functions::generate(w, env, analysis, special_functions, scope_version)? {
            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 unsafe_ = if analysis.unsafe_ { "unsafe " } else { "" };
    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)?;
    let version = Version::if_stricter_than(analysis.version, scope_version);
    version_condition(w, env, version, commented, indent)?;
    not_version_condition(w, analysis.not_version, commented, indent)?;
    doc_hidden(w, analysis.doc_hidden, comment_prefix, indent)?;
    if !in_trait || only_declaration {
        doc_alias(w, &analysis.glib_name, comment_prefix, indent)?;
        if analysis.codegen_name() != analysis.func_name {
            doc_alias(w, &analysis.func_name, comment_prefix, indent)?;
        }
    }
    writeln!(
        w,
        "{}{}{}{}{}{}",
        tabs(indent),
        comment_prefix,
        pub_prefix,
        unsafe_,
        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, 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 if let Some(return_type) = analysis.ret.to_return_value(
        env,
        analysis
            .ret
            .parameter
            .as_ref()
            .map_or(&TryFromGlib::Default, |par| &par.try_from_glib),
        false,
    ) {
        format!(" -> {}", return_type)
    } else {
        String::new()
    };
    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.codegen_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(bound) = analysis.bounds.get_parameter_bound(&c_par.name) {
                skipped_bounds.push(bound.type_parameter_reference());
            }
            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 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 lifetimes = bounds
        .iter_lifetimes()
        .filter(|s| !skip_lifetimes.contains(s))
        .map(|s| format!("'{}", s))
        .collect::<Vec<_>>();

    let bounds = bounds.iter().filter(|bound| {
        !skip.contains(&bound.alias) && (!filter_callback_modified || !bound.callback_modified)
    });

    let type_names = lifetimes
        .iter()
        .cloned()
        .chain(bounds.clone().map(|b| b.type_parameter_definition(r#async)))
        .collect::<Vec<_>>();

    let type_names = if type_names.is_empty() {
        String::new()
    } else {
        format!("<{}>", type_names.join(", "))
    };

    let bounds = lifetimes
        .into_iter()
        .chain(bounds.map(|b| b.alias.to_string()))
        .collect::<Vec<_>>();

    (type_names, 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)
        .in_unsafe(analysis.unsafe_)
        .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.codegen_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(|out| out.lib_par.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)?;

    if !async_future.assertion.is_none() {
        writeln!(
            body,
            "{}",
            safety_assertion_mode_to_str(async_future.assertion)
        )?;
    }
    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 = matches!(
            *type_,
            library::Type::Fundamental(library::Fundamental::Utf8)
        );

        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.codegen_name())?;
    } else if analysis.type_name.is_ok() {
        writeln!(body, "\tSelf::{}(", analysis.codegen_name())?;
    } else {
        writeln!(body, "\t{}(", analysis.codegen_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)
}
<commit_msg>Let cancellable be passed into GioFuture instead of creating it ourselves<commit_after>use super::{
    function_body_chunk,
    general::{
        cfg_condition, cfg_deprecated, doc_alias, doc_hidden, not_version_condition,
        version_condition,
    },
    parameter::ToParameter,
    return_value::{out_parameters_as_return, ToReturnValue},
    special_functions,
};
use crate::{
    analysis::{
        self, bounds::Bounds, functions::Visibility, namespaces, try_from_glib::TryFromGlib,
    },
    chunk::{ffi_function_todo, Chunk},
    env::Env,
    library,
    version::Version,
    writer::{primitives::tabs, safety_assertion_mode_to_str, 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,
    special_functions: Option<&analysis::special_functions::Infos>,
    scope_version: Option<Version>,
    in_trait: bool,
    only_declaration: bool,
    indent: usize,
) -> Result<()> {
    if !analysis.status.need_generate() {
        return Ok(());
    }

    if analysis.is_async_finish(env) {
        return Ok(());
    }

    if let Some(special_functions) = special_functions {
        if special_functions::generate(w, env, analysis, special_functions, scope_version)? {
            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 unsafe_ = if analysis.unsafe_ { "unsafe " } else { "" };
    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)?;
    let version = Version::if_stricter_than(analysis.version, scope_version);
    version_condition(w, env, version, commented, indent)?;
    not_version_condition(w, analysis.not_version, commented, indent)?;
    doc_hidden(w, analysis.doc_hidden, comment_prefix, indent)?;
    if !in_trait || only_declaration {
        doc_alias(w, &analysis.glib_name, comment_prefix, indent)?;
        if analysis.codegen_name() != analysis.func_name {
            doc_alias(w, &analysis.func_name, comment_prefix, indent)?;
        }
    }
    writeln!(
        w,
        "{}{}{}{}{}{}",
        tabs(indent),
        comment_prefix,
        pub_prefix,
        unsafe_,
        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, 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 if let Some(return_type) = analysis.ret.to_return_value(
        env,
        analysis
            .ret
            .parameter
            .as_ref()
            .map_or(&TryFromGlib::Default, |par| &par.try_from_glib),
        false,
    ) {
        format!(" -> {}", return_type)
    } else {
        String::new()
    };
    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.codegen_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(bound) = analysis.bounds.get_parameter_bound(&c_par.name) {
                skipped_bounds.push(bound.type_parameter_reference());
            }
            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 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 lifetimes = bounds
        .iter_lifetimes()
        .filter(|s| !skip_lifetimes.contains(s))
        .map(|s| format!("'{}", s))
        .collect::<Vec<_>>();

    let bounds = bounds.iter().filter(|bound| {
        !skip.contains(&bound.alias) && (!filter_callback_modified || !bound.callback_modified)
    });

    let type_names = lifetimes
        .iter()
        .cloned()
        .chain(bounds.clone().map(|b| b.type_parameter_definition(r#async)))
        .collect::<Vec<_>>();

    let type_names = if type_names.is_empty() {
        String::new()
    } else {
        format!("<{}>", type_names.join(", "))
    };

    let bounds = lifetimes
        .into_iter()
        .chain(bounds.map(|b| b.alias.to_string()))
        .collect::<Vec<_>>();

    (type_names, 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)
        .in_unsafe(analysis.unsafe_)
        .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.codegen_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(|out| out.lib_par.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)?;

    if !async_future.assertion.is_none() {
        writeln!(
            body,
            "{}",
            safety_assertion_mode_to_str(async_future.assertion)
        )?;
    }
    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 = matches!(
            *type_,
            library::Type::Fundamental(library::Fundamental::Utf8)
        );

        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, cancellable, send| {{",
            gio_future_name
        )?;
    } else {
        writeln!(
            body,
            "Box_::pin({}::new(&(), move |_obj, cancellable, send| {{",
            gio_future_name
        )?;
    }

    if async_future.is_method {
        writeln!(body, "\tobj.{}(", analysis.codegen_name())?;
    } else if analysis.type_name.is_ok() {
        writeln!(body, "\tSelf::{}(", analysis.codegen_name())?;
    } else {
        writeln!(body, "\t{}(", analysis.codegen_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, "}}))")?;

    Ok(body)
}
<|endoftext|>


<commit_before>use bot::LalafellBot;

use lalafell::bot::Bot;
use lalafell::commands::prelude::*;
use lalafell::commands::ChannelOrId;
use lalafell::error::*;

use discord::GetMessages;
use discord::model::{permissions, Message, PublicChannel, Member, Emoji, Role};

use serde_json;

use std::sync::Arc;
use std::fs::{self, File};
use std::path::Path;

const USAGE: &'static str = "!archive <channel>";

pub struct ArchiveCommand {
  bot: Arc<LalafellBot>
}

impl ArchiveCommand {
  pub fn new(bot: Arc<LalafellBot>) -> ArchiveCommand {
    ArchiveCommand {
      bot: bot
    }
  }
}

impl HasBot for ArchiveCommand {
  fn bot(&self) -> &Bot {
    self.bot.as_ref()
  }
}

#[derive(Debug, Deserialize)]
pub struct Params {
  channel: ChannelOrId
}

impl HasParams for ArchiveCommand {
  type Params = Params;
}

impl<'a> PublicChannelCommand<'a> for ArchiveCommand {
  fn run(&self, message: &Message, server: &LiveServer, channel: &PublicChannel, params: &[&str]) -> CommandResult<'a> {
    let params = self.params(USAGE, params)?;
    if !server.channels.iter().any(|c| c.id == *params.channel) {
      return Err("This command must be run in the server the channel is in.".into());
    }
    let can_manage_chans = server.permissions_for(*params.channel, message.author.id).contains(permissions::MANAGE_CHANNELS);
    if !can_manage_chans {
      return Err(ExternalCommandFailure::default()
        .message(|e: EmbedBuilder| e
          .title("Not enough permissions.")
          .description("You don't have enough permissions to use this command."))
        .wrap());
    }

    let archive_path = Path::new("./archives")
      .join(&server.id.to_string())
      .join(&params.channel.to_string())
      .with_extension("json");
    if let Some(parent) = archive_path.parent() {
      if !parent.exists() {
        fs::create_dir_all(&parent)
          .chain_err(|| "could not create archive directory")?;
      }
    }
    if archive_path.exists() {
      return Err("This channel is already archived.".into());
    }

    let mut messages = self.bot.discord.get_messages(*params.channel, GetMessages::MostRecent, Some(100))
      .chain_err(|| "could not download first set of messages")?;
    if messages.len() >= 100 {
      loop {
        let last_message_id = messages[messages.len() - 1].id;
        let mut next_batch = self.bot.discord.get_messages(*params.channel, GetMessages::Before(last_message_id), Some(100))
          .chain_err(|| "could not download more messages")?;
        if next_batch.is_empty() {
          break;
        }
        messages.append(&mut next_batch);
      }
    }

    let num_messages = messages.len();

    let archive = Archive {
      server: ArchiveServer {
        name: server.name.clone(),
        roles: server.roles.clone(),
        members: server.members.clone(),
        icon: server.icon.clone(),
        emojis: server.emojis.clone()
      },
      channel: ArchiveChannel {
        name: channel.name.clone(),
        topic: channel.topic.clone()
      },
      messages: messages
    };

    let file = File::create(archive_path)
      .chain_err(|| "could not create archive file")?;
    serde_json::to_writer(file, &archive)
      .chain_err(|| "could not serialize messages")?;

    Ok(format!("Archived {} message{}.", num_messages, if num_messages == 1 { "" } else { "s" }).into())
  }
}

#[derive(Debug, Serialize)]
struct Archive {
  server: ArchiveServer,
  channel: ArchiveChannel,
  messages: Vec<Message>
}

#[derive(Debug, Serialize)]
struct ArchiveServer {
  name: String,
  roles: Vec<Role>,
  members: Vec<Member>,
  icon: Option<String>,
  emojis: Vec<Emoji>
}

#[derive(Debug, Serialize)]
struct ArchiveChannel {
  name: String,
  topic: Option<String>
}
<commit_msg>fix: include channels in archive state<commit_after>use bot::LalafellBot;

use lalafell::bot::Bot;
use lalafell::commands::prelude::*;
use lalafell::commands::ChannelOrId;
use lalafell::error::*;

use discord::GetMessages;
use discord::model::{permissions, Message, PublicChannel, Member, Emoji, Role};

use serde_json;

use std::sync::Arc;
use std::fs::{self, File};
use std::path::Path;

const USAGE: &'static str = "!archive <channel>";

pub struct ArchiveCommand {
  bot: Arc<LalafellBot>
}

impl ArchiveCommand {
  pub fn new(bot: Arc<LalafellBot>) -> ArchiveCommand {
    ArchiveCommand {
      bot: bot
    }
  }
}

impl HasBot for ArchiveCommand {
  fn bot(&self) -> &Bot {
    self.bot.as_ref()
  }
}

#[derive(Debug, Deserialize)]
pub struct Params {
  channel: ChannelOrId
}

impl HasParams for ArchiveCommand {
  type Params = Params;
}

impl<'a> PublicChannelCommand<'a> for ArchiveCommand {
  fn run(&self, message: &Message, server: &LiveServer, channel: &PublicChannel, params: &[&str]) -> CommandResult<'a> {
    let params = self.params(USAGE, params)?;
    if !server.channels.iter().any(|c| c.id == *params.channel) {
      return Err("This command must be run in the server the channel is in.".into());
    }
    let can_manage_chans = server.permissions_for(*params.channel, message.author.id).contains(permissions::MANAGE_CHANNELS);
    if !can_manage_chans {
      return Err(ExternalCommandFailure::default()
        .message(|e: EmbedBuilder| e
          .title("Not enough permissions.")
          .description("You don't have enough permissions to use this command."))
        .wrap());
    }

    let archive_path = Path::new("./archives")
      .join(&server.id.to_string())
      .join(&params.channel.to_string())
      .with_extension("json");
    if let Some(parent) = archive_path.parent() {
      if !parent.exists() {
        fs::create_dir_all(&parent)
          .chain_err(|| "could not create archive directory")?;
      }
    }
    if archive_path.exists() {
      return Err("This channel is already archived.".into());
    }

    let mut messages = self.bot.discord.get_messages(*params.channel, GetMessages::MostRecent, Some(100))
      .chain_err(|| "could not download first set of messages")?;
    if messages.len() >= 100 {
      loop {
        let last_message_id = messages[messages.len() - 1].id;
        let mut next_batch = self.bot.discord.get_messages(*params.channel, GetMessages::Before(last_message_id), Some(100))
          .chain_err(|| "could not download more messages")?;
        if next_batch.is_empty() {
          break;
        }
        messages.append(&mut next_batch);
      }
    }

    let num_messages = messages.len();

    let archive = Archive {
      server: ArchiveServer {
        name: server.name.clone(),
        roles: server.roles.clone(),
        members: server.members.clone(),
        channels: server.channels.iter()
          .map(|c| ArchiveChannel { name: c.name.clone(), topic: c.topic.clone() })
          .collect(),
        icon: server.icon.clone(),
        emojis: server.emojis.clone()
      },
      channel: ArchiveChannel {
        name: channel.name.clone(),
        topic: channel.topic.clone()
      },
      messages: messages
    };

    let file = File::create(archive_path)
      .chain_err(|| "could not create archive file")?;
    serde_json::to_writer(file, &archive)
      .chain_err(|| "could not serialize messages")?;

    Ok(format!("Archived {} message{}.", num_messages, if num_messages == 1 { "" } else { "s" }).into())
  }
}

#[derive(Debug, Serialize)]
struct Archive {
  server: ArchiveServer,
  channel: ArchiveChannel,
  messages: Vec<Message>
}

#[derive(Debug, Serialize)]
struct ArchiveServer {
  name: String,
  roles: Vec<Role>,
  members: Vec<Member>,
  channels: Vec<ArchiveChannel>,
  icon: Option<String>,
  emojis: Vec<Emoji>
}

#[derive(Debug, Serialize)]
struct ArchiveChannel {
  name: String,
  topic: Option<String>
}
<|endoftext|>


<commit_before>//! A Rust interface for Objective-C blocks.
//!
//! For more information on the specifics of the block implementation, see
//! Clang's documentation: http://clang.llvm.org/docs/Block-ABI-Apple.html

use std::mem;
use std::ptr;
use libc::{c_int, c_ulong};

use runtime::Class;
use {Id, Message};

#[allow(improper_ctypes)]
#[link(name = "Foundation", kind = "framework")]
extern {
    static _NSConcreteStackBlock: Class;
}

/// An invoke function for a `Block`; this is the raw C function called by the
/// Objective-C runtime.
type BlockInvoke<A, R> = unsafe extern fn(*mut Block<A, R>, ...) -> R;

/// An invoke function for a `ConcreteBlock`; this is the raw C function called
/// by the Objective-C runtime.
pub type ConcreteBlockInvoke<A, R, C> =
    unsafe extern fn(*mut ConcreteBlock<A, R, C>, ...) -> R;

/// Types that may be used as the arguments to an Objective-C block.
pub trait BlockArguments {
    /// Calls the given `Block` with self as the arguments.
    fn call_block<R>(self, block: &Block<Self, R>) -> R;

    /// Returns an invoke function for a `ConcreteBlock` that takes this type
    /// of arguments.
    fn invoke_for_concrete_block<R, C>() -> ConcreteBlockInvoke<Self, R, C>;
}

macro_rules! block_args_impl(
    ($f:ident $(, $a:ident : $t:ident)*) => (
        impl<$($t),*> BlockArguments for ($($t,)*) {
            fn call_block<R>(self, block: &Block<Self, R>) -> R {
                let invoke: unsafe extern fn(*mut Block<Self, R> $(, $t)*) -> R = unsafe {
                    mem::transmute(block.invoke)
                };
                let ($($a,)*) = self;
                let block_ptr = block as *const _ as *mut _;
                unsafe {
                    invoke(block_ptr $(, $a)*)
                }
            }

            fn invoke_for_concrete_block<R, X>() ->
                    ConcreteBlockInvoke<Self, R, X> {
                unsafe extern fn $f<R, X $(, $t)*>(
                        block_ptr: *mut ConcreteBlock<Self, R, X>
                        $(, $a: $t)*) -> R {
                    let args = ($($a,)*);
                    let block = &*block_ptr;
                    (block.rust_invoke)(&block.context, args)
                }

                unsafe {
                    mem::transmute($f::<R, X $(, $t)*>)
                }
            }
        }
    );
);

block_args_impl!(concrete_block_invoke_args0);
block_args_impl!(concrete_block_invoke_args1, a: A);
block_args_impl!(concrete_block_invoke_args2, a: A, b: B);
block_args_impl!(concrete_block_invoke_args3, a: A, b: B, c: C);
block_args_impl!(concrete_block_invoke_args4, a: A, b: B, c: C, d: D);
block_args_impl!(concrete_block_invoke_args5, a: A, b: B, c: C, d: D, e: E);
block_args_impl!(concrete_block_invoke_args6, a: A, b: B, c: C, d: D, e: E, f: F);
block_args_impl!(concrete_block_invoke_args7, a: A, b: B, c: C, d: D, e: E, f: F, g: G);
block_args_impl!(concrete_block_invoke_args8, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H);
block_args_impl!(concrete_block_invoke_args9, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I);
block_args_impl!(concrete_block_invoke_args10, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J);
block_args_impl!(concrete_block_invoke_args11, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K);
block_args_impl!(concrete_block_invoke_args12, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L);

/// An Objective-C block that takes arguments of `A` when called and
/// returns a value of `R`.
#[repr(C)]
pub struct Block<A: BlockArguments, R> {
    isa: *const Class,
    flags: c_int,
    _reserved: c_int,
    invoke: BlockInvoke<A, R>,
}

impl<A: BlockArguments, R> Block<A, R> {
    /// Call self with the given arguments.
    pub fn call(&self, args: A) -> R {
        args.call_block(self)
    }
}

impl<A: BlockArguments, R> Message for Block<A, R> { }

/// An Objective-C block whose size is known at compile time and may be
/// constructed on the stack.
#[repr(C)]
pub struct ConcreteBlock<A: BlockArguments, R, C> {
    base: Block<A, R>,
    descriptor: Box<BlockDescriptor<ConcreteBlock<A, R, C>>>,
    rust_invoke: fn (&C, A) -> R,
    context: C,
}

impl<A: BlockArguments, R, C> ConcreteBlock<A, R, C> {
    /// Constructs a `ConcreteBlock` with the given invoke function and context.
    /// When the block is called, it will return the value that results from
    /// calling the invoke function with a reference to its context.
    pub fn new(invoke: fn (&C, A) -> R, context: C) -> Self {
        let extern_invoke: ConcreteBlockInvoke<A, R, C> =
            BlockArguments::invoke_for_concrete_block();
        ConcreteBlock {
            base: Block {
                isa: &_NSConcreteStackBlock,
                // 1 << 25 = BLOCK_HAS_COPY_DISPOSE
                flags: 1 << 25,
                _reserved: 0,
                invoke: unsafe { mem::transmute(extern_invoke) },
            },
            descriptor: box BlockDescriptor::<Self>::new(),
            rust_invoke: invoke,
            context: context,
        }
    }

    /// Copy self onto the heap.
    pub fn copy(self) -> Id<Block<A, R>> {
        unsafe {
            let block = msg_send![&self.base copy] as *mut Block<A, R>;
            // At this point, our copy helper has been run so the block will
            // be moved to the heap and we can forget the original block
            // because the heap block will drop in our dispose helper.
            mem::forget(self);
            Id::from_retained_ptr(block)
        }
    }
}

impl<A: BlockArguments, R, C: Clone> Clone for ConcreteBlock<A, R, C> {
    fn clone(&self) -> Self {
        ConcreteBlock::new(self.rust_invoke, self.context.clone())
    }
}

impl<A: BlockArguments, R, C> Deref<Block<A, R>> for ConcreteBlock<A, R, C> {
    fn deref(&self) -> &Block<A, R> {
        &self.base
    }
}

unsafe extern fn block_context_dispose<B>(block: &mut B) {
    // Read the block onto the stack and let it drop
    ptr::read(block);
}

unsafe extern fn block_context_copy<B>(_dst: &mut B, _src: &B) {
    // The runtime memmoves the src block into the dst block, nothing to do
}

#[repr(C)]
struct BlockDescriptor<B> {
    _reserved: c_ulong,
    block_size: c_ulong,
    copy_helper: unsafe extern fn(&mut B, &B),
    dispose_helper: unsafe extern fn(&mut B),
}

impl<B> BlockDescriptor<B> {
    fn new() -> BlockDescriptor<B> {
        BlockDescriptor {
            _reserved: 0,
            block_size: mem::size_of::<B>() as c_ulong,
            copy_helper: block_context_copy::<B>,
            dispose_helper: block_context_dispose::<B>,
        }
    }
}

#[cfg(test)]
mod tests {
    use Id;
    use objc_test_utils;
    use super::{Block, ConcreteBlock};

    fn get_int_block() -> &'static Block<(), int> {
        unsafe {
            &*(objc_test_utils::get_int_block() as *const _)
        }
    }

    fn get_int_block_with(i: int) -> Id<Block<(), int>> {
        unsafe {
            let ptr = objc_test_utils::get_int_block_with(i);
            Id::from_retained_ptr(ptr as *mut _)
        }
    }

    fn get_add_block() -> &'static Block<(int,), int> {
        unsafe {
            &*(objc_test_utils::get_add_block() as *const _)
        }
    }

    fn get_add_block_with(i: int) -> Id<Block<(int,), int>> {
        unsafe {
            let ptr = objc_test_utils::get_add_block_with(i);
            Id::from_retained_ptr(ptr as *mut _)
        }
    }

    fn invoke_int_block(block: &Block<(), int>) -> int {
        let ptr = block as *const _;
        unsafe {
            objc_test_utils::invoke_int_block(ptr as *const _)
        }
    }

    fn invoke_add_block(block: &Block<(int,), int>, a: int) -> int {
        let ptr = block as *const _;
        unsafe {
            objc_test_utils::invoke_add_block(ptr as *const _, a)
        }
    }

    #[test]
    fn test_call_block() {
        let block = get_int_block();
        assert!(block.call(()) == 7);

        let block = get_int_block_with(13);
        assert!(block.call(()) == 13);
    }

    #[test]
    fn test_call_block_args() {
        let block = get_add_block();
        assert!(block.call((2,)) == 9);

        let block = get_add_block_with(13);
        assert!(block.call((2,)) == 15);
    }

    #[test]
    fn test_create_block() {
        fn block_get_int(context: &int, _args: ()) -> int {
            *context
        }

        let block = ConcreteBlock::new(block_get_int, 13);
        let result = invoke_int_block(&*block);
        assert!(result == 13);
    }

    #[test]
    fn test_create_block_args() {
        fn block_add_int(context: &int, (a,): (int,)) -> int {
            a + *context
        }

        let block = ConcreteBlock::new(block_add_int, 5);
        let result = invoke_add_block(&*block, 6);
        assert!(result == 11);
    }

    #[test]
    fn test_concrete_block_clone() {
        fn block_get_string_len(context: &String, _args: ()) -> uint {
            context.len()
        }

        let s = String::from_str("Hello!");
        let expected_len = s.len();
        let block = ConcreteBlock::new(block_get_string_len, s);
        assert!(block.call(()) == expected_len);

        let cloned = block.clone();
        assert!(cloned.call(()) == expected_len);

        drop(block);
        assert!(cloned.call(()) == expected_len);
    }

    #[test]
    fn test_concrete_block_copy() {
        fn block_get_string_len(context: &String, _args: ()) -> uint {
            context.len()
        }

        let s = String::from_str("Hello!");
        let expected_len = s.len();
        let block = ConcreteBlock::new(block_get_string_len, s);
        assert!(block.call(()) == expected_len);

        let copied = block.copy();
        assert!(copied.call(()) == expected_len);
    }
}
<commit_msg>Changed ConcreteBlock to use a generic closure.<commit_after>//! A Rust interface for Objective-C blocks.
//!
//! For more information on the specifics of the block implementation, see
//! Clang's documentation: http://clang.llvm.org/docs/Block-ABI-Apple.html

use std::mem;
use std::ptr;
use libc::{c_int, c_ulong};

use runtime::Class;
use {Id, Message};

#[allow(improper_ctypes)]
#[link(name = "Foundation", kind = "framework")]
extern {
    static _NSConcreteStackBlock: Class;
}

/// An invoke function for a `Block`; this is the raw C function called by the
/// Objective-C runtime.
type BlockInvoke<A, R> = unsafe extern fn(*mut Block<A, R>, ...) -> R;

/// An invoke function for a `ConcreteBlock`; this is the raw C function called
/// by the Objective-C runtime.
pub type ConcreteBlockInvoke<A, R, F> =
    unsafe extern fn(*mut ConcreteBlock<A, R, F>, ...) -> R;

/// Types that may be used as the arguments to an Objective-C block.
pub trait BlockArguments {
    /// Calls the given `Block` with self as the arguments.
    fn call_block<R>(self, block: &Block<Self, R>) -> R;

    /// Returns an invoke function for a `ConcreteBlock` that takes this type
    /// of arguments.
    fn invoke_for_concrete_block<R, F: Fn<Self, R>>() -> ConcreteBlockInvoke<Self, R, F>;
}

macro_rules! block_args_impl(
    ($f:ident $(, $a:ident : $t:ident)*) => (
        impl<$($t),*> BlockArguments for ($($t,)*) {
            fn call_block<R>(self, block: &Block<Self, R>) -> R {
                let invoke: unsafe extern fn(*mut Block<Self, R> $(, $t)*) -> R = unsafe {
                    mem::transmute(block.invoke)
                };
                let ($($a,)*) = self;
                let block_ptr = block as *const _ as *mut _;
                unsafe {
                    invoke(block_ptr $(, $a)*)
                }
            }

            fn invoke_for_concrete_block<R, X: Fn<Self, R>>() ->
                    ConcreteBlockInvoke<Self, R, X> {
                unsafe extern fn $f<R, X: Fn<Self, R> $(, $t)*>(
                        block_ptr: *mut ConcreteBlock<Self, R, X>
                        $(, $a: $t)*) -> R {
                    let block = &*block_ptr;
                    (block.closure)($($a),*)
                }

                unsafe {
                    mem::transmute($f::<R, X $(, $t)*>)
                }
            }
        }
    );
);

block_args_impl!(concrete_block_invoke_args0);
block_args_impl!(concrete_block_invoke_args1, a: A);
block_args_impl!(concrete_block_invoke_args2, a: A, b: B);
block_args_impl!(concrete_block_invoke_args3, a: A, b: B, c: C);
block_args_impl!(concrete_block_invoke_args4, a: A, b: B, c: C, d: D);
block_args_impl!(concrete_block_invoke_args5, a: A, b: B, c: C, d: D, e: E);
block_args_impl!(concrete_block_invoke_args6, a: A, b: B, c: C, d: D, e: E, f: F);
block_args_impl!(concrete_block_invoke_args7, a: A, b: B, c: C, d: D, e: E, f: F, g: G);
block_args_impl!(concrete_block_invoke_args8, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H);
block_args_impl!(concrete_block_invoke_args9, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I);
block_args_impl!(concrete_block_invoke_args10, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J);
block_args_impl!(concrete_block_invoke_args11, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K);
block_args_impl!(concrete_block_invoke_args12, a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L);

/// An Objective-C block that takes arguments of `A` when called and
/// returns a value of `R`.
#[repr(C)]
pub struct Block<A: BlockArguments, R> {
    isa: *const Class,
    flags: c_int,
    _reserved: c_int,
    invoke: BlockInvoke<A, R>,
}

impl<A: BlockArguments, R> Block<A, R> {
    /// Call self with the given arguments.
    pub fn call(&self, args: A) -> R {
        args.call_block(self)
    }
}

impl<A: BlockArguments, R> Message for Block<A, R> { }

/// An Objective-C block whose size is known at compile time and may be
/// constructed on the stack.
#[repr(C)]
pub struct ConcreteBlock<A: BlockArguments, R, F> {
    base: Block<A, R>,
    descriptor: Box<BlockDescriptor<ConcreteBlock<A, R, F>>>,
    closure: F,
}

impl<A: BlockArguments, R, F: Fn<A, R>> ConcreteBlock<A, R, F> {
    pub fn new(closure: F) -> Self {
        let extern_invoke: ConcreteBlockInvoke<A, R, F> =
            BlockArguments::invoke_for_concrete_block();
        ConcreteBlock {
            base: Block {
                isa: &_NSConcreteStackBlock,
                // 1 << 25 = BLOCK_HAS_COPY_DISPOSE
                flags: 1 << 25,
                _reserved: 0,
                invoke: unsafe { mem::transmute(extern_invoke) },
            },
            descriptor: box BlockDescriptor::<Self>::new(),
            closure: closure,
        }
    }

    /// Copy self onto the heap.
    pub fn copy(self) -> Id<Block<A, R>> {
        unsafe {
            let block = msg_send![&self.base copy] as *mut Block<A, R>;
            // At this point, our copy helper has been run so the block will
            // be moved to the heap and we can forget the original block
            // because the heap block will drop in our dispose helper.
            mem::forget(self);
            Id::from_retained_ptr(block)
        }
    }
}

impl<A: BlockArguments, R, F: Fn<A, R> + Clone> Clone for ConcreteBlock<A, R, F> {
    fn clone(&self) -> Self {
        ConcreteBlock::new(self.closure.clone())
    }
}

impl<A: BlockArguments, R, F> Deref<Block<A, R>> for ConcreteBlock<A, R, F> {
    fn deref(&self) -> &Block<A, R> {
        &self.base
    }
}

unsafe extern fn block_context_dispose<B>(block: &mut B) {
    // Read the block onto the stack and let it drop
    ptr::read(block);
}

unsafe extern fn block_context_copy<B>(_dst: &mut B, _src: &B) {
    // The runtime memmoves the src block into the dst block, nothing to do
}

#[repr(C)]
struct BlockDescriptor<B> {
    _reserved: c_ulong,
    block_size: c_ulong,
    copy_helper: unsafe extern fn(&mut B, &B),
    dispose_helper: unsafe extern fn(&mut B),
}

impl<B> BlockDescriptor<B> {
    fn new() -> BlockDescriptor<B> {
        BlockDescriptor {
            _reserved: 0,
            block_size: mem::size_of::<B>() as c_ulong,
            copy_helper: block_context_copy::<B>,
            dispose_helper: block_context_dispose::<B>,
        }
    }
}

#[cfg(test)]
mod tests {
    use Id;
    use objc_test_utils;
    use super::{Block, ConcreteBlock};

    fn get_int_block() -> &'static Block<(), int> {
        unsafe {
            &*(objc_test_utils::get_int_block() as *const _)
        }
    }

    fn get_int_block_with(i: int) -> Id<Block<(), int>> {
        unsafe {
            let ptr = objc_test_utils::get_int_block_with(i);
            Id::from_retained_ptr(ptr as *mut _)
        }
    }

    fn get_add_block() -> &'static Block<(int,), int> {
        unsafe {
            &*(objc_test_utils::get_add_block() as *const _)
        }
    }

    fn get_add_block_with(i: int) -> Id<Block<(int,), int>> {
        unsafe {
            let ptr = objc_test_utils::get_add_block_with(i);
            Id::from_retained_ptr(ptr as *mut _)
        }
    }

    fn invoke_int_block(block: &Block<(), int>) -> int {
        let ptr = block as *const _;
        unsafe {
            objc_test_utils::invoke_int_block(ptr as *const _)
        }
    }

    fn invoke_add_block(block: &Block<(int,), int>, a: int) -> int {
        let ptr = block as *const _;
        unsafe {
            objc_test_utils::invoke_add_block(ptr as *const _, a)
        }
    }

    #[test]
    fn test_call_block() {
        let block = get_int_block();
        assert!(block.call(()) == 7);

        let block = get_int_block_with(13);
        assert!(block.call(()) == 13);
    }

    #[test]
    fn test_call_block_args() {
        let block = get_add_block();
        assert!(block.call((2,)) == 9);

        let block = get_add_block_with(13);
        assert!(block.call((2,)) == 15);
    }

    #[test]
    fn test_create_block() {
        let block = ConcreteBlock::new(|&:| 13);
        let result = invoke_int_block(&*block);
        assert!(result == 13);
    }

    #[test]
    fn test_create_block_args() {
        let block = ConcreteBlock::new(|&: a: int| a + 5);
        let result = invoke_add_block(&*block, 6);
        assert!(result == 11);
    }

    #[test]
    fn test_concrete_block_copy() {
        let s = String::from_str("Hello!");
        let expected_len = s.len();
        let block = ConcreteBlock::new(move |&:| s.len());
        assert!(block.call(()) == expected_len);

        let copied = block.copy();
        assert!(copied.call(()) == expected_len);
    }
}
<|endoftext|>


<commit_before>import std::map;
import std::map::hashmap;
import std::uint;
import std::int;
import std::vec;
import std::option::none;
import std::option::some;
import front::ast;
import front::ast::ty;
import front::ast::pat;
import front::codemap::codemap;
import middle::walk;

import std::io::stdout;
import std::io::str_writer;
import std::io::string_writer;
import pretty::pprust::print_block;
import pretty::pprust::print_item;
import pretty::pprust::print_expr;
import pretty::pprust::print_decl;
import pretty::pprust::print_fn;
import pretty::pprust::print_type;
import pretty::pprust::mo_untyped;
import pretty::pp::mk_printer;

type filename = str;
type span = rec(uint lo, uint hi);
type spanned[T] = rec(T node, span span);
type flag = hashmap[str, ()];

tag ty_mach {
    ty_i8;
    ty_i16;
    ty_i32;
    ty_i64;

    ty_u8;
    ty_u16;
    ty_u32;
    ty_u64;

    ty_f32;
    ty_f64;
}

tag ty_or_bang[T] {
    a_ty(T);
    a_bang;
}

fn ty_mach_to_str(ty_mach tm) -> str {
    alt (tm) {
        case (ty_u8) { ret "u8"; }
        case (ty_u16) { ret "u16"; }
        case (ty_u32) { ret "u32"; }
        case (ty_u64) { ret "u64"; }

        case (ty_i8) { ret "i8"; }
        case (ty_i16) { ret "i16"; }
        case (ty_i32) { ret "i32"; }
        case (ty_i64) { ret "i64"; }

        case (ty_f32) { ret "f32"; }
        case (ty_f64) { ret "f64"; }
    }
}

fn new_str_hash[V]() -> std::map::hashmap[str,V] {
    let std::map::hashfn[str] hasher = std::str::hash;
    let std::map::eqfn[str] eqer = std::str::eq;
    ret std::map::mk_hashmap[str,V](hasher, eqer);
}

fn def_eq(&ast::def_id a, &ast::def_id b) -> bool {
    ret a._0 == b._0 && a._1 == b._1;
}

fn hash_def(&ast::def_id d) -> uint {
    auto h = 5381u;
    h = ((h << 5u) + h) ^ (d._0 as uint);
    h = ((h << 5u) + h) ^ (d._1 as uint);
    ret h;
}

fn new_def_hash[V]() -> std::map::hashmap[ast::def_id,V] {
    let std::map::hashfn[ast::def_id] hasher = hash_def;
    let std::map::eqfn[ast::def_id] eqer = def_eq;
    ret std::map::mk_hashmap[ast::def_id,V](hasher, eqer);
}

fn new_int_hash[V]() -> std::map::hashmap[int,V] {
    fn hash_int(&int x) -> uint { ret x as uint; }
    fn eq_int(&int a, &int b) -> bool { ret a == b; }
    auto hasher = hash_int;
    auto eqer = eq_int;
    ret std::map::mk_hashmap[int,V](hasher, eqer);
}

fn new_uint_hash[V]() -> std::map::hashmap[uint,V] {
    fn hash_uint(&uint x) -> uint { ret x; }
    fn eq_uint(&uint a, &uint b) -> bool { ret a == b; }
    auto hasher = hash_uint;
    auto eqer = eq_uint;
    ret std::map::mk_hashmap[uint,V](hasher, eqer);
}

fn istr(int i) -> str {
    ret int::to_str(i, 10u);
}

fn uistr(uint i) -> str {
    ret uint::to_str(i, 10u);
}

fn elt_expr(&ast::elt e) -> @ast::expr { ret e.expr; }

fn elt_exprs(&vec[ast::elt] elts) -> vec[@ast::expr] {
    auto f = elt_expr;
    ret vec::map[ast::elt, @ast::expr](f, elts);
}

fn field_expr(&ast::field f) -> @ast::expr { ret f.node.expr; }

fn field_exprs(vec[ast::field] fields) -> vec [@ast::expr] {
    auto f = field_expr;
    ret vec::map[ast::field, @ast::expr](f, fields);
}

fn expr_to_str(&@ast::expr e) -> str {
  let str_writer s = string_writer();
  auto out_ = mk_printer(s.get_writer(), 80u);
  auto out = @rec(s=out_,
                  cm=none[codemap],
                  comments=none[vec[front::lexer::cmnt]],
                  mutable cur_cmnt=0u,
                  mode=mo_untyped);
  print_expr(out, e);
  ret s.get_str();
}

fn ty_to_str(&ty t) -> str {
  let str_writer s = string_writer();
  auto out_ = mk_printer(s.get_writer(), 80u);
  auto out = @rec(s=out_,
                  cm=none[codemap],
                  comments=none[vec[front::lexer::cmnt]],
                  mutable cur_cmnt=0u,
                  mode=mo_untyped);
  print_type(out, @t);
  ret s.get_str();
}

fn log_expr(&ast::expr e) -> () {
    log(expr_to_str(@e));
}

fn log_expr_err(&ast::expr e) -> () {
    log_err(expr_to_str(@e));
}

fn log_ty_err(&ty t) -> () {
    log_err(ty_to_str(t));
}

fn log_pat_err(&@pat p) -> () {
    log_err(pretty::pprust::pat_to_str(p));
}

fn block_to_str(&ast::block b) -> str {
  let str_writer s = string_writer();
  auto out_ = mk_printer(s.get_writer(), 80u);
  auto out = @rec(s=out_,
                  cm=none[codemap],
                  comments=none[vec[front::lexer::cmnt]],
                  mutable cur_cmnt=0u,
                  mode=mo_untyped);

  print_block(out, b);
  ret s.get_str();
}

fn item_to_str(&@ast::item i) -> str {
  let str_writer s = string_writer();
  auto out_ = mk_printer(s.get_writer(), 80u);
  auto out = @rec(s=out_,
                  cm=none[codemap],
                  comments=none[vec[front::lexer::cmnt]],
                  mutable cur_cmnt=0u,
                  mode=mo_untyped);
  print_item(out, i);
  ret s.get_str();
}

fn log_block(&ast::block b) -> () {
    log(block_to_str(b));
}

fn log_block_err(&ast::block b) -> () {
    log_err(block_to_str(b));
}

fn log_item_err(&@ast::item i) -> () {
    log_err(item_to_str(i));
}

fn fun_to_str(&ast::_fn f, str name, vec[ast::ty_param] params) -> str {
 let str_writer s = string_writer();
  auto out_ = mk_printer(s.get_writer(), 80u);
  auto out = @rec(s=out_,
                  cm=none[codemap],
                  comments=none[vec[front::lexer::cmnt]],
                  mutable cur_cmnt=0u,
                  mode=mo_untyped);

  print_fn(out, f.decl, name, params);
  ret s.get_str();
}

fn log_fn(&ast::_fn f, str name, vec[ast::ty_param] params) -> () {
    log(fun_to_str(f, name, params));
}

fn log_fn_err(&ast::_fn f, str name, vec[ast::ty_param] params) -> () {
    log_err(fun_to_str(f, name, params));
}

fn stmt_to_str(&ast::stmt st) -> str {
  let str_writer s = string_writer();
  auto out_ = mk_printer(s.get_writer(), 80u);
  auto out = @rec(s=out_,
                  cm=none[codemap],
                  comments=none[vec[front::lexer::cmnt]],
                  mutable cur_cmnt=0u,
                  mode=mo_untyped);
  alt (st.node) {
    case (ast::stmt_decl(?decl,_)) {
      print_decl(out, decl);
    }
    case (ast::stmt_expr(?ex,_)) {
      print_expr(out, ex);
    }
    case (_) { /* do nothing */ }
  }
  ret s.get_str();
}

fn log_stmt(&ast::stmt st) -> () {
    log(stmt_to_str(st));
}

fn log_stmt_err(&ast::stmt st) -> () {
    log_err(stmt_to_str(st));
}

fn decl_lhs(@ast::decl d) -> ast::def_id {
    alt (d.node) {
        case (ast::decl_local(?l)) {
            ret l.id;
        }
        case (ast::decl_item(?an_item)) {
            alt (an_item.node) {
                case (ast::item_const(_,_,_,?d,_)) {
                    ret d;
                }
                case (ast::item_fn(_,_,_,?d,_)) {
                    ret d;
                }
                case (ast::item_mod(_,_,?d)) {
                    ret d;
                }
                case (ast::item_native_mod(_,_,?d)) {
                    ret d;
                }
                case (ast::item_ty(_,_,_,?d,_)) {
                    ret d;
                }
                case (ast::item_tag(_,_,_,?d,_)) {
                    ret d;
                }
                case (ast::item_obj(_,_,_,?d,_)) {
                    ret d.ctor; /* This doesn't really make sense */
                }
            }
        }
    }
}

fn has_nonlocal_exits(&ast::block b) -> bool {
   auto has_exits = @mutable false;

   fn visit_expr(@mutable bool flag, &@ast::expr e) {
       alt (e.node) {
           case (ast::expr_break(_)) { *flag = true; }
           case (ast::expr_cont(_)) { *flag = true; }
           case (_) { }
       }
   }

    auto v = rec(visit_expr_pre=bind visit_expr(has_exits, _)
                 with walk::default_visitor());
    walk::walk_block(v, b);
    ret *has_exits;
}

fn local_rhs_span(&@ast::local l, &ast::span def) -> ast::span {
    alt (l.init) {
        case (some(?i)) { ret i.expr.span; }
        case (_) { ret def; }
    }
}

fn respan[T](&span sp, &T t) -> spanned[T] {
    ret rec(node=t, span=sp);
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<commit_msg>Fix utility printers to flush eof in common.rs.<commit_after>import std::map;
import std::map::hashmap;
import std::uint;
import std::int;
import std::vec;
import std::option::none;
import std::option::some;
import front::ast;
import front::ast::ty;
import front::ast::pat;
import front::codemap::codemap;
import middle::walk;

import std::io::stdout;
import std::io::str_writer;
import std::io::string_writer;
import pretty::pprust::print_block;
import pretty::pprust::print_item;
import pretty::pprust::print_expr;
import pretty::pprust::print_decl;
import pretty::pprust::print_fn;
import pretty::pprust::print_type;
import pretty::pprust::mo_untyped;
import pretty::pp::mk_printer;

type filename = str;
type span = rec(uint lo, uint hi);
type spanned[T] = rec(T node, span span);
type flag = hashmap[str, ()];

tag ty_mach {
    ty_i8;
    ty_i16;
    ty_i32;
    ty_i64;

    ty_u8;
    ty_u16;
    ty_u32;
    ty_u64;

    ty_f32;
    ty_f64;
}

tag ty_or_bang[T] {
    a_ty(T);
    a_bang;
}

fn ty_mach_to_str(ty_mach tm) -> str {
    alt (tm) {
        case (ty_u8) { ret "u8"; }
        case (ty_u16) { ret "u16"; }
        case (ty_u32) { ret "u32"; }
        case (ty_u64) { ret "u64"; }

        case (ty_i8) { ret "i8"; }
        case (ty_i16) { ret "i16"; }
        case (ty_i32) { ret "i32"; }
        case (ty_i64) { ret "i64"; }

        case (ty_f32) { ret "f32"; }
        case (ty_f64) { ret "f64"; }
    }
}

fn new_str_hash[V]() -> std::map::hashmap[str,V] {
    let std::map::hashfn[str] hasher = std::str::hash;
    let std::map::eqfn[str] eqer = std::str::eq;
    ret std::map::mk_hashmap[str,V](hasher, eqer);
}

fn def_eq(&ast::def_id a, &ast::def_id b) -> bool {
    ret a._0 == b._0 && a._1 == b._1;
}

fn hash_def(&ast::def_id d) -> uint {
    auto h = 5381u;
    h = ((h << 5u) + h) ^ (d._0 as uint);
    h = ((h << 5u) + h) ^ (d._1 as uint);
    ret h;
}

fn new_def_hash[V]() -> std::map::hashmap[ast::def_id,V] {
    let std::map::hashfn[ast::def_id] hasher = hash_def;
    let std::map::eqfn[ast::def_id] eqer = def_eq;
    ret std::map::mk_hashmap[ast::def_id,V](hasher, eqer);
}

fn new_int_hash[V]() -> std::map::hashmap[int,V] {
    fn hash_int(&int x) -> uint { ret x as uint; }
    fn eq_int(&int a, &int b) -> bool { ret a == b; }
    auto hasher = hash_int;
    auto eqer = eq_int;
    ret std::map::mk_hashmap[int,V](hasher, eqer);
}

fn new_uint_hash[V]() -> std::map::hashmap[uint,V] {
    fn hash_uint(&uint x) -> uint { ret x; }
    fn eq_uint(&uint a, &uint b) -> bool { ret a == b; }
    auto hasher = hash_uint;
    auto eqer = eq_uint;
    ret std::map::mk_hashmap[uint,V](hasher, eqer);
}

fn istr(int i) -> str {
    ret int::to_str(i, 10u);
}

fn uistr(uint i) -> str {
    ret uint::to_str(i, 10u);
}

fn elt_expr(&ast::elt e) -> @ast::expr { ret e.expr; }

fn elt_exprs(&vec[ast::elt] elts) -> vec[@ast::expr] {
    auto f = elt_expr;
    ret vec::map[ast::elt, @ast::expr](f, elts);
}

fn field_expr(&ast::field f) -> @ast::expr { ret f.node.expr; }

fn field_exprs(vec[ast::field] fields) -> vec [@ast::expr] {
    auto f = field_expr;
    ret vec::map[ast::field, @ast::expr](f, fields);
}

fn expr_to_str(&@ast::expr e) -> str {
  let str_writer s = string_writer();
  auto out_ = mk_printer(s.get_writer(), 80u);
  auto out = @rec(s=out_,
                  cm=none[codemap],
                  comments=none[vec[front::lexer::cmnt]],
                  mutable cur_cmnt=0u,
                  mode=mo_untyped);
  print_expr(out, e);
  pretty::pp::eof(out_);
  ret s.get_str();
}

fn ty_to_str(&ty t) -> str {
  let str_writer s = string_writer();
  auto out_ = mk_printer(s.get_writer(), 80u);
  auto out = @rec(s=out_,
                  cm=none[codemap],
                  comments=none[vec[front::lexer::cmnt]],
                  mutable cur_cmnt=0u,
                  mode=mo_untyped);
  print_type(out, @t);
  pretty::pp::eof(out_);
  ret s.get_str();
}

fn log_expr(&ast::expr e) -> () {
    log(expr_to_str(@e));
}

fn log_expr_err(&ast::expr e) -> () {
    log_err(expr_to_str(@e));
}

fn log_ty_err(&ty t) -> () {
    log_err(ty_to_str(t));
}

fn log_pat_err(&@pat p) -> () {
    log_err(pretty::pprust::pat_to_str(p));
}

fn block_to_str(&ast::block b) -> str {
  let str_writer s = string_writer();
  auto out_ = mk_printer(s.get_writer(), 80u);
  auto out = @rec(s=out_,
                  cm=none[codemap],
                  comments=none[vec[front::lexer::cmnt]],
                  mutable cur_cmnt=0u,
                  mode=mo_untyped);

  print_block(out, b);
  pretty::pp::eof(out_);
  ret s.get_str();
}

fn item_to_str(&@ast::item i) -> str {
  let str_writer s = string_writer();
  auto out_ = mk_printer(s.get_writer(), 80u);
  auto out = @rec(s=out_,
                  cm=none[codemap],
                  comments=none[vec[front::lexer::cmnt]],
                  mutable cur_cmnt=0u,
                  mode=mo_untyped);
  print_item(out, i);
  pretty::pp::eof(out_);
  ret s.get_str();
}

fn log_block(&ast::block b) -> () {
    log(block_to_str(b));
}

fn log_block_err(&ast::block b) -> () {
    log_err(block_to_str(b));
}

fn log_item_err(&@ast::item i) -> () {
    log_err(item_to_str(i));
}

fn fun_to_str(&ast::_fn f, str name, vec[ast::ty_param] params) -> str {
 let str_writer s = string_writer();
  auto out_ = mk_printer(s.get_writer(), 80u);
  auto out = @rec(s=out_,
                  cm=none[codemap],
                  comments=none[vec[front::lexer::cmnt]],
                  mutable cur_cmnt=0u,
                  mode=mo_untyped);

  print_fn(out, f.decl, name, params);
  pretty::pp::eof(out_);
  ret s.get_str();
}

fn log_fn(&ast::_fn f, str name, vec[ast::ty_param] params) -> () {
    log(fun_to_str(f, name, params));
}

fn log_fn_err(&ast::_fn f, str name, vec[ast::ty_param] params) -> () {
    log_err(fun_to_str(f, name, params));
}

fn stmt_to_str(&ast::stmt st) -> str {
  let str_writer s = string_writer();
  auto out_ = mk_printer(s.get_writer(), 80u);
  auto out = @rec(s=out_,
                  cm=none[codemap],
                  comments=none[vec[front::lexer::cmnt]],
                  mutable cur_cmnt=0u,
                  mode=mo_untyped);
  alt (st.node) {
    case (ast::stmt_decl(?decl,_)) {
      print_decl(out, decl);
    }
    case (ast::stmt_expr(?ex,_)) {
      print_expr(out, ex);
    }
    case (_) { /* do nothing */ }
  }
  pretty::pp::eof(out_);
  ret s.get_str();
}

fn log_stmt(&ast::stmt st) -> () {
    log(stmt_to_str(st));
}

fn log_stmt_err(&ast::stmt st) -> () {
    log_err(stmt_to_str(st));
}

fn decl_lhs(@ast::decl d) -> ast::def_id {
    alt (d.node) {
        case (ast::decl_local(?l)) {
            ret l.id;
        }
        case (ast::decl_item(?an_item)) {
            alt (an_item.node) {
                case (ast::item_const(_,_,_,?d,_)) {
                    ret d;
                }
                case (ast::item_fn(_,_,_,?d,_)) {
                    ret d;
                }
                case (ast::item_mod(_,_,?d)) {
                    ret d;
                }
                case (ast::item_native_mod(_,_,?d)) {
                    ret d;
                }
                case (ast::item_ty(_,_,_,?d,_)) {
                    ret d;
                }
                case (ast::item_tag(_,_,_,?d,_)) {
                    ret d;
                }
                case (ast::item_obj(_,_,_,?d,_)) {
                    ret d.ctor; /* This doesn't really make sense */
                }
            }
        }
    }
}

fn has_nonlocal_exits(&ast::block b) -> bool {
   auto has_exits = @mutable false;

   fn visit_expr(@mutable bool flag, &@ast::expr e) {
       alt (e.node) {
           case (ast::expr_break(_)) { *flag = true; }
           case (ast::expr_cont(_)) { *flag = true; }
           case (_) { }
       }
   }

    auto v = rec(visit_expr_pre=bind visit_expr(has_exits, _)
                 with walk::default_visitor());
    walk::walk_block(v, b);
    ret *has_exits;
}

fn local_rhs_span(&@ast::local l, &ast::span def) -> ast::span {
    alt (l.init) {
        case (some(?i)) { ret i.expr.span; }
        case (_) { ret def; }
    }
}

fn respan[T](&span sp, &T t) -> spanned[T] {
    ret rec(node=t, span=sp);
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//
<|endoftext|>