scolapasta_string_escape/lib.rs
1#![warn(clippy::all, clippy::pedantic, clippy::undocumented_unsafe_blocks)]
2#![allow(
3 clippy::let_underscore_untyped,
4 reason = "https://github.com/rust-lang/rust-clippy/pull/10442#issuecomment-1516570154"
5)]
6#![allow(
7 clippy::question_mark,
8 reason = "https://github.com/rust-lang/rust-clippy/issues/8281"
9)]
10#![allow(clippy::manual_let_else, reason = "manual_let_else was very buggy on release")]
11#![allow(clippy::missing_errors_doc, reason = "A lot of existing code fails this lint")]
12#![allow(
13 clippy::unnecessary_lazy_evaluations,
14 reason = "https://github.com/rust-lang/rust-clippy/issues/8109"
15)]
16#![cfg_attr(
17 test,
18 allow(clippy::non_ascii_literal, reason = "tests sometimes require UTF-8 string content")
19)]
20#![allow(unknown_lints)]
21#![warn(
22 missing_copy_implementations,
23 missing_debug_implementations,
24 missing_docs,
25 rust_2024_compatibility,
26 trivial_casts,
27 trivial_numeric_casts,
28 unused_qualifications,
29 variant_size_differences
30)]
31#![forbid(unsafe_code)]
32// Enable feature callouts in generated documentation:
33// https://doc.rust-lang.org/beta/unstable-book/language-features/doc-cfg.html
34//
35// This approach is borrowed from tokio.
36#![cfg_attr(docsrs, feature(doc_cfg))]
37#![cfg_attr(docsrs, feature(doc_alias))]
38
39//! Routines for debug escaping Ruby Strings.
40//!
41//! Ruby Strings are conventionally UTF-8 byte sequences. When calling
42//! [`String#inspect`] or [`Symbol#inspect`], these maybe UTF-8 byte strings are
43//! escaped to have a valid and printable UTF-8 representation.
44//!
45//! This crate exposes functions and iterators for encoding arbitrary byte
46//! slices as valid, printable UTF-8.
47//!
48//! # Ruby debug escapes
49//!
50//! Ruby produces debug escapes that look like:
51//!
52//! ```console
53//! [2.6.3] > "Artichoke Ruby is made with Rust.
54//!
55//! Invalid UTF-8: \xFF.
56//!
57//! Slash \\ and quote \" are escaped."
58//! => "Artichoke Ruby is made with Rust.\n\nInvalid UTF-8: \xFF.\n\nSlash \\ and quote \" are escaped."
59//! ```
60//!
61//! Ruby escape sequences differ than Rust escape sequences for some characters.
62//! For example `0x0C`:
63//!
64//! ```
65//! # use scolapasta_string_escape::Literal;
66//! // Rust
67//! assert_eq!('\x0C'.escape_debug().collect::<String>(), r"\u{c}");
68//! // Ruby
69//! assert_eq!(Literal::from(0x0C).as_str(), r"\f");
70//! ```
71//!
72//! # Examples
73//!
74//! ```
75//! # use scolapasta_string_escape::format_debug_escape_into;
76//! const EXAMPLE: &[u8] = b"Artichoke Ruby is made with Rust.
77//!
78//! Invalid UTF-8: \xFF.
79//!
80//! Slash \\ and quote \" are escaped.";
81//!
82//! # fn example() -> Result<(), core::fmt::Error> {
83//! let mut escaped = String::new();
84//! format_debug_escape_into(&mut escaped, EXAMPLE)?;
85//! assert_eq!(
86//! escaped,
87//! r#"Artichoke Ruby is made with Rust.\n\nInvalid UTF-8: \xFF.\n\nSlash \\ and quote \" are escaped."#,
88//! );
89//! # Ok(())
90//! # }
91//! # example().unwrap();
92//! ```
93//!
94//! # `no_std`
95//!
96//! This crate is `no_std` compatible. This crate does not depend on [`alloc`].
97//!
98//! [`String#inspect`]: https://ruby-doc.org/core-3.1.2/String.html#method-i-inspect
99//! [`Symbol#inspect`]: https://ruby-doc.org/core-3.1.2/Symbol.html#method-i-inspect
100//! [`alloc`]: https://doc.rust-lang.org/alloc/
101
102#![no_std]
103
104// Ensure code blocks in `README.md` compile
105#[cfg(doctest)]
106#[doc = include_str!("../README.md")]
107mod readme {}
108
109// Having access to `String` in tests is convenient to collect `Inspect`
110// iterators for whole content comparisons.
111#[cfg(any(test, doctest))]
112extern crate alloc;
113
114#[cfg(feature = "std")]
115extern crate std;
116
117mod literal;
118mod string;
119
120pub use literal::*;
121pub use string::*;