1use serde_json::Value;
8
9pub fn enabled() -> bool {
13 match std::env::var("SLACK_RS_DEBUG") {
14 Ok(v) => {
15 let v = v.trim().to_ascii_lowercase();
16 matches!(v.as_str(), "1" | "true" | "yes" | "on")
17 }
18 Err(_) => false,
19 }
20}
21
22pub fn log(msg: impl AsRef<str>) {
24 if enabled() {
25 eprintln!("DEBUG: {}", msg.as_ref());
26 }
27}
28
29pub fn token_hint(token: &str) -> String {
33 let kind = if token.starts_with("xoxb-") {
34 "xoxb"
35 } else if token.starts_with("xoxp-") {
36 "xoxp"
37 } else if token.starts_with("xoxa-") {
38 "xoxa"
39 } else if token.starts_with("xoxr-") {
40 "xoxr"
41 } else if token.starts_with("xoxs-") {
42 "xoxs"
43 } else {
44 "token"
45 };
46
47 format!("{} (len={})", kind, token.len())
48}
49
50pub fn redact_json_secrets(json: &str) -> String {
55 let Ok(mut v) = serde_json::from_str::<Value>(json) else {
56 return "<non-json body>".to_string();
57 };
58
59 redact_value_in_place(&mut v);
60 serde_json::to_string(&v).unwrap_or_else(|_| "<unserializable json>".to_string())
61}
62
63fn redact_value_in_place(v: &mut Value) {
64 match v {
65 Value::Object(map) => {
66 for (_k, child) in map.iter_mut() {
67 redact_value_in_place(child);
68 }
69 }
70 Value::Array(items) => {
71 for child in items.iter_mut() {
72 redact_value_in_place(child);
73 }
74 }
75 Value::String(s) => {
76 let trimmed = s.trim();
77 if trimmed.starts_with("xox") {
78 *s = "<redacted>".to_string();
79 }
80 }
81 _ => {}
82 }
83}