use alloc::borrow::{Cow, ToOwned};
use alloc::string::String;
use alloc::vec::Vec;
#[cfg(not(feature = "thread-safe"))]
use core::cell::{Ref, RefCell};
use core::fmt::{Display, Formatter, Result};
#[cfg(feature = "thread-safe")]
use std::sync::{RwLock, RwLockReadGuard};
pub struct Indented<TDisplay>(pub TDisplay)
where
TDisplay: Display;
impl<TDisplay> Display for Indented<TDisplay>
where
TDisplay: Display,
{
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
write!(f, " ")?;
self.0.fmt(f)
}
}
#[cfg(test)]
mod test_indented {
use super::*;
use alloc::string::ToString;
#[test]
fn test_write_indented() {
assert_eq!(" foo", Indented("foo").to_string());
assert_eq!(" ", Indented("".to_string()).to_string());
}
}
#[inline]
pub fn escape(s: &str) -> Cow<'_, str> {
escape_impl(s, false, false)
}
#[inline]
pub fn escape_path(s: &str) -> Cow<'_, str> {
escape_impl(s, true, false)
}
#[inline]
pub fn escape_build(s: &str) -> Cow<'_, str> {
escape_impl(s, true, true)
}
pub fn escape_impl(s: &str, escape_space: bool, escape_colon: bool) -> Cow<'_, str> {
let mut output: Option<String> = None;
for (i, c) in s.char_indices() {
let escape = match c {
'$' => true,
'\n' => true,
' ' => escape_space,
':' => escape_colon,
_ => false,
};
match output.as_mut() {
Some(output) => {
if escape {
output.push('$');
}
output.push(c);
}
None => {
if escape {
let before = &s[..i];
let mut copied = before.to_owned();
copied.push('$');
copied.push(c);
output = Some(copied);
}
}
}
}
match output {
Some(output) => Cow::Owned(output),
None => Cow::Borrowed(s),
}
}
#[cfg(test)]
mod test_escape {
use super::*;
fn run_test_on_all(input: &str, output: &str) {
assert_eq!(escape(input), output);
assert_eq!(escape_path(input), output);
assert_eq!(escape_build(input), output);
}
fn run_test_space(input: &str, output_no_escape_space: &str, output: &str) {
assert_eq!(escape(input), output_no_escape_space);
assert_eq!(escape_path(input), output);
assert_eq!(escape_build(input), output);
}
fn run_test_colon_no_space(input: &str, output_no_escape_colon: &str, output: &str) {
assert_eq!(escape(input), output_no_escape_colon);
assert_eq!(escape_path(input), output_no_escape_colon);
assert_eq!(escape_build(input), output);
}
#[test]
fn test_empty() {
run_test_on_all("", "");
}
#[test]
fn test_no_escape() {
run_test_on_all("foo", "foo");
run_test_on_all("foo,bar", "foo,bar");
}
#[test]
fn test_newline() {
run_test_on_all("foo\nbar", "foo$\nbar");
run_test_on_all("foo$\nbar", "foo$$$\nbar");
run_test_on_all("\nfoobar\n", "$\nfoobar$\n");
}
#[test]
fn test_dollar() {
run_test_on_all("foo$$bar", "foo$$$$bar");
run_test_on_all("foo$bar", "foo$$bar");
run_test_on_all("$foobar$", "$$foobar$$");
}
#[test]
fn test_space() {
run_test_space("foo bar", "foo bar", "foo$ bar");
run_test_space(" foo bar ", " foo bar ", "$ foo$ bar$ ");
run_test_space("foo bar", "foo bar", "foo$ $ bar");
run_test_space("foo\nb a r$baz", "foo$\nb a r$$baz", "foo$\nb$ a$ r$$baz");
}
#[test]
fn test_colon() {
run_test_colon_no_space("foo:bar", "foo:bar", "foo$:bar");
run_test_colon_no_space("foo::bar", "foo::bar", "foo$:$:bar");
run_test_colon_no_space("$foo:bar\n", "$$foo:bar$\n", "$$foo$:bar$\n");
}
#[test]
fn test_all() {
let input = "foo$\nb ar$$baz$:$qux";
assert_eq!(escape(input), "foo$$$\nb ar$$$$baz$$:$$qux");
assert_eq!(escape_path(input), "foo$$$\nb$ ar$$$$baz$$:$$qux");
assert_eq!(escape_build(input), "foo$$$\nb$ ar$$$$baz$$$:$$qux");
let input = "\u{4f60}he llo\u{597d}$\nb: ";
assert_eq!(escape(input), "\u{4f60}he llo\u{597d}$$$\nb: ");
assert_eq!(escape_path(input), "\u{4f60}he$ llo\u{597d}$$$\nb:$ ");
assert_eq!(escape_build(input), "\u{4f60}he$ llo\u{597d}$$$\nb$:$ ");
}
}
#[cfg(feature = "thread-safe")]
pub type RefCounted<T> = alloc::sync::Arc<T>;
#[cfg(not(feature = "thread-safe"))]
pub type RefCounted<T> = alloc::rc::Rc<T>;
#[derive(Debug)]
pub struct AddOnlyVec<T> {
#[cfg(feature = "thread-safe")]
inner: RwLock<Vec<T>>,
#[cfg(not(feature = "thread-safe"))]
inner: RefCell<Vec<T>>,
}
#[cfg(feature = "thread-safe")]
pub type VecInnerGuard<'a, T> = RwLockReadGuard<'a, Vec<T>>;
#[cfg(not(feature = "thread-safe"))]
pub type VecInnerGuard<'a, T> = Ref<'a, Vec<T>>;
impl<T> AddOnlyVec<T> {
pub fn new() -> Self {
#[cfg(feature = "thread-safe")]
{
Self {
inner: RwLock::new(Vec::new()),
}
}
#[cfg(not(feature = "thread-safe"))]
{
Self {
inner: RefCell::new(Vec::new()),
}
}
}
pub fn add(&self, element: T) {
#[cfg(feature = "thread-safe")]
self.inner.write().unwrap().push(element);
#[cfg(not(feature = "thread-safe"))]
self.inner.borrow_mut().push(element);
}
pub fn extend<TIter>(&self, iter: TIter)
where
TIter: IntoIterator<Item = T>,
{
#[cfg(feature = "thread-safe")]
self.inner.write().unwrap().extend(iter);
#[cfg(not(feature = "thread-safe"))]
self.inner.borrow_mut().extend(iter);
}
pub fn inner(&self) -> VecInnerGuard<'_, T> {
#[cfg(feature = "thread-safe")]
return self.inner.read().unwrap();
#[cfg(not(feature = "thread-safe"))]
self.inner.borrow()
}
}
impl<T> Default for AddOnlyVec<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> AddOnlyVec<RefCounted<T>> {
pub fn add_rc(&self, element: T) -> RefCounted<T> {
let rc = RefCounted::new(element);
self.add(RefCounted::clone(&rc));
rc
}
}