deno_ops/
lib.rs

1// Copyright 2018-2025 the Deno authors. MIT license.
2
3#![doc = include_str!("README.md")]
4#![deny(clippy::unnecessary_wraps)]
5
6use proc_macro::TokenStream;
7use std::error::Error;
8
9mod op2;
10mod webidl;
11
12/// A macro designed to provide an extremely fast V8->Rust interface layer.
13#[doc = include_str!("op2/README.md")]
14#[proc_macro_attribute]
15pub fn op2(attr: TokenStream, item: TokenStream) -> TokenStream {
16  op2_macro(attr, item)
17}
18
19fn op2_macro(attr: TokenStream, item: TokenStream) -> TokenStream {
20  match crate::op2::op2(attr.into(), item.into()) {
21    Ok(output) => output.into(),
22    Err(err) => {
23      let mut err: &dyn Error = &err;
24      let mut output = "Failed to parse #[op2]:\n".to_owned();
25      loop {
26        output += &format!(" - {err}\n");
27        if let Some(source) = err.source() {
28          err = source;
29        } else {
30          break;
31        }
32      }
33      panic!("{output}");
34    }
35  }
36}
37
38#[proc_macro_derive(WebIDL, attributes(webidl, options))]
39pub fn webidl(item: TokenStream) -> TokenStream {
40  match webidl::webidl(item.into()) {
41    Ok(output) => output.into(),
42    Err(err) => err.into_compile_error().into(),
43  }
44}
45
46#[cfg(test)]
47mod infra {
48  use std::path::PathBuf;
49  use syn::File;
50
51  pub fn run_macro_expansion_test<F, I>(input: PathBuf, expander: F)
52  where
53    F: FnOnce(File) -> I,
54    I: Iterator<Item = proc_macro2::TokenStream>,
55  {
56    let update_expected = std::env::var("UPDATE_EXPECTED").is_ok();
57
58    let source =
59      std::fs::read_to_string(&input).expect("Failed to read test file");
60
61    const PRELUDE: &str = r"// Copyright 2018-2025 the Deno authors. MIT license.
62
63#![deny(warnings)]
64deno_ops_compile_test_runner::prelude!();";
65
66    if !source.starts_with(PRELUDE) {
67      panic!("Source does not start with expected prelude:]n{PRELUDE}");
68    }
69
70    let file =
71      syn::parse_str::<File>(&source).expect("Failed to parse Rust file");
72
73    let expected_out = expander(file)
74      .map(|tokens| {
75        println!("======== Raw tokens ========:\n{}", tokens.clone());
76        let tree = syn::parse2(tokens).unwrap();
77        let actual = prettyplease::unparse(&tree);
78        println!("======== Generated ========:\n{}", actual);
79        actual
80      })
81      .collect::<Vec<String>>()
82      .join("\n");
83
84    if update_expected {
85      std::fs::write(input.with_extension("out"), expected_out)
86        .expect("Failed to write expectation file");
87    } else {
88      let expected = std::fs::read_to_string(input.with_extension("out"))
89        .expect("Failed to read expectation file");
90
91      pretty_assertions::assert_eq!(
92        expected,
93        expected_out,
94        "Failed to match expectation. Use UPDATE_EXPECTED=1."
95      );
96    }
97  }
98}