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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
//! The single Java source-literal escaper.
//!
//! Wire names (`#[serde(rename)]`, `#[serde(tag)]`, `#[serde(content)]` and `rename_all` output)
//! are arbitrary Rust string literals. Every one of them is interpolated into Java *source* —
//! `@JsonProperty("...")`, `@JsonSubTypes.Type(name = "...")`, `case "..." ->`,
//! `gen.writeStringField("...", tag)` — so a name carrying a double quote, a backslash or a
//! newline terminates the literal it was pasted into and the generated package stops compiling.
//! Backends must route every such interpolation through this module instead of pasting the raw
//! name between two quote characters.
//!
//! # Why a backslash may never survive unchanged
//!
//! JLS §3.3 processes Unicode escapes in the *first* translation step, before the source is even
//! lexed. A Unicode escape naming U+0022 is therefore a real double quote *everywhere* — inside
//! `//` comments, inside `/** */` javadoc — and a `\u` followed by anything that is not four hex
//! digits is `illegal unicode escape` rather than four harmless characters. A wire name
//! containing a literal backslash-`u` is consequently a compile error in emitted source that a
//! naive "escape the quotes" pass would call safe.
//!
//! JLS §3.3 also says a backslash is only *eligible* to start a Unicode escape when preceded by
//! an even number of backslashes. Doubling every backslash therefore disarms the pre-lexer: the
//! `\` in front of a `u` always has an odd-count run of backslashes before it and is read as
//! data. ~keep
//!
//! # Why control characters use octal, not `\uXXXX`
//!
//! `\uXXXX` is substituted before lexing, so a Unicode escape naming U+000A becomes a *real*
//! line terminator and ends the literal. Java's octal escapes (`\000`–`\377`) are lexer-level
//! and cannot do that, so control characters without a named escape use the three-digit octal
//! form. Non-ASCII characters have no such hazard and are emitted as `\uXXXX` UTF-16 code units
//! (an astral character becomes a surrogate pair) to keep generated sources pure ASCII: `javac`
//! decodes source with the platform charset unless told otherwise, and a raw UTF-8 byte sequence
//! read as a single-byte charset is silently mangled rather than rejected. ~keep
/// The highest code point Java's octal escape (`\000`–`\377`) can express.
const MAX_OCTAL_ESCAPE: u32 = 0o377;
/// First code point that is not plain ASCII.
const FIRST_NON_ASCII: u32 = 0x80;
/// Escape `value` for embedding between the double quotes of a Java string literal.
///
/// The result is pure ASCII, contains no line terminator, and contains no backslash that is
/// eligible to start a Unicode escape. It is therefore also safe inside a Java comment, except
/// that a comment additionally has to survive `*/` — use [`escape_java_comment_text`] there.
///
/// Callers supply their own surrounding quotes; this escapes the literal *content* only.
/// Escape `value` for embedding into Java comment text (`//`, `/* */`, `/** */`).
///
/// Everything [`escape_java_string_literal`] guarantees, plus: no `*/` that would close the
/// comment early, and no bare `<`, `>` or `&` that javadoc's HTML reader (or a doclint run)
/// would interpret as markup.
/// Emit one scalar that has no named escape: printable ASCII verbatim, any remaining control
/// character as three-digit octal, everything else as `\uXXXX` UTF-16 code units.