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