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