1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// Proc-macro crate: runs at build time only, so the runtime hot-path
// lints are not relevant here.
//! Procedural macros for the obs SDK.
//!
//! Phase-1 surface (impl-plan task 1.9):
//!
//! - [`Event`] (`#[derive(Event)]`) — emits `EventSchema` impl, `EventSchemaErased` impl,
//! `linkme::distributed_slice` registration, typed builder, and the const-eval lint block
//! (L001/L002/L003/L011).
//! - [`emit`] — terse `obs::emit!(MyEvent { … })` shorthand.
//! - [`scope`] — placeholder (full impl in Phase 3 task 3.3).
//!
//! See spec 12 § 1.2 (Rust-first authoring) and spec 13 § 2 (`obs::scope!`).
use TokenStream;
/// Derive macro for the Rust-first authoring path.
///
/// Container attributes:
///
/// - `#[event(tier = "log" | "metric" | "trace" | "audit")]`
/// - `#[event(default_sev = "trace" | "debug" | "info" | "warn" | "error" | "fatal")]`
/// - `#[event(full_name = "myapp.v1.ObsXxx")]` (defaults to `<crate>.v1.<TypeName>` derived from
/// `module_path!`).
///
/// Field attributes:
///
/// - `#[obs(label, cardinality = "low" | "medium" | "high" | "unbounded")]`
/// - `#[obs(attribute, classification = "internal" | "pii" | "secret")]`
/// - `#[obs(measurement)]`
/// - `#[obs(trace_id)]`, `#[obs(span_id)]`, `#[obs(parent_span_id)]`
/// - `#[obs(forensic)]`
///
/// Lints (compile-time `const _: () = { assert!(...) }` blocks):
///
/// - **L001** — every `LABEL` field must declare a `Low` or `Medium` cardinality.
/// - **L002** — `PII`-classified fields must not be `LABEL`.
/// - **L003** — `SECRET`-classified fields must not exist on `LOG` or `AUDIT` tier events.
/// - **L011** — the type name must start with the workspace event prefix (default `Obs`).
///
/// See spec 12 § 3.4.
/// Function-like emit macro: `obs::emit!(MyEvent { field: value })`
/// or `obs::emit!(WARN, MyEvent { field: value })` to escalate.
///
/// Spec 13 § 1.
/// `obs::include_schemas!("myapp.v1")` — wire up every file
/// `obs-build` emits into the user's crate. Expands to four
/// `include!` calls under `OUT_DIR/obs/`. Spec 12 § 3.1.
/// `obs::scope!(name = value, ...)` — push an `obs::scope!` frame
/// onto the active task's scope stack. Returns a `ScopeGuard` that
/// pops the frame on drop and flushes the tail-on-error buffer when
/// `>= ERROR` was observed inside.
///
/// Spec 13 § 2.
/// `obs::context!(name = value, ...)` — like `obs::scope!` but without
/// the per-scope tail buffer. Spec 13 § 2.2.
/// `obs::forensic!(site = "...", message = "...", { "k" => v, ... })`
/// — emergency escape hatch. Always emits, regardless of sampling.
/// Spec 13 § 8.
/// `#[obs::instrument]` — wraps a function body in an `obs::scope!`
/// and emits one `ObsFnExecuted` event on exit (default) or two
/// (`ObsFnEntered` + `ObsFnExecuted`) when `enter = true`.
///
/// Spec 13 § 5.
/// `#[obs::test]` — drop-in replacement for `#[test]` /
/// `#[tokio::test]` that installs an `InMemoryObserver` on the
/// current thread (sync) or current task (async) for the duration of
/// the test. The body's emits land in a thread-local /
/// task-local handle that `obs::test::assert_emitted!` reads.
///
/// Sync example:
///
/// ```ignore
/// #[obs::test]
/// fn login_emits_event() -> anyhow::Result<()> {
/// login("alice")?;
/// obs::test::assert_emitted!(ObsLoggedIn { user: "alice", .. });
/// Ok(())
/// }
/// ```
///
/// Async example:
///
/// ```ignore
/// #[obs::test]
/// async fn billing_emits_charge_event() -> anyhow::Result<()> {
/// charge_card("4242…").await?;
/// obs::test::assert_emitted!(ObsChargeAttempted { outcome: "approved", .. });
/// Ok(())
/// }
/// ```
///
/// Spec 60 § 8 + spec 72 § 3.