<commit_before>use crate::gen::namespace_organizer::NamespaceEntries;
use crate::gen::out::OutFile;
use crate::gen::{builtin, include, Opt};
use crate::syntax::atom::Atom::{self, *};
use crate::syntax::symbol::Symbol;
use crate::syntax::{
    mangle, Api, CppName, Enum, ExternFn, ExternType, ResolvableName, Signature, Struct, Type,
    Types, Var,
};
use proc_macro2::Ident;
use std::collections::HashMap;

pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) -> OutFile<'a> {
    let mut out_file = OutFile::new(header, types);
    let out = &mut out_file;

    if header {
        writeln!(out.include, "#pragma once");
    }

    pick_includes_and_builtins(out, apis);
    out.include.extend(&opt.include);

    let apis_by_namespace = NamespaceEntries::new(apis);

    gen_namespace_forward_declarations(out, &apis_by_namespace);
    gen_namespace_contents(out, &apis_by_namespace, opt);

    if !header {
        out.next_section();
        write_generic_instantiations(out);
    }

    builtin::write(out);
    include::write(out);

    out_file
}

fn gen_namespace_forward_declarations(out: &mut OutFile, ns_entries: &NamespaceEntries) {
    let apis = ns_entries.entries();

    out.next_section();
    for api in apis {
        match api {
            Api::Include(include) => out.include.insert(include),
            Api::Struct(strct) => write_struct_decl(out, &strct.ident.cxx.ident),
            Api::CxxType(ety) => write_struct_using(out, &ety.ident.cxx),
            Api::RustType(ety) => write_struct_decl(out, &ety.ident.cxx.ident),
            _ => {}
        }
    }

    out.next_section();

    for (child_ns, child_ns_entries) in ns_entries.children() {
        writeln!(out, "namespace {} {{", child_ns);
        gen_namespace_forward_declarations(out, child_ns_entries);
        writeln!(out, "}} // namespace {}", child_ns);
    }
}

fn gen_namespace_contents(out: &mut OutFile, ns_entries: &NamespaceEntries, opt: &Opt) {
    let apis = ns_entries.entries();

    let mut methods_for_type = HashMap::new();
    for api in apis {
        if let Api::RustFunction(efn) = api {
            if let Some(receiver) = &efn.sig.receiver {
                methods_for_type
                    .entry(&receiver.ty.rust)
                    .or_insert_with(Vec::new)
                    .push(efn);
            }
        }
    }

    for api in apis {
        match api {
            Api::Struct(strct) => {
                out.next_section();
                if !out.types.cxx.contains(&strct.ident.rust) {
                    write_struct(out, strct);
                }
            }
            Api::Enum(enm) => {
                out.next_section();
                if out.types.cxx.contains(&enm.ident.rust) {
                    check_enum(out, enm);
                } else {
                    write_enum(out, enm);
                }
            }
            Api::RustType(ety) => {
                if let Some(methods) = methods_for_type.get(&ety.ident.rust) {
                    out.next_section();
                    write_struct_with_methods(out, ety, methods);
                }
            }
            _ => {}
        }
    }

    out.next_section();
    for api in apis {
        if let Api::TypeAlias(ety) = api {
            if out.types.required_trivial.contains_key(&ety.ident.rust) {
                check_trivial_extern_type(out, &ety.ident.cxx)
            }
        }
    }

    if !out.header {
        out.begin_block("extern \"C\"");
        write_exception_glue(out, apis);
        for api in apis {
            let (efn, write): (_, fn(_, _, _)) = match api {
                Api::CxxFunction(efn) => (efn, write_cxx_function_shim),
                Api::RustFunction(efn) => (efn, write_rust_function_decl),
                _ => continue,
            };
            out.next_section();
            write(out, efn, &opt.cxx_impl_annotations);
        }
        out.end_block("extern \"C\"");
    }

    for api in apis {
        if let Api::RustFunction(efn) = api {
            out.next_section();
            write_rust_function_shim(out, efn);
        }
    }

    out.next_section();

    for (child_ns, child_ns_entries) in ns_entries.children() {
        writeln!(out, "namespace {} {{", child_ns);
        gen_namespace_contents(out, child_ns_entries, opt);
        writeln!(out, "}} // namespace {}", child_ns);
    }
}

fn pick_includes_and_builtins(out: &mut OutFile, apis: &[Api]) {
    for ty in out.types {
        match ty {
            Type::Ident(ident) => match Atom::from(&ident.rust) {
                Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(I8) | Some(I16) | Some(I32)
                | Some(I64) => out.include.cstdint = true,
                Some(Usize) => out.include.cstddef = true,
                Some(Isize) => {
                    out.include.basetsd = true;
                    out.builtin.rust_isize = true;
                }
                Some(CxxString) => out.include.string = true,
                Some(RustString) => {
                    out.include.array = true;
                    out.include.cstdint = true;
                    out.include.string = true;
                    out.builtin.rust_string = true;
                }
                Some(Bool) | Some(F32) | Some(F64) | None => {}
            },
            Type::RustBox(_) => {
                out.include.new = true;
                out.include.type_traits = true;
                out.builtin.rust_box = true;
            }
            Type::RustVec(_) => {
                out.include.array = true;
                out.include.new = true;
                out.include.type_traits = true;
                out.builtin.panic = true;
                out.builtin.rust_vec = true;
                out.builtin.unsafe_bitcopy = true;
            }
            Type::UniquePtr(_) => out.include.memory = true,
            Type::Str(_) => {
                out.include.cstdint = true;
                out.include.string = true;
                out.builtin.rust_str = true;
            }
            Type::CxxVector(_) => out.include.vector = true,
            Type::Fn(_) => {
                out.builtin.rust_fn = true;
            }
            Type::Slice(_) => {
                out.builtin.rust_slice = true;
            }
            Type::SliceRefU8(_) => {
                out.include.cstdint = true;
                out.builtin.rust_slice = true;
            }
            _ => {}
        }
    }

    for api in apis {
        match api {
            Api::CxxFunction(efn) if !out.header => {
                if efn.throws {
                    out.builtin.trycatch = true;
                } else if let Some(Type::Str(_)) = efn.ret {
                    out.builtin.rust_str_repr = true;
                }
                for arg in &efn.args {
                    match arg.ty {
                        Type::Str(_) => out.builtin.rust_str_new_unchecked = true,
                        Type::RustVec(_) => out.builtin.unsafe_bitcopy = true,
                        _ => out.builtin.unsafe_bitcopy |= arg.ty == RustString,
                    }
                }
            }
            Api::RustFunction(efn) if !out.header => {
                if efn.throws {
                    out.include.exception = true;
                    out.include.string = true;
                    out.builtin.rust_str = true;
                    out.builtin.rust_error = true;
                    out.builtin.maybe_uninit = true;
                }
                for arg in &efn.args {
                    if arg.ty != RustString && out.types.needs_indirect_abi(&arg.ty) {
                        out.builtin.manually_drop = true;
                    }
                    if let Type::Str(_) = arg.ty {
                        out.builtin.rust_str_repr = true;
                    }
                }
                if let Some(ret) = &efn.ret {
                    if out.types.needs_indirect_abi(ret) {
                        out.builtin.maybe_uninit = true;
                    } else if let Type::Str(_) = ret {
                        out.builtin.rust_str_new_unchecked = true;
                    }
                }
            }
            _ => {}
        }
    }
}

