blob: 4c3a292f990136fea736b09af5d15c426158d99d [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;
6pub(super) mod out;
7mod write;
8
9use self::error::format_err;
David Tolnay08419302020-04-19 20:38:20 -070010use crate::syntax::namespace::Namespace;
David Tolnayb6cf3142020-04-19 20:56:09 -070011use crate::syntax::{self, check, Types};
David Tolnay7db73692019-10-20 14:51:12 -040012use quote::quote;
13use std::fs;
14use std::io;
15use std::path::Path;
David Tolnayb6cf3142020-04-19 20:56:09 -070016use syn::{Attribute, File, Item};
David Tolnay7db73692019-10-20 14:51:12 -040017use thiserror::Error;
18
19pub(super) type Result<T, E = Error> = std::result::Result<T, E>;
20
21#[derive(Error, Debug)]
22pub(super) enum Error {
23 #[error("no #[cxx::bridge] module found")]
24 NoBridgeMod,
25 #[error("#[cxx::bridge] module must have inline contents")]
26 OutOfLineMod,
27 #[error(transparent)]
28 Io(#[from] io::Error),
29 #[error(transparent)]
30 Syn(#[from] syn::Error),
31}
32
33struct Input {
David Tolnay754e21c2020-03-29 20:58:46 -070034 namespace: Namespace,
David Tolnay7db73692019-10-20 14:51:12 -040035 module: Vec<Item>,
36}
37
David Tolnay33d30292020-03-18 18:02:02 -070038#[derive(Default)]
39pub(super) struct Opt {
40 /// Any additional headers to #include
41 pub include: Vec<String>,
42}
43
David Tolnay7ece56f2020-03-29 21:21:38 -070044pub(super) fn do_generate_bridge(path: &Path, opt: Opt) -> Vec<u8> {
David Tolnay7db73692019-10-20 14:51:12 -040045 let header = false;
David Tolnay33d30292020-03-18 18:02:02 -070046 generate(path, opt, header)
David Tolnay7db73692019-10-20 14:51:12 -040047}
48
David Tolnay7ece56f2020-03-29 21:21:38 -070049pub(super) fn do_generate_header(path: &Path, opt: Opt) -> Vec<u8> {
David Tolnay7db73692019-10-20 14:51:12 -040050 let header = true;
David Tolnay33d30292020-03-18 18:02:02 -070051 generate(path, opt, header)
David Tolnay7db73692019-10-20 14:51:12 -040052}
53
David Tolnay7ece56f2020-03-29 21:21:38 -070054fn generate(path: &Path, opt: Opt, header: bool) -> Vec<u8> {
David Tolnay7db73692019-10-20 14:51:12 -040055 let source = match fs::read_to_string(path) {
56 Ok(source) => source,
57 Err(err) => format_err(path, "", Error::Io(err)),
58 };
59 match (|| -> Result<_> {
60 let syntax = syn::parse_file(&source)?;
61 let bridge = find_bridge_mod(syntax)?;
62 let apis = syntax::parse_items(bridge.module)?;
63 let types = Types::collect(&apis)?;
64 check::typecheck(&apis, &types)?;
David Tolnay33d30292020-03-18 18:02:02 -070065 let out = write::gen(bridge.namespace, &apis, &types, opt, header);
David Tolnay7db73692019-10-20 14:51:12 -040066 Ok(out)
67 })() {
David Tolnay7ece56f2020-03-29 21:21:38 -070068 Ok(out) => out.content(),
David Tolnay7db73692019-10-20 14:51:12 -040069 Err(err) => format_err(path, &source, err),
70 }
71}
72
73fn find_bridge_mod(syntax: File) -> Result<Input> {
74 for item in syntax.items {
75 if let Item::Mod(item) = item {
76 for attr in &item.attrs {
77 let path = &attr.path;
78 if quote!(#path).to_string() == "cxx :: bridge" {
79 let module = match item.content {
80 Some(module) => module.1,
81 None => {
82 return Err(Error::Syn(syn::Error::new_spanned(
83 item,
84 Error::OutOfLineMod,
85 )));
86 }
87 };
David Tolnayb6cf3142020-04-19 20:56:09 -070088 let namespace = parse_args(attr)?;
David Tolnay754e21c2020-03-29 20:58:46 -070089 return Ok(Input { namespace, module });
David Tolnay7db73692019-10-20 14:51:12 -040090 }
91 }
92 }
93 }
94 Err(Error::NoBridgeMod)
95}
96
David Tolnayb6cf3142020-04-19 20:56:09 -070097fn parse_args(attr: &Attribute) -> syn::Result<Namespace> {
David Tolnay7db73692019-10-20 14:51:12 -040098 if attr.tokens.is_empty() {
David Tolnayb6cf3142020-04-19 20:56:09 -070099 Ok(Namespace::none())
100 } else {
101 attr.parse_args()
David Tolnay7db73692019-10-20 14:51:12 -0400102 }
David Tolnay7db73692019-10-20 14:51:12 -0400103}