| 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 | |