1 | use std::fmt::Write; |
2 | |
3 | use crate::cargo; |
4 | |
5 | pub(crate) fn mangled(defmt_tag: &str, data: &str) -> String { |
6 | Symbol::new(defmt_tag, data).mangle() |
7 | } |
8 | |
9 | struct 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 | |
42 | impl<'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 | |
66 | fn 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 | |