blob: 523ba1d6bd2e34e56ab02e2ce9dcb439fd2dae7d [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 crate::syntax::{self, check, ident, Types};
13use quote::quote;
14use std::fs;
15use std::io;
16use std::path::Path;
17use syn::parse::ParseStream;
18use syn::{Attribute, File, Item, Token};
19use thiserror::Error;
20
21pub(super) type Result<T, E = Error> = std::result::Result<T, E>;
22
23#[derive(Error, Debug)]
24pub(super) enum Error {
25 #[error("no #[cxx::bridge] module found")]
26 NoBridgeMod,
27 #[error("#[cxx::bridge] module must have inline contents")]
28 OutOfLineMod,
29 #[error(transparent)]
30 Io(#[from] io::Error),
31 #[error(transparent)]
32 Syn(#[from] syn::Error),
33}
34
35struct Input {
David Tolnay754e21c2020-03-29 20:58:46 -070036 namespace: Namespace,
David Tolnay7db73692019-10-20 14:51:12 -040037 module: Vec<Item>,
38}
39
David Tolnay33d30292020-03-18 18:02:02 -070040#[derive(Default)]
41pub(super) struct Opt {
42 /// Any additional headers to #include
43 pub include: Vec<String>,
44}
45
David Tolnay7ece56f2020-03-29 21:21:38 -070046pub(super) fn do_generate_bridge(path: &Path, opt: Opt) -> Vec<u8> {
David Tolnay7db73692019-10-20 14:51:12 -040047 let header = false;
David Tolnay33d30292020-03-18 18:02:02 -070048 generate(path, opt, header)
David Tolnay7db73692019-10-20 14:51:12 -040049}
50
David Tolnay7ece56f2020-03-29 21:21:38 -070051pub(super) fn do_generate_header(path: &Path, opt: Opt) -> Vec<u8> {
David Tolnay7db73692019-10-20 14:51:12 -040052 let header = true;
David Tolnay33d30292020-03-18 18:02:02 -070053 generate(path, opt, header)
David Tolnay7db73692019-10-20 14:51:12 -040054}
55
David Tolnay7ece56f2020-03-29 21:21:38 -070056fn generate(path: &Path, opt: Opt, header: bool) -> Vec<u8> {
David Tolnay7db73692019-10-20 14:51:12 -040057 let source = match fs::read_to_string(path) {
58 Ok(source) => source,
59 Err(err) => format_err(path, "", Error::Io(err)),
60 };
61 match (|| -> Result<_> {
62 let syntax = syn::parse_file(&source)?;
63 let bridge = find_bridge_mod(syntax)?;
64 let apis = syntax::parse_items(bridge.module)?;
65 let types = Types::collect(&apis)?;
66 check::typecheck(&apis, &types)?;
David Tolnay33d30292020-03-18 18:02:02 -070067 let out = write::gen(bridge.namespace, &apis, &types, opt, header);
David Tolnay7db73692019-10-20 14:51:12 -040068 Ok(out)
69 })() {
David Tolnay7ece56f2020-03-29 21:21:38 -070070 Ok(out) => out.content(),
David Tolnay7db73692019-10-20 14:51:12 -040071 Err(err) => format_err(path, &source, err),
72 }
73}
74
75fn find_bridge_mod(syntax: File) -> Result<Input> {
76 for item in syntax.items {
77 if let Item::Mod(item) = item {
78 for attr in &item.attrs {
79 let path = &attr.path;
80 if quote!(#path).to_string() == "cxx :: bridge" {
81 let module = match item.content {
82 Some(module) => module.1,
83 None => {
84 return Err(Error::Syn(syn::Error::new_spanned(
85 item,
86 Error::OutOfLineMod,
87 )));
88 }
89 };
David Tolnay754e21c2020-03-29 20:58:46 -070090 let namespace_segments = parse_args(attr)?;
91 let namespace = Namespace::new(namespace_segments);
92 return Ok(Input { namespace, module });
David Tolnay7db73692019-10-20 14:51:12 -040093 }
94 }
95 }
96 }
97 Err(Error::NoBridgeMod)
98}
99
100fn parse_args(attr: &Attribute) -> syn::Result<Vec<String>> {
101 if attr.tokens.is_empty() {
102 return Ok(Vec::new());
103 }
104 attr.parse_args_with(|input: ParseStream| {
105 mod kw {
106 syn::custom_keyword!(namespace);
107 }
108 input.parse::<kw::namespace>()?;
109 input.parse::<Token![=]>()?;
110 let path = syn::Path::parse_mod_style(input)?;
111 input.parse::<Option<Token![,]>>()?;
112 path.segments
113 .into_iter()
114 .map(|seg| {
115 ident::check(&seg.ident)?;
116 Ok(seg.ident.to_string())
117 })
118 .collect()
119 })
120}