fn write_struct(out: &mut OutFile, strct: &Struct) {
    let guard = format!("CXXBRIDGE05_STRUCT_{}", strct.ident.cxx.to_symbol());
    writeln!(out, "#ifndef {}", guard);
    writeln!(out, "#define {}", guard);
    for line in strct.doc.to_string().lines() {
        writeln!(out, "//{}", line);
    }
    writeln!(out, "struct {} final {{", strct.ident.cxx.ident);
    for field in &strct.fields {
        write!(out, "  ");
        write_type_space(out, &field.ty);
        writeln!(out, "{};", field.ident);
    }
    writeln!(out, "}};");
    writeln!(out, "#endif // {}", guard);
}

fn write_struct_decl(out: &mut OutFile, ident: &Ident) {
    writeln!(out, "struct {};", ident);
}

fn write_struct_using(out: &mut OutFile, ident: &CppName) {
    writeln!(
        out,
        "using {} = {};",
        ident.ident,
        ident.to_fully_qualified()
    );
}

fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&ExternFn]) {
    let guard = format!("CXXBRIDGE05_STRUCT_{}", ety.ident.cxx.to_symbol());
    writeln!(out, "#ifndef {}", guard);
    writeln!(out, "#define {}", guard);
    for line in ety.doc.to_string().lines() {
        writeln!(out, "//{}", line);
    }
    writeln!(out, "struct {} final {{", ety.ident.cxx.ident);
    writeln!(out, "  {}() = delete;", ety.ident.cxx.ident);
    writeln!(
        out,
        "  {}(const {} &) = delete;",
        ety.ident.cxx.ident, ety.ident.cxx.ident
    );
    for method in methods {
        write!(out, "  ");
        let sig = &method.sig;
        let local_name = method.ident.cxx.ident.to_string();
        write_rust_function_shim_decl(out, &local_name, sig, false);
        writeln!(out, ";");
    }
    writeln!(out, "}};");
    writeln!(out, "#endif // {}", guard);
}

fn write_enum(out: &mut OutFile, enm: &Enum) {
    let guard = format!("CXXBRIDGE05_ENUM_{}", enm.ident.cxx.to_symbol());
    writeln!(out, "#ifndef {}", guard);
    writeln!(out, "#define {}", guard);
    for line in enm.doc.to_string().lines() {
        writeln!(out, "//{}", line);
    }
    write!(out, "enum class {} : ", enm.ident.cxx.ident);
    write_atom(out, enm.repr);
    writeln!(out, " {{");
    for variant in &enm.variants {
        writeln!(out, "  {} = {},", variant.ident, variant.discriminant);
    }
    writeln!(out, "}};");
    writeln!(out, "#endif // {}", guard);
}

fn check_enum(out: &mut OutFile, enm: &Enum) {
    write!(
        out,
        "static_assert(sizeof({}) == sizeof(",
        enm.ident.cxx.ident
    );
    write_atom(out, enm.repr);
    writeln!(out, "), \"incorrect size\");");
    for variant in &enm.variants {
        write!(out, "static_assert(static_cast<");
        write_atom(out, enm.repr);
        writeln!(
            out,
            ">({}::{}) == {}, \"disagrees with the value in #[cxx::bridge]\");",
            enm.ident.cxx.ident, variant.ident, variant.discriminant,
        );
    }
}

fn check_trivial_extern_type(out: &mut OutFile, id: &CppName) {
    // NOTE: The following two static assertions are just nice-to-have and not
    // necessary for soundness. That's because triviality is always declared by
    // the user in the form of an unsafe impl of cxx::ExternType:
    //
    //     unsafe impl ExternType for MyType {
    //         type Id = cxx::type_id!("...");
    //         type Kind = cxx::kind::Trivial;
    //     }
    //
    // Since the user went on the record with their unsafe impl to unsafely
    // claim they KNOW that the type is trivial, it's fine for that to be on
    // them if that were wrong.
    //
    // There may be a legitimate reason we'll want to remove these assertions
    // for support of types that the programmer knows are Rust-movable despite
    // not being recognized as such by the C++ type system due to a move
    // constructor or destructor.

    let id = &id.to_fully_qualified();
    out.include.type_traits = true;
    writeln!(out, "static_assert(");
    writeln!(
        out,
        "    ::std::is_trivially_move_constructible<{}>::value,",
        id,
    );
    writeln!(
        out,
        "    \"type {} marked as Trivial in Rust is not trivially move constructible in C++\");",
        id,
    );
    writeln!(out, "static_assert(");
    writeln!(out, "    ::std::is_trivially_destructible<{}>::value,", id);
    writeln!(
        out,
        "    \"type {} marked as Trivial in Rust is not trivially destructible in C++\");",
        id,
    );
}

fn write_exception_glue(out: &mut OutFile, apis: &[&Api]) {
    let mut has_cxx_throws = false;
    for api in apis {
        if let Api::CxxFunction(efn) = api {
            if efn.throws {
                has_cxx_throws = true;
                break;
            }
        }
    }

    if has_cxx_throws {
        out.next_section();
        writeln!(
            out,
            "const char *cxxbridge05$exception(const char *, size_t);",
        );
    }
}

fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations: &Option<String>) {
    if let Some(annotation) = impl_annotations {
        write!(out, "{} ", annotation);
    }
    if efn.throws {
        write!(out, "::rust::repr::PtrLen ");
    } else {
        write_extern_return_type_space(out, &efn.ret);
    }
    let mangled = mangle::extern_fn(efn, out.types);
    write!(out, "{}(", mangled);
    if let Some(receiver) = &efn.receiver {
        if receiver.mutability.is_none() {
            write!(out, "const ");
        }
        write!(
            out,
            "{} &self",
            out.types.resolve(&receiver.ty).to_fully_qualified()
        );
    }
    for (i, arg) in efn.args.iter().enumerate() {
        if i > 0 || efn.receiver.is_some() {
            write!(out, ", ");
        }
        if arg.ty == RustString {
            write!(out, "const ");
        } else if let Type::RustVec(_) = arg.ty {
            write!(out, "const ");
        }
        write_extern_arg(out, arg);
    }
    let indirect_return = indirect_return(efn, out.types);
    if indirect_return {
        if !efn.args.is_empty() || efn.receiver.is_some() {
            write!(out, ", ");
        }
        write_indirect_return_type_space(out, efn.ret.as_ref().unwrap());
        write!(out, "*return$");
    }
    writeln!(out, ") noexcept {{");
    write!(out, "  ");
    write_return_type(out, &efn.ret);
    match &efn.receiver {
        None => write!(out, "(*{}$)(", efn.ident.rust),
        Some(receiver) => write!(
            out,
            "({}::*{}$)(",
            out.types.resolve(&receiver.ty).to_fully_qualified(),
            efn.ident.rust
        ),
    }
    for (i, arg) in efn.args.iter().enumerate() {
        if i > 0 {
            write!(out, ", ");
        }
        write_type(out, &arg.ty);
    }
    write!(out, ")");
    if let Some(receiver) = &efn.receiver {
        if receiver.mutability.is_none() {
            write!(out, " const");
        }
    }
    write!(out, " = ");
    match &efn.receiver {
        None => write!(out, "{}", efn.ident.cxx.to_fully_qualified()),
        Some(receiver) => write!(
            out,
            "&{}::{}",
            out.types.resolve(&receiver.ty).to_fully_qualified(),
            efn.ident.cxx.ident
        ),
    }
    writeln!(out, ";");
    write!(out, "  ");
    if efn.throws {
        writeln!(out, "::rust::repr::PtrLen throw$;");
        writeln!(out, "  ::rust::behavior::trycatch(");
        writeln!(out, "      [&] {{");
        write!(out, "        ");
    }
    if indirect_return {
        out.include.new = true;
        write!(out, "new (return$) ");
        write_indirect_return_type(out, efn.ret.as_ref().unwrap());
        write!(out, "(");
    } else if efn.ret.is_some() {
        write!(out, "return ");
    }
    match &efn.ret {
        Some(Type::Ref(_)) => write!(out, "&"),
        Some(Type::Str(_)) if !indirect_return => write!(out, "::rust::impl<::rust::Str>::repr("),
        Some(Type::SliceRefU8(_)) if !indirect_return => {
            write!(out, "::rust::Slice<uint8_t>::Repr(")
        }
        _ => {}
    }
    match &efn.receiver {
        None => write!(out, "{}$(", efn.ident.rust),
        Some(_) => write!(out, "(self.*{}$)(", efn.ident.rust),
    }
    for (i, arg) in efn.args.iter().enumerate() {
        if i > 0 {
            write!(out, ", ");
        }
        if let Type::RustBox(_) = &arg.ty {
            write_type(out, &arg.ty);
            write!(out, "::from_raw({})", arg.ident);
        } else if let Type::UniquePtr(_) = &arg.ty {
            write_type(out, &arg.ty);
            write!(out, "({})", arg.ident);
        } else if let Type::Str(_) = arg.ty {
            write!(
                out,
                "::rust::impl<::rust::Str>::new_unchecked({})",
                arg.ident,
            );
        } else if arg.ty == RustString {
            write!(
                out,
                "::rust::String(::rust::unsafe_bitcopy, *{})",
                arg.ident,
            );
        } else if let Type::RustVec(_) = arg.ty {
            write_type(out, &arg.ty);
            write!(out, "(::rust::unsafe_bitcopy, *{})", arg.ident);
        } else if out.types.needs_indirect_abi(&arg.ty) {
            out.include.utility = true;
            write!(out, "::std::move(*{})", arg.ident);
        } else {
            write!(out, "{}", arg.ident);
        }
    }
    write!(out, ")");
    match &efn.ret {
        Some(Type::RustBox(_)) => write!(out, ".into_raw()"),
        Some(Type::UniquePtr(_)) => write!(out, ".release()"),
        Some(Type::Str(_)) | Some(Type::SliceRefU8(_)) if !indirect_return => write!(out, ")"),
        _ => {}
    }
    if indirect_return {
        write!(out, ")");
    }
    writeln!(out, ";");
    if efn.throws {
        out.include.cstring = true;
        writeln!(out, "        throw$.ptr = nullptr;");
        writeln!(out, "      }},");
        writeln!(out, "      [&](const char *catch$) noexcept {{");
        writeln!(out, "        throw$.len = ::std::strlen(catch$);");
        writeln!(
            out,
            "        throw$.ptr = cxxbridge05$exception(catch$, throw$.len);",
        );
        writeln!(out, "      }});");
        writeln!(out, "  return throw$;");
    }
    writeln!(out, "}}");
    for arg in &efn.args {
        if let Type::Fn(f) = &arg.ty {
            let var = &arg.ident;
            write_function_pointer_trampoline(out, efn, var, f);
        }
    }
}

fn write_function_pointer_trampoline(
    out: &mut OutFile,
    efn: &ExternFn,
    var: &Ident,
    f: &Signature,
) {
    out.next_section();
    let r_trampoline = mangle::r_trampoline(efn, var, out.types);
    let indirect_call = true;
    write_rust_function_decl_impl(out, &r_trampoline, f, indirect_call);

    out.next_section();
    let c_trampoline = mangle::c_trampoline(efn, var, out.types).to_string();
    write_rust_function_shim_impl(out, &c_trampoline, f, &r_trampoline, indirect_call);
}

fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, _: &Option<String>) {
    let link_name = mangle::extern_fn(efn, out.types);
    let indirect_call = false;
    write_rust_function_decl_impl(out, &link_name, efn, indirect_call);
}

fn write_rust_function_decl_impl(
    out: &mut OutFile,
    link_name: &Symbol,
    sig: &Signature,
    indirect_call: bool,
) {
    if sig.throws {
        write!(out, "::rust::repr::PtrLen ");
    } else {
        write_extern_return_type_space(out, &sig.ret);
    }
    write!(out, "{}(", link_name);
    let mut needs_comma = false;
    if let Some(receiver) = &sig.receiver {
        if receiver.mutability.is_none() {
            write!(out, "const ");
        }
        write!(
            out,
            "{} &self",
            out.types.resolve(&receiver.ty).to_fully_qualified()
        );
        needs_comma = true;
    }
    for arg in &sig.args {
        if needs_comma {
            write!(out, ", ");
        }
        write_extern_arg(out, arg);
        needs_comma = true;
    }
    if indirect_return(sig, out.types) {
        if needs_comma {
            write!(out, ", ");
        }
        write_return_type(out, &sig.ret);
        write!(out, "*return$");
        needs_comma = true;
    }
    if indirect_call {
        if needs_comma {
            write!(out, ", ");
        }
        write!(out, "void *");
    }
    writeln!(out, ") noexcept;");
}

fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn) {
    for line in efn.doc.to_string().lines() {
        writeln!(out, "//{}", line);
    }
    let local_name = match &efn.sig.receiver {
        None => efn.ident.cxx.ident.to_string(),
        Some(receiver) => format!(
            "{}::{}",
            out.types.resolve(&receiver.ty).ident,
            efn.ident.cxx.ident
        ),
    };
    let invoke = mangle::extern_fn(efn, out.types);
    let indirect_call = false;
    write_rust_function_shim_impl(out, &local_name, efn, &invoke, indirect_call);
}

fn write_rust_function_shim_decl(
    out: &mut OutFile,
    local_name: &str,
    sig: &Signature,
    indirect_call: bool,
) {
    write_return_type(out, &sig.ret);
    write!(out, "{}(", local_name);
    for (i, arg) in sig.args.iter().enumerate() {
        if i > 0 {
            write!(out, ", ");
        }
        write_type_space(out, &arg.ty);
        write!(out, "{}", arg.ident);
    }
    if indirect_call {
        if !sig.args.is_empty() {
            write!(out, ", ");
        }
        write!(out, "void *extern$");
    }
    write!(out, ")");
    if let Some(receiver) = &sig.receiver {
        if receiver.mutability.is_none() {
            write!(out, " const");
        }
    }
    if !sig.throws {
        write!(out, " noexcept");
    }
}

fn write_rust_function_shim_impl(
    out: &mut OutFile,
    local_name: &str,
    sig: &Signature,
    invoke: &Symbol,
    indirect_call: bool,
) {
    if out.header && sig.receiver.is_some() {
        // We've already defined this inside the struct.
        return;
    }
    write_rust_function_shim_decl(out, local_name, sig, indirect_call);
    if out.header {
        writeln!(out, ";");
        return;
    }
    writeln!(out, " {{");
    for arg in &sig.args {
        if arg.ty != RustString && out.types.needs_indirect_abi(&arg.ty) {
            out.include.utility = true;
            write!(out, "  ::rust::ManuallyDrop<");
            write_type(out, &arg.ty);
            writeln!(out, "> {}$(::std::move({0}));", arg.ident);
        }
    }
    write!(out, "  ");
    let indirect_return = indirect_return(sig, out.types);
    if indirect_return {
        write!(out, "::rust::MaybeUninit<");
        write_type(out, sig.ret.as_ref().unwrap());
        writeln!(out, "> return$;");
        write!(out, "  ");
    } else if let Some(ret) = &sig.ret {
        write!(out, "return ");
        match ret {
            Type::RustBox(_) => {
                write_type(out, ret);
                write!(out, "::from_raw(");
            }
            Type::UniquePtr(_) => {
                write_type(out, ret);
                write!(out, "(");
            }
            Type::Ref(_) => write!(out, "*"),
            Type::Str(_) => write!(out, "::rust::impl<::rust::Str>::new_unchecked("),
            _ => {}
        }
    }
    if sig.throws {
        write!(out, "::rust::repr::PtrLen error$ = ");
    }
    write!(out, "{}(", invoke);
    if sig.receiver.is_some() {
        write!(out, "*this");
    }
    for (i, arg) in sig.args.iter().enumerate() {
        if i > 0 || sig.receiver.is_some() {
            write!(out, ", ");
        }
        match &arg.ty {
            Type::Str(_) => write!(out, "::rust::impl<::rust::Str>::repr("),
            Type::SliceRefU8(_) => write!(out, "::rust::Slice<uint8_t>::Repr("),
            ty if out.types.needs_indirect_abi(ty) => write!(out, "&"),
            _ => {}
        }
        write!(out, "{}", arg.ident);
        match &arg.ty {
            Type::RustBox(_) => write!(out, ".into_raw()"),
            Type::UniquePtr(_) => write!(out, ".release()"),
            Type::Str(_) | Type::SliceRefU8(_) => write!(out, ")"),
            ty if ty != RustString && out.types.needs_indirect_abi(ty) => write!(out, "$.value"),
            _ => {}
        }
    }
    if indirect_return {
        if !sig.args.is_empty() {
            write!(out, ", ");
        }
        write!(out, "&return$.value");
    }
    if indirect_call {
        if !sig.args.is_empty() || indirect_return {
            write!(out, ", ");
        }
        write!(out, "extern$");
    }
    write!(out, ")");
    if !indirect_return {
        if let Some(ret) = &sig.ret {
            if let Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) = ret {
                write!(out, ")");
            }
        }
    }
    writeln!(out, ";");
    if sig.throws {
        writeln!(out, "  if (error$.ptr) {{");
        writeln!(out, "    throw ::rust::impl<::rust::Error>::error(error$);");
        writeln!(out, "  }}");
    }
    if indirect_return {
        out.include.utility = true;
        writeln!(out, "  return ::std::move(return$.value);");
    }
    writeln!(out, "}}");
}

fn write_return_type(out: &mut OutFile, ty: &Option<Type>) {
    match ty {
        None => write!(out, "void "),
        Some(ty) => write_type_space(out, ty),
    }
}

