facet_macros_impl/lib.rs
1#![warn(missing_docs)]
2#![allow(uncommon_codepoints)]
3//!
4//! [](https://coveralls.io/github/facet-rs/facet?branch=main)
5//! [](https://crates.io/crates/facet-macros-impl)
6//! [](https://docs.rs/facet-macros-impl)
7//! [](./LICENSE)
8//! [](https://discord.gg/JhD7CwCJ8F)
9//!
10//! Implementation of facet derive macros, combining parsing and code generation.
11//!
12//! This crate provides the internal implementation for `#[derive(Facet)]` and related procedural macros. It's used by `facet-macros` (the proc-macro crate) and should not be used directly.
13//!
14#![doc = include_str!("../readme-footer.md")]
15
16// ============================================================================
17// RE-EXPORTS FROM FACET-MACRO-TYPES (grammar) AND FACET-MACRO-PARSE (parsed types)
18// ============================================================================
19
20// Re-export everything from facet-macro-types (includes unsynn, grammar, RenameRule)
21pub use facet_macro_types::*;
22
23// Re-export everything from facet-macro-parse (includes parsed types like PStruct, PEnum, etc.)
24pub use facet_macro_parse::*;
25
26// ============================================================================
27// FUNCTION PARSING (optional feature)
28// ============================================================================
29
30/// Parse function signature shape
31#[cfg(feature = "function")]
32pub mod function;
33
34// ============================================================================
35// CODE EMISSION
36// ============================================================================
37
38mod process_enum;
39mod process_struct;
40
41// ============================================================================
42// DOC STRIPPING DETECTION
43// ============================================================================
44
45/// Returns true if doc strings should be stripped from generated shapes.
46///
47/// Controlled by `--cfg facet_no_doc`. Set via rustflags in .cargo/config.toml,
48/// Cargo.toml profile, or RUSTFLAGS env var. The cfg is evaluated when the
49/// proc-macro is compiled.
50#[cfg(all(feature = "doc", facet_no_doc))]
51pub const fn is_no_doc() -> bool {
52 true
53}
54
55/// Returns true if doc strings should be stripped from generated shapes.
56#[cfg(all(feature = "doc", not(facet_no_doc)))]
57pub const fn is_no_doc() -> bool {
58 false
59}
60
61mod derive;
62pub use derive::*;
63
64mod plugin;
65pub use plugin::*;
66
67mod extension;
68pub use extension::*;
69
70mod on_error;
71pub use on_error::*;
72
73/// Attribute grammar infrastructure for extension crates
74pub mod attr_grammar;
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79 use quote::quote;
80
81 #[test]
82 fn test_struct_with_field_doc_comments() {
83 let input = quote! {
84 #[derive(Facet)]
85 pub struct User {
86 #[doc = " The user's unique identifier"]
87 pub id: u64,
88 }
89 };
90
91 let mut it = input.to_token_iter();
92 let parsed = it.parse::<Struct>().expect("Failed to parse struct");
93
94 // Check that we parsed the struct correctly
95 assert_eq!(parsed.name.to_string(), "User");
96
97 // Extract fields from the struct
98 if let StructKind::Struct { fields, .. } = &parsed.kind {
99 assert_eq!(fields.content.len(), 1);
100
101 // Check first field (id)
102 let id_field = &fields.content[0].value;
103 assert_eq!(id_field.name.to_string(), "id");
104
105 // Extract doc comments from id field
106 let mut doc_found = false;
107 for attr in &id_field.attributes {
108 match &attr.body.content {
109 AttributeInner::Doc(doc_inner) => {
110 // This should work with LiteralString
111 assert_eq!(doc_inner.value, " The user's unique identifier");
112 doc_found = true;
113 }
114 _ => {
115 // Skip non-doc attributes
116 }
117 }
118 }
119 assert!(doc_found, "Should have found a doc comment");
120 } else {
121 panic!("Expected a regular struct with named fields");
122 }
123 }
124}