Mountain/ProcessManagement/ExtractDevTag.rs
1//! Cocoon stdout-line inspector. Detects the `[DEV:<TAG>]` prefix written by
2//! `Cocoon/Source/Services/DevLog.ts::CocoonDevLog` and returns the lower-
3//! cased tag for dispatch into Mountain's per-tag `dev_log!` sinks. Returns
4//! `None` for bare stdout so the caller falls back to the catch-all `cocoon`
5//! tag.
6
7pub fn Fn(Line:&str) -> Option<String> {
8 let Stripped = Line.strip_prefix("[DEV:")?;
9
10 let (TagUpper, _Rest) = Stripped.split_once(']')?;
11
12 if TagUpper.is_empty() {
13 return None;
14 }
15
16 // Reject anything that isn't a simple tag ident - prevents stray
17 // `[DEV: something with space]` headers from being treated as tags.
18 if !TagUpper.chars().all(|C| C.is_ascii_uppercase() || C == '-' || C == '_') {
19 return None;
20 }
21
22 Some(TagUpper.to_ascii_lowercase())
23}
24
25#[cfg(test)]
26mod Tests {
27
28 use super::Fn;
29
30 #[test]
31 fn StripsKnownTag() {
32 assert_eq!(
33 Fn("[DEV:BOOTSTRAP-STAGE] [Bootstrap] stage=Environment event=start"),
34 Some("bootstrap-stage".to_string())
35 );
36 }
37
38 #[test]
39 fn RejectsPlainText() {
40 assert_eq!(Fn("plain stdout line"), None);
41 }
42
43 #[test]
44 fn RejectsMalformed() {
45 assert_eq!(Fn("[DEV: BOOT] x"), None);
46
47 assert_eq!(Fn("[DEV:]"), None);
48
49 assert_eq!(Fn("[DEV:BOOT"), None);
50 }
51}