fn indirect_return(sig: &Signature, types: &Types) -> bool {
    sig.ret
        .as_ref()<commit_msg>Select builtins lazily during code generation<commit_after>use crate::gen::namespace_organizer::NamespaceEntries;
use crate::gen::out::OutFile;
use crate::gen::{builtin, include, Opt};
use crate::syntax::atom::Atom::{self, *};
use crate::syntax::symbol::Symbol;
use crate::syntax::{
    mangle, Api, CppName, Enum, ExternFn, ExternType, ResolvableName, Signature, Struct, Type,
    Types, Var,
};
use proc_macro2::Ident;
use std::collections::HashMap;

pub(super) fn gen<'a>(apis: &[Api], types: &'a Types, opt: &Opt, header: bool) -> OutFile<'a> {
    let mut out_file = OutFile::new(header, types);
    let out = &mut out_file;

    if header {
        writeln!(out.include, "#pragma once");
    }

    pick_includes_and_builtins(out);
    out.include.extend(&opt.include);

    let apis_by_namespace = NamespaceEntries::new(apis);

    gen_namespace_forward_declarations(out, &apis_by_namespace);
    gen_namespace_contents(out, &apis_by_namespace, opt);

    if !header {
        out.next_section();
        write_generic_instantiations(out);
    }

    builtin::write(out);
    include::write(out);

    out_file
}

fn gen_namespace_forward_declarations(out: &mut OutFile, ns_entries: &NamespaceEntries) {
    let apis = ns_entries.entries();

    out.next_section();
    for api in apis {
        match api {
            Api::Include(include) => out.include.insert(include),
            Api::Struct(strct) => write_struct_decl(out, &strct.ident.cxx.ident),
            Api::CxxType(ety) => write_struct_using(out, &ety.ident.cxx),
            Api::RustType(ety) => write_struct_decl(out, &ety.ident.cxx.ident),
            _ => {}
        }
    }

    out.next_section();

    for (child_ns, child_ns_entries) in ns_entries.children() {
        writeln!(out, "namespace {} {{", child_ns);
        gen_namespace_forward_declarations(out, child_ns_entries);
        writeln!(out, "}} // namespace {}", child_ns);
    }
}

fn gen_namespace_contents(out: &mut OutFile, ns_entries: &NamespaceEntries, opt: &Opt) {
    let apis = ns_entries.entries();

    let mut methods_for_type = HashMap::new();
    for api in apis {
        if let Api::RustFunction(efn) = api {
            if let Some(receiver) = &efn.sig.receiver {
                methods_for_type
                    .entry(&receiver.ty.rust)
                    .or_insert_with(Vec::new)
                    .push(efn);
            }
        }
    }

    for api in apis {
        match api {
            Api::Struct(strct) => {
                out.next_section();
                if !out.types.cxx.contains(&strct.ident.rust) {
                    write_struct(out, strct);
                }
            }
            Api::Enum(enm) => {
                out.next_section();
                if out.types.cxx.contains(&enm.ident.rust) {
                    check_enum(out, enm);
                } else {
                    write_enum(out, enm);
                }
            }
            Api::RustType(ety) => {
                if let Some(methods) = methods_for_type.get(&ety.ident.rust) {
                    out.next_section();
                    write_struct_with_methods(out, ety, methods);
                }
            }
            _ => {}
        }
    }

    out.next_section();
    for api in apis {
        if let Api::TypeAlias(ety) = api {
            if out.types.required_trivial.contains_key(&ety.ident.rust) {
                check_trivial_extern_type(out, &ety.ident.cxx)
            }
        }
    }

    if !out.header {
        out.begin_block("extern \"C\"");
        write_exception_glue(out, apis);
        for api in apis {
            let (efn, write): (_, fn(_, _, _)) = match api {
                Api::CxxFunction(efn) => (efn, write_cxx_function_shim),
                Api::RustFunction(efn) => (efn, write_rust_function_decl),
                _ => continue,
            };
            out.next_section();
            write(out, efn, &opt.cxx_impl_annotations);
        }
        out.end_block("extern \"C\"");
    }

    for api in apis {
        if let Api::RustFunction(efn) = api {
            out.next_section();
            write_rust_function_shim(out, efn);
        }
    }

    out.next_section();

    for (child_ns, child_ns_entries) in ns_entries.children() {
        writeln!(out, "namespace {} {{", child_ns);
        gen_namespace_contents(out, child_ns_entries, opt);
        writeln!(out, "}} // namespace {}", child_ns);
    }
}

fn pick_includes_and_builtins(out: &mut OutFile) {
    for ty in out.types {
        match ty {
            Type::Ident(ident) => match Atom::from(&ident.rust) {
                Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(I8) | Some(I16) | Some(I32)
                | Some(I64) => out.include.cstdint = true,
                Some(Usize) => out.include.cstddef = true,
                Some(Isize) => {
                    out.include.basetsd = true;
                    out.builtin.rust_isize = true;
                }
                Some(CxxString) => out.include.string = true,
                Some(RustString) => {
                    out.include.array = true;
                    out.include.cstdint = true;
                    out.include.string = true;
                    out.builtin.rust_string = true;
                }
                Some(Bool) | Some(F32) | Some(F64) | None => {}
            },
            Type::RustBox(_) => {
                out.include.new = true;
                out.include.type_traits = true;
                out.builtin.rust_box = true;
            }
            Type::RustVec(_) => {
                out.include.array = true;
                out.include.new = true;
                out.include.type_traits = true;
                out.builtin.panic = true;
                out.builtin.rust_vec = true;
                out.builtin.unsafe_bitcopy = true;
            }
            Type::UniquePtr(_) => out.include.memory = true,
            Type::Str(_) => {
                out.include.cstdint = true;
                out.include.string = true;
                out.builtin.rust_str = true;
            }
            Type::CxxVector(_) => out.include.vector = true,
            Type::Fn(_) => {
                out.builtin.rust_fn = true;
            }
            Type::Slice(_) => {
                out.builtin.rust_slice = true;
            }
            Type::SliceRefU8(_) => {
                out.include.cstdint = true;
                out.builtin.rust_slice = true;
            }
            _ => {}
        }
    }
}

