Skip to main content

facet_macros_impl/
lib.rs

1#![warn(missing_docs)]
2#![allow(uncommon_codepoints)]
3//!
4//! [![Coverage Status](https://coveralls.io/repos/github/facet-rs/facet-macros-impl/badge.svg?branch=main)](https://coveralls.io/github/facet-rs/facet?branch=main)
5//! [![crates.io](https://img.shields.io/crates/v/facet-macros-impl.svg)](https://crates.io/crates/facet-macros-impl)
6//! [![documentation](https://docs.rs/facet-macros-impl/badge.svg)](https://docs.rs/facet-macros-impl)
7//! [![MIT/Apache-2.0 licensed](https://img.shields.io/crates/l/facet-macros-impl.svg)](./LICENSE)
8//! [![Discord](https://img.shields.io/discord/1379550208551026748?logo=discord&label=discord)](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}