Skip to content

Commit

Permalink
feat(stdx): Add stdx::fmt::Separator for sequence formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
tingerrr committed Aug 23, 2024
1 parent 3f0758b commit 6b9b987
Showing 1 changed file with 163 additions and 0 deletions.
163 changes: 163 additions & 0 deletions crates/typst-test-stdx/src/fmt.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Helper functions and types for formatting.
use std::cell::RefCell;
use std::fmt::Display;

/// Types which affect the plurality of a word. Mostly numbers.
Expand Down Expand Up @@ -110,3 +111,165 @@ impl Display for PluralDisplay<'_> {
}
}
}

/// Displays a sequence of elements as comma separated list with a final
/// separator.
///
/// # Examples
/// ```
/// # use typst_test_stdx::fmt::Separators;
/// assert_eq!(
/// Separators::new(", ", " or ").with(["a", "b", "c"]).to_string(),
/// "a, b or c",
/// );
/// assert_eq!(
/// Separators::comma_or().with(["a", "b", "c"]).to_string(),
/// "a, b or c",
/// );
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Separators {
separator: &'static str,
terminal_separator: Option<&'static str>,
}

impl Separators {
/// Creates a new sequence to display.
///
/// # Examples
/// ```
/// # use typst_test_stdx::fmt::Separators;
/// assert_eq!(
/// Separators::new("-", None).with(["a", "b", "c"]).to_string(),
/// "a-b-c",
/// );
/// assert_eq!(
/// Separators::new("-", "/").with(["a", "b", "c"]).to_string(),
/// "a-b/c",
/// );
/// ```
pub fn new(
separator: &'static str,
terminal_separator: impl Into<Option<&'static str>>,
) -> Self {
Self {
separator,
terminal_separator: terminal_separator.into(),
}
}

/// Creates a new sequence to display using only `, ` as separtor.
///
/// # Examples
/// ```
/// # use typst_test_stdx::fmt::Separators;
/// assert_eq!(
/// Separators::comma().with(["a", "b"]).to_string(),
/// "a, b",
/// );
/// assert_eq!(
/// Separators::comma().with(["a", "b", "c"]).to_string(),
/// "a, b, c",
/// );
/// ```
pub fn comma() -> Self {
Self::new(", ", None)
}

/// Creates a new sequence to display using `, ` and ` or ` as the separtors.
///
/// # Examples
/// ```
/// # use typst_test_stdx::fmt::Separators;
/// assert_eq!(
/// Separators::comma_or().with(["a", "b"]).to_string(),
/// "a or b",
/// );
/// assert_eq!(
/// Separators::comma_or().with(["a", "b", "c"]).to_string(),
/// "a, b or c",
/// );
/// ```
pub fn comma_or() -> Self {
Self::new(", ", " or ")
}

/// Creates a new sequence to display using `, ` and ` and ` as the separtors.
///
/// # Examples
/// ```
/// # use typst_test_stdx::fmt::Separators;
/// assert_eq!(
/// Separators::comma_and().with(["a", "b"]).to_string(),
/// "a and b",
/// );
/// assert_eq!(
/// Separators::comma_and().with(["a", "b", "c"]).to_string(),
/// "a, b and c",
/// );
/// ```
pub fn comma_and() -> Self {
Self::new(", ", " and ")
}

/// Formats th given items with this sequence's separators.
///
/// # Examples
/// ```
/// # use typst_test_stdx::fmt::Separators;
/// assert_eq!(
/// Separators::new(", ", " or ").with(["a", "b", "c"]).to_string(),
/// "a, b or c",
/// );
/// assert_eq!(
/// Separators::comma_or().with(["a", "b", "c"]).to_string(),
/// "a, b or c",
/// );
/// ```
pub fn with<S>(self, items: S) -> impl Display
where
S: IntoIterator,
S::IntoIter: ExactSizeIterator,
S::Item: Display,
{
SequenceDisplay {
seq: self,
items: RefCell::new(items.into_iter()),
}
}
}

struct SequenceDisplay<I> {
seq: Separators,
items: RefCell<I>,
}

impl<I> Display for SequenceDisplay<I>
where
I: ExactSizeIterator,
I::Item: Display,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut items = self.items.try_borrow_mut().expect("is not Sync");
let mut items = items.by_ref().enumerate();
let len = items.len();

if let Some((_, item)) = items.next() {
write!(f, "{item}")?;
} else {
return Ok(());
}

for (idx, item) in items {
let sep = if idx == len - 1 {
self.seq.terminal_separator.unwrap_or(self.seq.separator)
} else {
self.seq.separator
};

write!(f, "{sep}{item}")?;
}

Ok(())
}
}

0 comments on commit 6b9b987

Please sign in to comment.