blob: 4ca86d9e3170ec8afa41311726b72e0b46e87ae5 [file] [log] [blame]
Brian Silverman4e662aa2022-05-11 23:10:19 -07001// Copyright 2022 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use indexmap::IndexMap;
10use proc_macro2::TokenStream;
11use serde::{Deserialize, Serialize};
12
13use crate::IncludeCppConfig;
14
15/// Struct which stores multiple sets of bindings and can be serialized
16/// to disk. This is used when our build system uses `autocxx_gen`; that
17/// can handle multiple `include_cpp!` macros and therefore generate multiple
18/// sets of Rust bindings. We can't simply `include!` those because there's
19/// no (easy) way to pass their details from the codegen phase across to
20/// the Rust macro phase. Instead, we use this data structure to store
21/// several sets of .rs bindings in a single file, and then the macro
22/// extracts the correct set of bindings at expansion time.
23#[derive(Serialize, Deserialize, Default)]
24pub struct MultiBindings(IndexMap<u64, String>);
25
26use thiserror::Error;
27
28#[derive(Error, Debug)]
29pub enum MultiBindingsErr {
30 #[error("unable to find the desired bindings within the archive of Rust bindings produced by the autocxx code generation phase")]
31 MissingBindings,
32 #[error("the stored bindings within the JSON file could not be parsed as valid Rust tokens")]
33 BindingsNotParseable,
34}
35
36impl MultiBindings {
37 /// Insert some generated Rust bindings into this data structure.
38 pub fn insert(&mut self, config: &IncludeCppConfig, bindings: TokenStream) {
39 self.0.insert(config.get_hash(), bindings.to_string());
40 }
41
42 /// Retrieves the bindings corresponding to a given [`IncludeCppConfig`].
43 pub fn get(&self, config: &IncludeCppConfig) -> Result<TokenStream, MultiBindingsErr> {
44 match self.0.get(&(config.get_hash())) {
45 None => Err(MultiBindingsErr::MissingBindings),
46 Some(bindings) => Ok(bindings
47 .parse()
48 .map_err(|_| MultiBindingsErr::BindingsNotParseable)?),
49 }
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use proc_macro2::Span;
56 use quote::quote;
57 use syn::parse_quote;
58
59 use crate::IncludeCppConfig;
60
61 use super::MultiBindings;
62
63 #[test]
64 fn test_multi_bindings() {
65 let hexathorpe = syn::token::Pound(Span::call_site());
66 let config1: IncludeCppConfig = parse_quote! {
67 #hexathorpe include "a.h"
68 generate!("Foo")
69 };
70 let config2: IncludeCppConfig = parse_quote! {
71 #hexathorpe include "b.h"
72 generate!("Bar")
73 };
74 let config3: IncludeCppConfig = parse_quote! {
75 #hexathorpe include "c.h"
76 generate!("Bar")
77 };
78 let mut multi_bindings = MultiBindings::default();
79 multi_bindings.insert(&config1, quote! { first; });
80 multi_bindings.insert(&config2, quote! { second; });
81 let json = serde_json::to_string(&multi_bindings).unwrap();
82 let multi_bindings2: MultiBindings = serde_json::from_str(&json).unwrap();
83 assert_eq!(
84 multi_bindings2.get(&config2).unwrap().to_string(),
85 "second ;"
86 );
87 assert_eq!(
88 multi_bindings2.get(&config1).unwrap().to_string(),
89 "first ;"
90 );
91 assert!(multi_bindings2.get(&config3).is_err());
92 }
93}