blob: 800689894d1cae12a569f8c4724cd62f37a4fb0f [file] [log] [blame]
David Tolnay7db73692019-10-20 14:51:12 -04001// Functionality that is shared between the cxx::generate_bridge entry point and
2// the cmd.
3
4mod error;
5pub(super) mod include;
David Tolnay754e21c2020-03-29 20:58:46 -07006mod namespace;
David Tolnay7db73692019-10-20 14:51:12 -04007pub(super) mod out;
8mod write;
9
10use self::error::format_err;
David Tolnay754e21c2020-03-29 20:58:46 -070011use self::namespace::Namespace;
David Tolnay7db73692019-10-20 14:51:12 -040012use self::out::OutFile;
13use crate::syntax::{self, check, ident, Types};
14use quote::quote;
15use std::fs;
16use std::io;
17use std::path::Path;
18use syn::parse::ParseStream;
19use syn::{Attribute, File, Item, Token};
20use thiserror::Error;
21
22pub(super) type Result<T, E = Error> = std::result::Result<T, E>;
23
24#[derive(Error, Debug)]
25pub(super) enum Error {
26 #[error("no #[cxx::bridge] module found")]
27 NoBridgeMod,
28 #[error("#[cxx::bridge] module must have inline contents")]
29 OutOfLineMod,
30 #[error(transparent)]
31 Io(#[from] io::Error),
32 #[error(transparent)]
33 Syn(#[from] syn::Error),
34}
35
36struct Input {
David Tolnay754e21c2020-03-29 20:58:46 -070037 namespace: Namespace,
David Tolnay7db73692019-10-20 14:51:12 -040038 module: Vec<Item>,
39}
40
David Tolnay33d30292020-03-18 18:02:02 -070041#[derive(Default)]
42pub(super) struct Opt {
43 /// Any additional headers to #include
44 pub include: Vec<String>,
45}
46
47pub(super) fn do_generate_bridge(path: &Path, opt: Opt) -> OutFile {
David Tolnay7db73692019-10-20 14:51:12 -040048 let header = false;
David Tolnay33d30292020-03-18 18:02:02 -070049 generate(path, opt, header)
David Tolnay7db73692019-10-20 14:51:12 -040050}
51
David Tolnay33d30292020-03-18 18:02:02 -070052pub(super) fn do_generate_header(path: &Path, opt: Opt) -> OutFile {
David Tolnay7db73692019-10-20 14:51:12 -040053 let header = true;
David Tolnay33d30292020-03-18 18:02:02 -070054 generate(path, opt, header)
David Tolnay7db73692019-10-20 14:51:12 -040055}
56
David Tolnay33d30292020-03-18 18:02:02 -070057fn generate(path: &Path, opt: Opt, header: bool) -> OutFile {
David Tolnay7db73692019-10-20 14:51:12 -040058 let source = match fs::read_to_string(path) {
59 Ok(source) => source,
60 Err(err) => format_err(path, "", Error::Io(err)),
61 };
62 match (|| -> Result<_> {
63 let syntax = syn::parse_file(&source)?;
64 let bridge = find_bridge_mod(syntax)?;
65 let apis = syntax::parse_items(bridge.module)?;
66 let types = Types::collect(&apis)?;
67 check::typecheck(&apis, &types)?;
David Tolnay33d30292020-03-18 18:02:02 -070068 let out = write::gen(bridge.namespace, &apis, &types, opt, header);
David Tolnay7db73692019-10-20 14:51:12 -040069 Ok(out)
70 })() {
71 Ok(out) => out,
72 Err(err) => format_err(path, &source, err),
73 }
74}
75
76fn find_bridge_mod(syntax: File) -> Result<Input> {
77 for item in syntax.items {
78 if let Item::Mod(item) = item {
79 for attr in &item.attrs {
80 let path = &attr.path;
81 if quote!(#path).to_string() == "cxx :: bridge" {
82 let module = match item.content {
83 Some(module) => module.1,
84 None => {
85 return Err(Error::Syn(syn::Error::new_spanned(
86 item,
87 Error::OutOfLineMod,
88 )));
89 }
90 };
David Tolnay754e21c2020-03-29 20:58:46 -070091 let namespace_segments = parse_args(attr)?;
92 let namespace = Namespace::new(namespace_segments);
93 return Ok(Input { namespace, module });
David Tolnay7db73692019-10-20 14:51:12 -040094 }
95 }
96 }
97 }
98 Err(Error::NoBridgeMod)
99}
100
101fn parse_args(attr: &Attribute) -> syn::Result<Vec<String>> {
102 if attr.tokens.is_empty() {
103 return Ok(Vec::new());
104 }
105 attr.parse_args_with(|input: ParseStream| {
106 mod kw {
107 syn::custom_keyword!(namespace);
108 }
109 input.parse::<kw::namespace>()?;
110 input.parse::<Token![=]>()?;
111 let path = syn::Path::parse_mod_style(input)?;
112 input.parse::<Option<Token![,]>>()?;
113 path.segments
114 .into_iter()
115 .map(|seg| {
116 ident::check(&seg.ident)?;
117 Ok(seg.ident.to_string())
118 })
119 .collect()
120 })
121}