Skip to main content

spider_macro/
lib.rs

1//! # spider-macro
2//!
3//! Provides procedural macros for the `spider-lib` framework to reduce boilerplate code.
4//!
5//! ## Overview
6//!
7//! The `spider-macro` crate contains procedural macros that automate the
8//! implementation of common traits and patterns used in the spider framework.
9//! These macros significantly reduce the amount of boilerplate code required
10//! when defining custom data structures for scraped items and spider state.
11//!
12//! ## Key Macros
13//!
14//! - **`#[scraped_item]`**: Derives the `ScrapedItem` trait along with necessary
15//!   implementations for serialization, deserialization, cloning, and type
16//!   conversions.
17//!
18//! ## Features
19//!
20//! - **Automatic Trait Derivation**: Implements `Serialize`, `Deserialize`,
21//!   `Clone`, and `Debug` traits automatically
22//! - **ScrapedItem Implementation**: Provides the complete implementation of
23//!   the `ScrapedItem` trait required by the framework
24//! - **Type Safety**: Maintains type safety while reducing boilerplate
25//! - **Performance**: Generates efficient code without runtime overhead
26//!
27//! ## Dependencies
28//!
29//! When using this macro, your project must include the following dependencies:
30//!
31//! ```toml
32//! [dependencies]
33//! spider-lib = "1.1.1"
34//! serde = { version = "1.0", features = ["derive"] }
35//! serde_json = "1.0"
36//! ```
37//!
38//! ## Example
39//!
40//! ```rust,ignore
41//! use spider_lib::prelude::*;
42//!
43//! #[scraped_item]
44//! struct Article {
45//!     title: String,
46//!     content: String,
47//!     author: String,
48//!     published_date: String,
49//! }
50//!
51//! // The macro generates all necessary implementations automatically
52//! // including serialization, deserialization, and the ScrapedItem trait
53//! //
54//! // Note: Make sure your Cargo.toml includes serde and serde_json as dependencies
55//! ```
56
57extern crate proc_macro;
58
59use proc_macro::TokenStream;
60use quote::quote;
61use syn::{ItemStruct, parse_macro_input};
62
63/// A procedural macro to derive the `ScrapedItem` trait.
64///
65/// This macro:
66/// 1. Implements the ScrapedItem trait
67/// 2. Adds serde Serialize and Deserialize derives
68/// 3. Makes use of items that should be in scope via prelude import
69///
70/// # Dependencies
71///
72/// Your project must include `serde` and `serde_json` as direct dependencies:
73///
74/// ```toml
75/// [dependencies]
76/// serde = { version = "1.0", features = ["derive"] }
77/// serde_json = "1.0"
78/// ```
79#[proc_macro_attribute]
80pub fn scraped_item(_attr: TokenStream, item: TokenStream) -> TokenStream {
81    let ast = parse_macro_input!(item as ItemStruct);
82    let name = &ast.ident;
83
84    let expanded = quote! {
85        #[derive(
86            ::serde::Serialize,
87            ::serde::Deserialize,
88            Clone,
89            Debug
90        )]
91        #ast
92
93        impl ScrapedItem for #name {
94            fn as_any(&self) -> &dyn ::std::any::Any {
95                self
96            }
97
98            fn box_clone(&self) -> Box<dyn ScrapedItem + Send + Sync> {
99                Box::new(self.clone())
100            }
101
102            fn to_json_value(&self) -> ::serde_json::Value {
103                match ::serde_json::to_value(self) {
104                    Ok(value) => value,
105                    Err(err) => panic!("failed to serialize ScrapedItem '{}': {}", stringify!(#name), err),
106                }
107            }
108        }
109    };
110
111    TokenStream::from(expanded)
112}