1use crate::hashing::HashCode;
6use crate::serde_helpers;
7use crate::spec::ProbeSpecification;
8use crate::{TracersError, TracersResult};
9use darling::FromMeta;
10use heck::SnakeCase;
11use proc_macro2::TokenStream;
12use quote::quote;
13use serde::{Deserialize, Serialize};
14use std::fmt;
15use syn::parse::{Parse, ParseStream, Result as ParseResult};
16use syn::visit::Visit;
17use syn::Token;
18use syn::{ItemTrait, TraitItem};
19
20#[derive(Debug, FromMeta, Clone, Serialize, Deserialize, Default)]
22pub(crate) struct TracerAttributeArgs {
23 #[darling(default)]
24 provider_name: Option<String>,
25}
26
27impl Parse for TracerAttributeArgs {
31 fn parse(input: ParseStream) -> ParseResult<Self> {
32 let mut metas: Vec<syn::NestedMeta> = vec![];
36
37 loop {
38 if input.is_empty() {
39 break;
40 }
41 let value = input.parse()?;
42 metas.push(value);
43 if input.is_empty() {
44 break;
45 }
46 input.parse::<Token![,]>()?;
47 }
48
49 TracerAttributeArgs::from_list(&metas).map_err(|e| input.error(e))
50 }
51}
52
53impl TracerAttributeArgs {
54 pub(crate) fn from_token_stream(attr: TokenStream) -> TracersResult<Self> {
63 syn::parse2(attr).map_err(|e| TracersError::syn_error("Error parsing attribute args", e))
64 }
65
66 fn from_attribute(attr: syn::Attribute) -> TracersResult<Self> {
68 let meta = attr
78 .parse_meta()
79 .map_err(|e| TracersError::syn_error("Error parsing attribute metadata", e))?;
80
81 let args = match meta {
82 syn::Meta::Path(_) =>
83 {
85 Ok(TracerAttributeArgs::default())
86 }
87 syn::Meta::NameValue(_) => Err(TracersError::syn_like_error(
88 "Expected name/value pairs in ()",
89 attr,
90 )),
91 syn::Meta::List(list) => {
92 TracerAttributeArgs::from_list(
95 &list
96 .nested
97 .into_pairs()
98 .map(syn::punctuated::Pair::into_value)
99 .collect::<Vec<_>>(),
100 )
101 .map_err(TracersError::darling_error)
102 }
103 }?;
104
105 Ok(args)
106 }
107}
108
109pub(crate) struct TracerAttribute {
114 args: TracerAttributeArgs,
115}
116
117impl TracerAttribute {
118 fn from_attribute(attr: syn::Attribute) -> TracersResult<Self> {
119 Ok(TracerAttribute {
120 args: TracerAttributeArgs::from_attribute(attr)?,
121 })
122 }
123}
124
125impl Parse for TracerAttribute {
126 fn parse(input: ParseStream) -> ParseResult<Self> {
127 let mut attrs: Vec<syn::Attribute> = input.call(syn::Attribute::parse_outer)?;
129
130 if let Some(tracer_attr) = attrs.pop() {
132 Ok(TracerAttribute {
133 args: TracerAttributeArgs::from_attribute(tracer_attr)
134 .map_err(TracersError::into_syn_error)?,
135 })
136 } else {
137 Err(input.error("Expected exactly one attribute, `#[tracer]`"))
138 }
139 }
140}
141
142#[derive(Serialize, Deserialize, Clone)]
143pub struct ProviderSpecification {
144 name: String,
145 hash: HashCode,
146 #[serde(with = "serde_helpers::syn")]
147 item_trait: ItemTrait,
148 #[serde(with = "serde_helpers::token_stream")]
149 token_stream: TokenStream,
150 args: TracerAttributeArgs,
151 probes: Vec<ProbeSpecification>,
152}
153
154impl fmt::Debug for ProviderSpecification {
155 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
156 writeln!(
157 f,
158 "ProviderSpecification(
159 name='{}',
160 probes:",
161 self.name
162 )?;
163
164 for probe in self.probes.iter() {
165 writeln!(f, " {:?},", probe)?;
166 }
167
168 write!(f, ")")
169 }
170}
171
172impl ProviderSpecification {
173 fn new(
174 crate_name: &str,
175 args: TracerAttributeArgs,
176 item_trait: ItemTrait,
177 ) -> TracersResult<ProviderSpecification> {
178 let probes = find_probes(&item_trait)?;
179 let token_stream = quote! { #item_trait };
180 let hash = crate::hashing::hash(&item_trait);
181
182 let name: String = match args.provider_name {
184 Some(ref name) => name.clone(),
185 None => Self::provider_name_from_trait(crate_name, &item_trait.ident),
186 };
187 Ok(ProviderSpecification {
188 name,
189 hash,
190 item_trait,
191 token_stream,
192 args,
193 probes,
194 })
195 }
196
197 pub(crate) fn from_token_stream(
198 crate_name: &str,
199 args: TracerAttributeArgs,
200 tokens: TokenStream,
201 ) -> TracersResult<ProviderSpecification> {
202 match syn::parse2::<syn::ItemTrait>(tokens) {
203 Ok(item_trait) => Self::new(crate_name, args, item_trait),
204 Err(e) => Err(TracersError::syn_error("Expected a trait", e)),
205 }
206 }
207
208 pub(crate) fn from_trait(
209 crate_name: &str,
210 attr: TracerAttribute,
211 item_trait: ItemTrait,
212 ) -> TracersResult<ProviderSpecification> {
213 Self::new(crate_name, attr.args, item_trait)
214 }
215
216 pub(crate) fn provider_name_from_trait(crate_name: &str, ident: &syn::Ident) -> String {
218 format!("{}_{}", crate_name, ident.to_string().to_snake_case())
226 }
227
228 pub(crate) fn name(&self) -> &str {
229 &self.name
230 }
231
232 pub(crate) fn name_with_hash(&self) -> String {
235 format!("{}_{:x}", self.name, self.hash)
236 }
237
238 pub(crate) fn hash(&self) -> HashCode {
239 self.hash
240 }
241
242 pub(crate) fn ident(&self) -> &syn::Ident {
243 &self.item_trait.ident
244 }
245
246 pub(crate) fn item_trait(&self) -> &syn::ItemTrait {
247 &self.item_trait
248 }
249
250 pub(crate) fn token_stream(&self) -> &TokenStream {
251 &self.token_stream
252 }
253
254 pub(crate) fn probes(&self) -> &Vec<ProbeSpecification> {
255 &self.probes
256 }
257
258 pub(crate) fn separate_probes(self) -> (ProviderSpecification, Vec<ProbeSpecification>) {
263 let probes = self.probes;
264 (
265 ProviderSpecification {
266 name: self.name,
267 hash: self.hash,
268 item_trait: self.item_trait,
269 token_stream: self.token_stream,
270 args: self.args,
271 probes: Vec::new(),
272 },
273 probes,
274 )
275 }
276}
277
278pub(crate) fn find_providers(crate_name: &str, ast: &syn::File) -> Vec<ProviderSpecification> {
286 struct Visitor<'a> {
289 crate_name: &'a str,
290 providers: Vec<ProviderSpecification>,
291 }
292
293 impl<'ast> Visit<'ast> for Visitor<'ast> {
294 fn visit_item_trait(&mut self, i: &'ast ItemTrait) {
295 syn::visit::visit_item_trait(self, i);
297
298 fn is_tracer_attribute(attr: &syn::Attribute) -> bool {
299 match attr.path.segments.iter().last() {
300 Some(syn::PathSegment { ident, .. }) if *ident == "tracer" => true,
301 _ => false,
302 }
303 }
304
305 let mut i = i.clone();
310 let (mut tracer_attrs, other_attrs) = i
311 .attrs
312 .into_iter()
313 .partition::<Vec<syn::Attribute>, _>(is_tracer_attribute);
314 if let Some(tracer_attr) = tracer_attrs.pop() {
315 i.attrs = other_attrs;
319 if let Ok(provider) = ProviderSpecification::from_trait(
320 self.crate_name,
321 TracerAttribute::from_attribute(tracer_attr)
322 .expect("Failed parsing attribute metadata"),
323 i,
324 ) {
325 self.providers.push(provider)
326 }
327 }
328 }
329 }
330
331 let mut visitor = Visitor {
332 crate_name,
333 providers: Vec::new(),
334 };
335 visitor.visit_file(ast);
336
337 visitor.providers
338}
339
340fn find_probes(item: &ItemTrait) -> TracersResult<Vec<ProbeSpecification>> {
346 if item.generics.type_params().next() != None || item.generics.lifetimes().next() != None {
347 return Err(TracersError::invalid_provider(
348 "Probe traits must not take any lifetime or type parameters",
349 item,
350 ));
351 }
352
353 let mut specs: Vec<ProbeSpecification> = Vec::new();
355 for f in item.items.iter() {
356 match f {
357 TraitItem::Method(ref m) => {
358 specs.push(ProbeSpecification::from_method(item, m)?);
359 }
360 _ => {
361 return Err(TracersError::invalid_provider(
362 "Probe traits must consist entirely of methods, no other contents",
363 f,
364 ));
365 }
366 }
367 }
368
369 Ok(specs)
370}
371
372#[cfg(test)]
373mod test {
374 use super::*;
375 use crate::testdata::*;
376 use std::io::{BufReader, BufWriter};
377 use syn::parse_quote;
378
379 impl PartialEq<ProviderSpecification> for ProviderSpecification {
380 fn eq(&self, other: &ProviderSpecification) -> bool {
381 self.name == other.name && self.probes == other.probes
382 }
383 }
384
385 impl PartialEq<TestProviderTrait> for ProviderSpecification {
387 fn eq(&self, other: &TestProviderTrait) -> bool {
388 self.name == other.provider_name
389 && other
390 .probes
391 .as_ref()
392 .map(|probes| &self.probes == probes)
393 .unwrap_or(false)
394 }
395 }
396
397 fn get_filtered_test_traits(with_errors: bool) -> Vec<TestProviderTrait> {
398 get_test_provider_traits(|t: &TestProviderTrait| t.expected_error.is_some() == with_errors)
399 }
400
401 #[test]
402 fn find_providers_ignores_invalid_traits() {
403 for test_trait in get_filtered_test_traits(true) {
404 let trait_decl = test_trait.tokenstream;
405 let test_file: syn::File = parse_quote! {
406 #[tracer]
407 #trait_decl
408 };
409
410 assert_eq!(
411 None,
412 find_providers(TEST_CRATE_NAME, &test_file).first(),
413 "The invalid trait '{}' was returned by find_providers as valid",
414 test_trait.description
415 );
416 }
417 }
418
419 #[test]
420 fn find_providers_finds_valid_traits() {
421 for test_trait in get_filtered_test_traits(false) {
422 let trait_attr = test_trait.attr_tokenstream.clone();
423 let trait_decl = test_trait.tokenstream.clone();
424 let test_file: syn::File = parse_quote! {
425 #trait_attr
426 #trait_decl
427 };
428
429 let mut providers = find_providers(TEST_CRATE_NAME, &test_file);
430 assert_ne!(
431 0,
432 providers.len(),
433 "the test trait '{}' was not properly detected by find_provider",
434 test_trait.description
435 );
436
437 assert_eq!(providers.pop().unwrap(), test_trait);
438 }
439 }
440
441 #[test]
442 fn find_probes_fails_with_invalid_traits() {
443 for test_trait in get_filtered_test_traits(true) {
444 let trait_decl = test_trait.tokenstream;
445 let item_trait: syn::ItemTrait = parse_quote! {
446 #[tracer]
447 #trait_decl
448 };
449
450 let error = find_probes(&item_trait).err();
451 assert_ne!(
452 None, error,
453 "The invalid trait '{}' was returned by find_probes as valid",
454 test_trait.description
455 );
456
457 let expected_error_substring = test_trait.expected_error.unwrap();
458 let message = error.unwrap().to_string();
459 assert!(message.contains(expected_error_substring),
460 "The invalid trait '{}' should produce an error containing '{}' but instead it produced '{}'",
461 test_trait.description,
462 expected_error_substring,
463 message
464 );
465 }
466 }
467
468 #[test]
469 fn find_probes_succeeds_with_valid_traits() {
470 for test_trait in get_filtered_test_traits(false) {
471 let trait_decl = test_trait.tokenstream;
472 let item_trait: syn::ItemTrait = parse_quote! {
473 #[tracer]
474 #trait_decl
475 };
476
477 let probes = find_probes(&item_trait).unwrap();
478 assert_eq!(probes, test_trait.probes.unwrap_or(Vec::new()));
479 }
480 }
481
482 #[test]
483 fn found_providers_have_same_hash() {
484 for test_trait in get_filtered_test_traits(false) {
497 let provider_from_ts = ProviderSpecification::from_trait(
502 TEST_CRATE_NAME,
503 syn::parse2(test_trait.attr_tokenstream.clone()).unwrap(),
504 syn::parse2(test_trait.tokenstream.clone()).unwrap(),
505 )
506 .unwrap();
507
508 let tracer_attr = test_trait.attr_tokenstream;
509 let trait_decl = test_trait.tokenstream;
510 let file: syn::File = parse_quote! {
511 mod foo {
512 fn useless_func() -> bool { false }
513 }
514
515 trait NotAProvider {
516 fn probe0(not_a_probe_arg: usize);
517 }
518
519 #tracer_attr
520 #trait_decl
521 };
522
523 let providers = find_providers(TEST_CRATE_NAME, &file);
524
525 assert_eq!(1, providers.len());
526 let provider_from_file = providers.get(0).unwrap();
527
528 assert_eq!(provider_from_ts.name(), provider_from_file.name());
529 assert_eq!(provider_from_ts.hash(), provider_from_file.hash());
530 }
531 }
532
533 #[test]
534 fn provider_serde_test() {
535 for test_trait in get_filtered_test_traits(false) {
538 println!("Parsing attribute: {}", test_trait.attr_tokenstream);
539 let (attr, item_trait) = test_trait.get_attr_and_item_trait();
540 let provider =
541 ProviderSpecification::from_trait(TEST_CRATE_NAME, attr, item_trait).unwrap();
542 let mut buffer = Vec::new();
543 let writer = BufWriter::new(&mut buffer);
544 serde_json::to_writer(writer, &provider).unwrap();
545
546 let reader = BufReader::new(buffer.as_slice());
547
548 let rt_provider: ProviderSpecification = match serde_json::from_reader(reader) {
549 Ok(p) => p,
550 Err(e) => {
551 panic!(
552 r###"Error deserializing provider:
553 Test case: {}
554 JSON: {}
555 Error: {}"###,
556 test_trait.description,
557 String::from_utf8(buffer).unwrap(),
558 e
559 );
560 }
561 };
562
563 assert_eq!(
564 provider, rt_provider,
565 "test case: {}",
566 test_trait.description
567 );
568 }
569 }
570
571 #[test]
572 fn parses_tracer_attributes_test() {
573 for test_trait in get_filtered_test_traits(false) {
575 println!("Parsing attribute: {}", test_trait.attr_tokenstream);
576 let _attribute: TracerAttribute =
577 syn::parse2(test_trait.attr_tokenstream).expect("Expected valid tracer attribute");
578 }
579 }
580}