use std::borrow::Cow;
use super::{cbordiagnostic, DelimiterPolicy, InconsistentEdn, Unparse};
#[derive(Copy, Clone)]
pub(super) enum Comment {
Slashed,
Hashed,
}
pub(super) struct SDetails<'input> {
pub(super) data: &'input str,
pub(super) last_comment_style: Option<Comment>,
}
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct S<'a>(pub(crate) Cow<'a, str>);
impl Default for S<'_> {
fn default() -> Self {
S(Cow::Borrowed(""))
}
}
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct MS<'a>(pub(crate) Cow<'a, str>);
impl Default for MS<'_> {
fn default() -> Self {
MS(Cow::Borrowed(" "))
}
}
#[derive(Debug, Clone, PartialEq)]
#[allow(clippy::upper_case_acronyms)] pub(crate) struct MSC<'a>(pub(crate) Cow<'a, str>);
impl Default for MSC<'_> {
fn default() -> Self {
MSC(Cow::Borrowed(","))
}
}
#[derive(Debug, Clone, PartialEq)]
#[allow(clippy::upper_case_acronyms)] pub(crate) struct SOC<'a>(pub(crate) Cow<'a, str>);
impl Default for SOC<'_> {
fn default() -> Self {
SOC(Cow::Borrowed(""))
}
}
pub(crate) trait Spaceish {
fn prepend_comment(&mut self, comment: &str);
}
macro_rules! spaceish {
($t:ident, $may_have_comma:expr, $is_ms:expr, $is_soc:expr) => {
impl Unparse for $t<'_> {
fn serialize_write(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
formatter.write_str(&self.0)
}
fn to_cbor(&self) -> Result<impl Iterator<Item = u8>, InconsistentEdn> {
Ok(core::iter::empty())
}
}
impl<'a> $t<'a> {
pub(crate) fn set_delimiters(
&mut self,
policy: DelimiterPolicy,
potential_start_of_line: bool,
) {
use DelimiterPolicy::*;
match policy {
DiscardAll => {
*self = Default::default();
}
DiscardAllButComments
| SingleLineRegularSpacing
| IndentedRegularSpacing { .. } => {
fn trim_one(parsed: SDetails<'_>) -> &str {
if let Some(Comment::Hashed) = parsed.last_comment_style {
let b = parsed.data.trim_start_matches([' ', '\n', '\t', '\x09']);
let last_hash_index =
b.rfind('#').expect("Parsing ensured this is present");
let from_last_hash = &b[last_hash_index..];
let first_newline = last_hash_index
+ from_last_hash
.find('\n')
.expect("parsing ensured this is present");
&b[..first_newline + 1]
} else {
parsed.data.trim_matches([' ', '\n', '\t', '\x09'])
}
}
let input = &self.0;
let (before, after) = cbordiagnostic::SOC_details(input.as_ref())
.expect("Comments are always well-formed");
let mut owned = String::new();
let add_space = |owned: &mut String| match policy {
DiscardAllButComments => (),
IndentedRegularSpacing { base_indent, .. } => {
if potential_start_of_line {
owned.push_str("\n");
for _ in 0..base_indent {
owned.push_str(" ");
}
}
}
_ => {
if $may_have_comma && !$is_soc {
owned.push_str(" ");
}
}
};
if $may_have_comma && !$is_soc {
owned.push_str(trim_one(before));
owned.push_str(",");
add_space(&mut owned);
} else {
add_space(&mut owned);
owned.push_str(trim_one(before));
}
if let Some(after) = after {
owned.push_str(trim_one(after));
}
self.0 = Cow::Owned(owned);
if $is_ms && self.0 == "" {
*self = Default::default()
}
}
SingleSpace => {
self.set_delimiters(DiscardAllButComments, false);
self.prefix(Cow::Borrowed(" "));
}
}
}
pub(crate) fn prefix<'longer: 'a>(&mut self, prefix: Cow<'longer, str>) {
if prefix == "" {
return;
}
if self.0 == "" {
self.0 = prefix
} else {
self.0 = [prefix.as_ref(), self.0.as_ref()].concat().into();
}
}
pub(crate) fn cloned<'any>(&self) -> $t<'any> {
$t(Cow::Owned(self.0.clone().into()))
}
}
impl Spaceish for $t<'_> {
fn prepend_comment(&mut self, comment: &str) {
self.0 = if comment.contains('/') {
format!("# {}\n{}", comment.replace('\n', "\n# "), self.0)
} else {
format!("/ {} /{}", comment, self.0)
}
.into();
}
}
};
}
spaceish!(S, false, false, false);
spaceish!(MS, false, true, false);
spaceish!(MSC, true, false, false);
spaceish!(SOC, true, false, true);