1use std::fmt::Write;
2
3use crate::cargo;
4
5pub(crate) fn mangled(defmt_tag: &str, data: &str) -> String {
6 Symbol::new(defmt_tag, data).mangle()
7}
8
9struct Symbol<'a> {
10 /// Name of the Cargo package in which the symbol is being instantiated. Used for avoiding
11 /// symbol name collisions.
12 package: String,
13
14 /// Unique identifier that disambiguates otherwise equivalent invocations in the same crate.
15 disambiguator: u64,
16
17 /// Symbol categorization. Known values:
18 /// * `defmt_prim` for primitive formatting strings that are placed at the start of the `.defmt`
19 /// section.
20 /// * `defmt_fmt`, `defmt_str` for interned format strings and string literals.
21 /// * `defmt_println` for logging messages that are always displayed.
22 /// * `defmt_trace`, `defmt_debug`, `defmt_info`, `defmt_warn`, `defmt_error` for logging
23 /// messages used at the different log levels.
24 /// * `defmt_bitflags` indicates that a format string was generated by a `defmt::bitflags!`
25 /// invocation, and that the decoder should look up possible flags in the binary.
26 /// The data string is of the format `NAME@REPR#NUM`, where `NAME` is the name of the bitflags
27 /// struct, `REPR` is the raw integer type representing the bitflags value (and also the
28 /// wire format), and `NUM` is the number of defined bitflag values.
29 /// * `defmt_bitflags_value` marks a `static` that holds the value of a bitflags `const`, its
30 /// data field is `STRUCT_NAME::FLAG_NAME`.
31 /// * Anything starting with `defmt_` is reserved for use by defmt, other prefixes are free for
32 /// use by third-party apps (but they all should use a prefix!).
33 tag: String,
34
35 /// Symbol data for use by the host tooling. Interpretation depends on `tag`.
36 data: &'a str,
37
38 /// Crate name obtained via CARGO_CRATE_NAME (added since a Cargo package can contain many crates).
39 crate_name: String,
40}
41
42impl<'a> Symbol<'a> {
43 fn new(tag: &'a str, data: &'a str) -> Self {
44 Self {
45 // `CARGO_PKG_NAME` is set to the invoking package's name.
46 package: cargo::package_name(),
47 disambiguator: super::crate_local_disambiguator(),
48 tag: format!("defmt_{tag}"),
49 data,
50 crate_name: cargo::crate_name(),
51 }
52 }
53
54 fn mangle(&self) -> String {
55 format!(
56 r#"{{"package":"{}","tag":"{}","data":"{}","disambiguator":"{}","crate_name":"{}"}}"#,
57 json_escape(&self.package),
58 json_escape(&self.tag),
59 json_escape(self.data),
60 self.disambiguator,
61 json_escape(&self.crate_name),
62 )
63 }
64}
65
66fn json_escape(string: &str) -> String {
67 let mut escaped: String = String::new();
68 for c: char in string.chars() {
69 match c {
70 '\\' => escaped.push_str(string:"\\\\"),
71 '\"' => escaped.push_str(string:"\\\""),
72 '\n' => escaped.push_str(string:"\\n"),
73 c: char if c.is_control() || c == '@' => write!(escaped, "\\u{:04x}", c as u32).unwrap(),
74 c: char => escaped.push(ch:c),
75 }
76 }
77 escaped
78}
79