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
//! Shared state file for cross-macro communication between `installed_apps!`
//! and other macros.
//!
//! These proc macros expand within the same user crate, but cannot share data through
//! Rust's type system. This module provides file-based state sharing:
//!
//! - `installed_apps!` writes the list of app labels to a state file
//! - Other macros may read the file for app discovery
//!
//! This eliminates the need for the `#[macro_export]` callback pattern that triggers
//! `macro_expanded_macro_exports_accessed_by_absolute_paths` on Rust 1.96+.
//!
//! ## Why `CARGO_MANIFEST_DIR/target/` instead of `OUT_DIR`?
//!
//! `OUT_DIR` is only available during build-script execution, not during proc-macro
//! expansion. Proc macros have access to `CARGO_MANIFEST_DIR` via `std::env::var`,
//! so the state file is placed under `$CARGO_MANIFEST_DIR/target/reinhardt/`.
//!
//! ## Why a per-crate subdirectory? (Issue #4592)
//!
//! When a single Cargo manifest contains multiple compilation units that each expand
//! `installed_apps!` independently — most commonly the `[[test]]` targets under
//! `tests/integration/tests/*.rs` — cargo invokes rustc in parallel for every target.
//! All those rustc instances see the same `CARGO_MANIFEST_DIR`, so a flat state path
//! would race: one binary's `installed_apps!` would overwrite the labels another
//! binary's `#[routes]` is about to read, producing spurious E0433 errors.
//!
//! Cargo additionally sets `CARGO_CRATE_NAME` per compilation unit. We use it as a
//! subdirectory under `target/reinhardt/`, isolating each binary's state file.
//!
//! Both `CARGO_MANIFEST_DIR` and `CARGO_CRATE_NAME` are treated as hard requirements
//! and surface as `compile_error!` if missing. This matches the symmetric hard-fail
//! in the `include_bytes!` tracker emitted by `#[routes]`
//! (`routes_registration.rs`), which uses `env!("CARGO_MANIFEST_DIR")` and
//! `env!("CARGO_CRATE_NAME")` — `concat!()` cannot consume `option_env!()` with a
//! compile-time fallback, so emitting a runtime sentinel on one side and a
//! compile-time hard-fail on the other would be inconsistent. Both sides hard-fail
//! together (Issue #4592 / CodeRabbit thread).
use PathBuf;
/// File name for the installed apps state.
const STATE_FILE_NAME: &str = ".installed_apps";
/// Subdirectory under `target/` for reinhardt state files.
const STATE_SUBDIR: &str = "reinhardt";
/// Composes the state directory path. Pure function — extracted so it can be unit
/// tested without mutating process env vars.
/// Returns the directory path for state files:
/// `$CARGO_MANIFEST_DIR/target/reinhardt/$CARGO_CRATE_NAME/`.
/// Writes the installed app labels to the state file.
///
/// Creates the directory structure if it does not exist.
/// Labels are written as newline-separated UTF-8 text.
///
/// Returns an error if the directory cannot be created or the file cannot be written.
pub