///|
fn hex_digit(value : Int) -> Char {
if value < 10 {
Int::unsafe_to_char('0'.to_int() + value)
} else {
Int::unsafe_to_char('A'.to_int() + value - 10)
}
}
///|
fn write_hex2(out : StringBuilder, value : Int) -> Unit {
out.write_char(hex_digit((value >> 4) & 0x0f))
out.write_char(hex_digit(value & 0x0f))
}
///|
fn write_hex4(out : StringBuilder, value : Int) -> Unit {
out.write_char(hex_digit((value >> 12) & 0x0f))
out.write_char(hex_digit((value >> 8) & 0x0f))
out.write_char(hex_digit((value >> 4) & 0x0f))
out.write_char(hex_digit(value & 0x0f))
}
///|
/// Render non-printing characters as stable ASCII. The result is safe to paste
/// into terminals and useful in audit reports.
pub fn visible(input : String) -> String {
let out = StringBuilder::new(size_hint=input.length())
for c in input {
let n = c.to_int()
if n == 0x1b {
out.write_string("\\x1B")
} else if n == 0x07 {
out.write_string("\\a")
} else if n == 0x08 {
out.write_string("\\b")
} else if n == 0x09 {
out.write_string("\\t")
} else if n == 0x0a {
out.write_string("\\n")
} else if n == 0x0d {
out.write_string("\\r")
} else if n < 0x20 || n == 0x7f || (n >= 0x80 && n <= 0x9f) {
out.write_string("\\x")
write_hex2(out, n & 0xff)
} else if is_bidi_control(c) {
out.write_string("\\u")
write_hex4(out, n)
} else {
out.write_char(c)
}
}
out.to_string()
}
///|
pub fn strip_bidi_controls(input : String) -> String {
let out = StringBuilder::new(size_hint=input.length())
for c in input {
if !is_bidi_control(c) {
out.write_char(c)
}
}
out.to_string()
}
///|
fn json_escape_char(out : StringBuilder, c : Char) -> Unit {
let n = c.to_int()
if c == '"' {
out.write_string("\\\"")
} else if c == '\\' {
out.write_string("\\\\")
} else if c == '\n' {
out.write_string("\\n")
} else if c == '\r' {
out.write_string("\\r")
} else if c == '\t' {
out.write_string("\\t")
} else if n < 0x20 || is_bidi_control(c) {
out.write_string("\\u")
write_hex4(out, n)
} else {
out.write_char(c)
}
}
///|
pub fn json_quote(input : String) -> String {
let out = StringBuilder::new(size_hint=input.length() + 2)
out.write_char('"')
for c in input {
json_escape_char(out, c)
}
out.write_char('"')
out.to_string()
}
///|
pub fn markdown_escape(input : String) -> String {
let out = StringBuilder::new(size_hint=input.length())
for c in input {
if c == '|' || c == '`' || c == '\\' {
out.write_char('\\')
}
if c == '\n' || c == '\r' {
out.write_char(' ')
} else {
out.write_char(c)
}
}
out.to_string()
}
///|
/// GitHub workflow command escaping as documented for annotation properties.
pub fn github_property_escape(input : String) -> String {
let out = StringBuilder::new()
for c in input {
if c == '%' {
out.write_string("%25")
} else if c == '\r' {
out.write_string("%0D")
} else if c == '\n' {
out.write_string("%0A")
} else if c == ':' {
out.write_string("%3A")
} else if c == ',' {
out.write_string("%2C")
} else {
out.write_char(c)
}
}
out.to_string()
}
///|
pub fn github_message_escape(input : String) -> String {
let out = StringBuilder::new()
for c in input {
if c == '%' {
out.write_string("%25")
} else if c == '\r' {
out.write_string("%0D")
} else if c == '\n' {
out.write_string("%0A")
} else {
out.write_char(c)
}
}
out.to_string()
}