fn write_struct(out: &mut OutFile, strct: &Struct) {
    let guard = format!("CXXBRIDGE05_STRUCT_{}", strct.ident.cxx.to_symbol());
    writeln!(out, "#ifndef {}", guard);
    writeln!(out, "#define {}", guard);
    for line in strct.doc.to_string().lines() {
        writeln!(out, "//{}", line);
    }
    writeln!(out, "struct {} final {{", strct.ident.cxx.ident);
    for field in &strct.fields {
        write!(out, "  ");
        write_type_space(out, &field.ty);
        writeln!(out, "{};", field.ident);
    }
    writeln!(out, "}};");
    writeln!(out, "#endif // {}", guard);
}

fn write_struct_decl(out: &mut OutFile, ident: &Ident) {
    writeln!(out, "struct {};", ident);
}

fn write_struct_using(out: &mut OutFile, ident: &CppName) {
    writeln!(
        out,
        "using {} = {};",
        ident.ident,
        ident.to_fully_qualified()
    );
}

fn write_struct_with_methods(out: &mut OutFile, ety: &ExternType, methods: &[&ExternFn]) {
    let guard = format!("CXXBRIDGE05_STRUCT_{}", ety.ident.cxx.to_symbol());
    writeln!(out, "#ifndef {}", guard);
    writeln!(out, "#define {}", guard);
    for line in ety.doc.to_string().lines() {
        writeln!(out, "//{}", line);
    }
    writeln!(out, "struct {} final {{", ety.ident.cxx.ident);
    writeln!(out, "  {}() = delete;", ety.ident.cxx.ident);
    writeln!(
        out,
        "  {}(const {} &) = delete;",
        ety.ident.cxx.ident, ety.ident.cxx.ident
    );
    for method in methods {
        write!(out, "  ");
        let sig = &method.sig;
        let local_name = method.ident.cxx.ident.to_string();
        write_rust_function_shim_decl(out, &local_name, sig, false);
        writeln!(out, ";");
    }
    writeln!(out, "}};");
    writeln!(out, "#endif // {}", guard);
}

fn write_enum(out: &mut OutFile, enm: &Enum) {
    let guard = format!("CXXBRIDGE05_ENUM_{}", enm.ident.cxx.to_symbol());
    writeln!(out, "#ifndef {}", guard);
    writeln!(out, "#define {}", guard);
    for line in enm.doc.to_string().lines() {
        writeln!(out, "//{}", line);
    }
    write!(out, "enum class {} : ", enm.ident.cxx.ident);
    write_atom(out, enm.repr);
    writeln!(out, " {{");
    for variant in &enm.variants {
        writeln!(out, "  {} = {},", variant.ident, variant.discriminant);
    }
    writeln!(out, "}};");
    writeln!(out, "#endif // {}", guard);
}

fn check_enum(out: &mut OutFile, enm: &Enum) {
    write!(
        out,
        "static_assert(sizeof({}) == sizeof(",
        enm.ident.cxx.ident
    );
    write_atom(out, enm.repr);
    writeln!(out, "), \"incorrect size\");");
    for variant in &enm.variants {
        write!(out, "static_assert(static_cast<");
        write_atom(out, enm.repr);
        writeln!(
            out,
            ">({}::{}) == {}, \"disagrees with the value in #[cxx::bridge]\");",
            enm.ident.cxx.ident, variant.ident, variant.discriminant,
        );
    }
}

fn check_trivial_extern_type(out: &mut OutFile, id: &CppName) {
    // NOTE: The following two static assertions are just nice-to-have and not
    // necessary for soundness. That's because triviality is always declared by
    // the user in the form of an unsafe impl of cxx::ExternType:
    //
    //     unsafe impl ExternType for MyType {
    //         type Id = cxx::type_id!("...");
    //         type Kind = cxx::kind::Trivial;
    //     }
    //
    // Since the user went on the record with their unsafe impl to unsafely
    // claim they KNOW that the type is trivial, it's fine for that to be on
    // them if that were wrong.
    //
    // There may be a legitimate reason we'll want to remove these assertions
    // for support of types that the programmer knows are Rust-movable despite
    // not being recognized as such by the C++ type system due to a move
    // constructor or destructor.

    let id = &id.to_fully_qualified();
    out.include.type_traits = true;
    writeln!(out, "static_assert(");
    writeln!(
        out,
        "    ::std::is_trivially_move_constructible<{}>::value,",
        id,
    );
    writeln!(
        out,
        "    \"type {} marked as Trivial in Rust is not trivially move constructible in C++\");",
        id,
    );
    writeln!(out, "static_assert(");
    writeln!(out, "    ::std::is_trivially_destructible<{}>::value,", id);
    writeln!(
        out,
        "    \"type {} marked as Trivial in Rust is not trivially destructible in C++\");",
        id,
    );
}

fn write_exception_glue(out: &mut OutFile, apis: &[&Api]) {
    let mut has_cxx_throws = false;
    for api in apis {
        if let Api::CxxFunction(efn) = api {
            if efn.throws {
                has_cxx_throws = true;
                break;
            }
        }
    }

    if has_cxx_throws {
        out.next_section();
        writeln!(
            out,
            "const char *cxxbridge05$exception(const char *, size_t);",
        );
    }
}

fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, impl_annotations: &Option<String>) {
    if let Some(annotation) = impl_annotations {
        write!(out, "{} ", annotation);
    }
    if efn.throws {
        write!(out, "::rust::repr::PtrLen ");
    } else {
        write_extern_return_type_space(out, &efn.ret);
    }
    let mangled = mangle::extern_fn(efn, out.types);
    write!(out, "{}(", mangled);
    if let Some(receiver) = &efn.receiver {
        if receiver.mutability.is_none() {
            write!(out, "const ");
        }
        write!(
            out,
            "{} &self",
            out.types.resolve(&receiver.ty).to_fully_qualified()
        );
    }
    for (i, arg) in efn.args.iter().enumerate() {
        if i > 0 || efn.receiver.is_some() {
            write!(out, ", ");
        }
        if arg.ty == RustString {
            write!(out, "const ");
        } else if let Type::RustVec(_) = arg.ty {
            write!(out, "const ");
        }
        write_extern_arg(out, arg);
    }
    let indirect_return = indirect_return(efn, out.types);
    if indirect_return {
        if !efn.args.is_empty() || efn.receiver.is_some() {
            write!(out, ", ");
        }
        write_indirect_return_type_space(out, efn.ret.as_ref().unwrap());
        write!(out, "*return$");
    }
    writeln!(out, ") noexcept {{");
    write!(out, "  ");
    write_return_type(out, &efn.ret);
    match &efn.receiver {
        None => write!(out, "(*{}$)(", efn.ident.rust),
        Some(receiver) => write!(
            out,
            "({}::*{}$)(",
            out.types.resolve(&receiver.ty).to_fully_qualified(),
            efn.ident.rust
        ),
    }
    for (i, arg) in efn.args.iter().enumerate() {
        if i > 0 {
            write!(out, ", ");
        }
        write_type(out, &arg.ty);
    }
    write!(out, ")");
    if let Some(receiver) = &efn.receiver {
        if receiver.mutability.is_none() {
            write!(out, " const");
        }
    }
    write!(out, " = ");
    match &efn.receiver {
        None => write!(out, "{}", efn.ident.cxx.to_fully_qualified()),
        Some(receiver) => write!(
            out,
            "&{}::{}",
            out.types.resolve(&receiver.ty).to_fully_qualified(),
            efn.ident.cxx.ident
        ),
    }
    writeln!(out, ";");
    write!(out, "  ");
    if efn.throws {
        out.builtin.trycatch = true;
        writeln!(out, "::rust::repr::PtrLen throw$;");
        writeln!(out, "  ::rust::behavior::trycatch(");
        writeln!(out, "      [&] {{");
        write!(out, "        ");
    }
    if indirect_return {
        out.include.new = true;
        write!(out, "new (return$) ");
        write_indirect_return_type(out, efn.ret.as_ref().unwrap());
        write!(out, "(");
    } else if efn.ret.is_some() {
        write!(out, "return ");
    }
    match &efn.ret {
        Some(Type::Ref(_)) => write!(out, "&"),
        Some(Type::Str(_)) if !indirect_return => {
            out.builtin.rust_str_repr = true;
            write!(out, "::rust::impl<::rust::Str>::repr(");
        }
        Some(Type::SliceRefU8(_)) if !indirect_return => {
            write!(out, "::rust::Slice<uint8_t>::Repr(")
        }
        _ => {}
    }
    match &efn.receiver {
        None => write!(out, "{}$(", efn.ident.rust),
        Some(_) => write!(out, "(self.*{}$)(", efn.ident.rust),
    }
    for (i, arg) in efn.args.iter().enumerate() {
        if i > 0 {
            write!(out, ", ");
        }
        if let Type::RustBox(_) = &arg.ty {
            write_type(out, &arg.ty);
            write!(out, "::from_raw({})", arg.ident);
        } else if let Type::UniquePtr(_) = &arg.ty {
            write_type(out, &arg.ty);
            write!(out, "({})", arg.ident);
        } else if let Type::Str(_) = arg.ty {
            out.builtin.rust_str_new_unchecked = true;
            write!(
                out,
                "::rust::impl<::rust::Str>::new_unchecked({})",
                arg.ident,
            );
        } else if arg.ty == RustString {
            out.builtin.unsafe_bitcopy = true;
            write!(
                out,
                "::rust::String(::rust::unsafe_bitcopy, *{})",
                arg.ident,
            );
        } else if let Type::RustVec(_) = arg.ty {
            out.builtin.unsafe_bitcopy = true;
            write_type(out, &arg.ty);
            write!(out, "(::rust::unsafe_bitcopy, *{})", arg.ident);
        } else if out.types.needs_indirect_abi(&arg.ty) {
            out.include.utility = true;
            write!(out, "::std::move(*{})", arg.ident);
        } else {
            write!(out, "{}", arg.ident);
        }
    }
    write!(out, ")");
    match &efn.ret {
        Some(Type::RustBox(_)) => write!(out, ".into_raw()"),
        Some(Type::UniquePtr(_)) => write!(out, ".release()"),
        Some(Type::Str(_)) | Some(Type::SliceRefU8(_)) if !indirect_return => write!(out, ")"),
        _ => {}
    }
    if indirect_return {
        write!(out, ")");
    }
    writeln!(out, ";");
    if efn.throws {
        out.include.cstring = true;
        writeln!(out, "        throw$.ptr = nullptr;");
        writeln!(out, "      }},");
        writeln!(out, "      [&](const char *catch$) noexcept {{");
        writeln!(out, "        throw$.len = ::std::strlen(catch$);");
        writeln!(
            out,
            "        throw$.ptr = cxxbridge05$exception(catch$, throw$.len);",
        );
        writeln!(out, "      }});");
        writeln!(out, "  return throw$;");
    }
    writeln!(out, "}}");
    for arg in &efn.args {
        if let Type::Fn(f) = &arg.ty {
            let var = &arg.ident;
            write_function_pointer_trampoline(out, efn, var, f);
        }
    }
}

fn write_function_pointer_trampoline(
    out: &mut OutFile,
    efn: &ExternFn,
    var: &Ident,
    f: &Signature,
) {
    out.next_section();
    let r_trampoline = mangle::r_trampoline(efn, var, out.types);
    let indirect_call = true;
    write_rust_function_decl_impl(out, &r_trampoline, f, indirect_call);

    out.next_section();
    let c_trampoline = mangle::c_trampoline(efn, var, out.types).to_string();
    write_rust_function_shim_impl(out, &c_trampoline, f, &r_trampoline, indirect_call);
}

fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, _: &Option<String>) {
    let link_name = mangle::extern_fn(efn, out.types);
    let indirect_call = false;
    write_rust_function_decl_impl(out, &link_name, efn, indirect_call);
}

fn write_rust_function_decl_impl(
    out: &mut OutFile,
    link_name: &Symbol,
    sig: &Signature,
    indirect_call: bool,
) {
    if sig.throws {
        write!(out, "::rust::repr::PtrLen ");
    } else {
        write_extern_return_type_space(out, &sig.ret);
    }
    write!(out, "{}(", link_name);
    let mut needs_comma = false;
    if let Some(receiver) = &sig.receiver {
        if receiver.mutability.is_none() {
            write!(out, "const ");
        }
        write!(
            out,
            "{} &self",
            out.types.resolve(&receiver.ty).to_fully_qualified()
        );
        needs_comma = true;
    }
    for arg in &sig.args {
        if needs_comma {
            write!(out, ", ");
        }
        write_extern_arg(out, arg);
        needs_comma = true;
    }
    if indirect_return(sig, out.types) {
        if needs_comma {
            write!(out, ", ");
        }
        write_return_type(out, &sig.ret);
        write!(out, "*return$");
        needs_comma = true;
    }
    if indirect_call {
        if needs_comma {
            write!(out, ", ");
        }
        write!(out, "void *");
    }
    writeln!(out, ") noexcept;");
}

fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn) {
    for line in efn.doc.to_string().lines() {
        writeln!(out, "//{}", line);
    }
    let local_name = match &efn.sig.receiver {
        None => efn.ident.cxx.ident.to_string(),
        Some(receiver) => format!(
            "{}::{}",
            out.types.resolve(&receiver.ty).ident,
            efn.ident.cxx.ident
        ),
    };
    let invoke = mangle::extern_fn(efn, out.types);
    let indirect_call = false;
    write_rust_function_shim_impl(out, &local_name, efn, &invoke, indirect_call);
}

fn write_rust_function_shim_decl(
    out: &mut OutFile,
    local_name: &str,
    sig: &Signature,
    indirect_call: bool,
) {
    write_return_type(out, &sig.ret);
    write!(out, "{}(", local_name);
    for (i, arg) in sig.args.iter().enumerate() {
        if i > 0 {
            write!(out, ", ");
        }
        write_type_space(out, &arg.ty);
        write!(out, "{}", arg.ident);
    }
    if indirect_call {
        if !sig.args.is_empty() {
            write!(out, ", ");
        }
        write!(out, "void *extern$");
    }
    write!(out, ")");
    if let Some(receiver) = &sig.receiver {
        if receiver.mutability.is_none() {
            write!(out, " const");
        }
    }
    if !sig.throws {
        write!(out, " noexcept");
    }
}

fn write_rust_function_shim_impl(
    out: &mut OutFile,
    local_name: &str,
    sig: &Signature,
    invoke: &Symbol,
    indirect_call: bool,
) {
    if out.header && sig.receiver.is_some() {
        // We've already defined this inside the struct.
        return;
    }
    write_rust_function_shim_decl(out, local_name, sig, indirect_call);
    if out.header {
        writeln!(out, ";");
        return;
    }
    writeln!(out, " {{");
    for arg in &sig.args {
        if arg.ty != RustString && out.types.needs_indirect_abi(&arg.ty) {
            out.include.utility = true;
            out.builtin.manually_drop = true;
            write!(out, "  ::rust::ManuallyDrop<");
            write_type(out, &arg.ty);
            writeln!(out, "> {}$(::std::move({0}));", arg.ident);
        }
    }
    write!(out, "  ");
    let indirect_return = indirect_return(sig, out.types);
    if indirect_return {
        out.builtin.maybe_uninit = true;
        write!(out, "::rust::MaybeUninit<");
        write_type(out, sig.ret.as_ref().unwrap());
        writeln!(out, "> return$;");
        write!(out, "  ");
    } else if let Some(ret) = &sig.ret {
        write!(out, "return ");
        match ret {
            Type::RustBox(_) => {
                write_type(out, ret);
                write!(out, "::from_raw(");
            }
            Type::UniquePtr(_) => {
                write_type(out, ret);
                write!(out, "(");
            }
            Type::Ref(_) => write!(out, "*"),
            Type::Str(_) => {
                out.builtin.rust_str_new_unchecked = true;
                write!(out, "::rust::impl<::rust::Str>::new_unchecked(");
            }
            _ => {}
        }
    }
    if sig.throws {
        write!(out, "::rust::repr::PtrLen error$ = ");
    }
    write!(out, "{}(", invoke);
    if sig.receiver.is_some() {
        write!(out, "*this");
    }
    for (i, arg) in sig.args.iter().enumerate() {
        if i > 0 || sig.receiver.is_some() {
            write!(out, ", ");
        }
        match &arg.ty {
            Type::Str(_) => {
                out.builtin.rust_str_repr = true;
                write!(out, "::rust::impl<::rust::Str>::repr(");
            }
            Type::SliceRefU8(_) => write!(out, "::rust::Slice<uint8_t>::Repr("),
            ty if out.types.needs_indirect_abi(ty) => write!(out, "&"),
            _ => {}
        }
        write!(out, "{}", arg.ident);
        match &arg.ty {
            Type::RustBox(_) => write!(out, ".into_raw()"),
            Type::UniquePtr(_) => write!(out, ".release()"),
            Type::Str(_) | Type::SliceRefU8(_) => write!(out, ")"),
            ty if ty != RustString && out.types.needs_indirect_abi(ty) => write!(out, "$.value"),
            _ => {}
        }
    }
    if indirect_return {
        if !sig.args.is_empty() {
            write!(out, ", ");
        }
        write!(out, "&return$.value");
    }
    if indirect_call {
        if !sig.args.is_empty() || indirect_return {
            write!(out, ", ");
        }
        write!(out, "extern$");
    }
    write!(out, ")");
    if !indirect_return {
        if let Some(ret) = &sig.ret {
            if let Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) = ret {
                write!(out, ")");
            }
        }
    }
    writeln!(out, ";");
    if sig.throws {
        out.include.exception = true;
        out.builtin.rust_error = true;
        writeln!(out, "  if (error$.ptr) {{");
        writeln!(out, "    throw ::rust::impl<::rust::Error>::error(error$);");
        writeln!(out, "  }}");
    }
    if indirect_return {
        out.include.utility = true;
        writeln!(out, "  return ::std::move(return$.value);");
    }
    writeln!(out, "}}");
}

fn write_return_type(out: &mut OutFile, ty: &Option<Type>) {
    match ty {
        None => write!(out, "void "),
        Some(ty) => write_type_space(out, ty),
    }
}

fn indirect_return(sig: &Signature, types: &Types) -> bool {
    sig.ret
        .as_ref()
{'fraction_non_alphanumeric': 0.1659921449789669, 'fraction_numerical': 0.0016746745349787676, 'mean_word_length': 1.8985842242126554, 'pattern_counts': {'":': 30, '<': 37, '<?xml version=': 0, '>': 177, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1825255', 'n_tokens_mistral': 16613, 'n_tokens_neox': 16341, 'n_words': 2986}