use std::fmt;
use snafu::{Snafu, ensure};
use crate::{source::Spanned, sync::PackageSyncContext};
use super::marker::ResolvedReplaceSpecifier;
mod badge;
mod rustdoc;
mod title;
pub(super) fn create_all<'a>(
cx: &PackageSyncContext<'_>,
specifiers: Vec<Spanned<ResolvedReplaceSpecifier<'a>>>,
) -> Result<Vec<Contents<'a>>, CreateAllContentsError> {
let mut contents = vec![];
let mut errors = vec![];
for specifier in specifiers {
let res = create_content(cx, specifier);
match res {
Ok(c) => contents.push(c),
Err(err) => errors.push(err),
}
}
ensure!(errors.is_empty(), CreateAllContentsSnafu { errors });
Ok(contents)
}
#[derive(Debug, Snafu, miette::Diagnostic)]
#[snafu(display("failed to create replacement contents"))]
pub(crate) struct CreateAllContentsError {
#[related]
errors: Vec<CreateContentsError>,
}
#[derive(Debug, Snafu, miette::Diagnostic)]
pub(super) enum CreateContentsError {
#[snafu(transparent)]
#[diagnostic(transparent)]
CreateBadge {
#[snafu(source)]
#[diagnostic_source]
source: badge::CreateAllBadgesError,
},
#[snafu(transparent)]
#[diagnostic(transparent)]
CreateRustdoc {
#[snafu(source)]
#[diagnostic_source]
source: rustdoc::CreateRustdocError,
},
}
#[derive(Debug, Clone)]
pub(super) struct Contents<'a> {
specifier: Spanned<ResolvedReplaceSpecifier<'a>>,
text: String,
}
fn create_content<'a>(
cx: &PackageSyncContext<'_>,
specifier: Spanned<ResolvedReplaceSpecifier<'a>>,
) -> Result<Contents<'a>, CreateContentsError> {
let text = match specifier.value {
ResolvedReplaceSpecifier::Title => title::create(cx),
ResolvedReplaceSpecifier::Badge { group: _, badges } => badge::create_all(cx, badges)?,
ResolvedReplaceSpecifier::Rustdoc => rustdoc::create(cx)?,
};
assert!(text.is_empty() || text.ends_with('\n'));
Ok(Contents { specifier, text })
}
impl Contents<'_> {
pub(super) fn specifier(&self) -> &Spanned<ResolvedReplaceSpecifier<'_>> {
&self.specifier
}
pub(super) fn text(&self) -> &str {
&self.text
}
}
struct Escape<'s>(&'s str, &'s [char]);
impl fmt::Display for Escape<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut s = self.0;
while let Some(idx) = s.find(self.1) {
f.write_str(&s[..idx])?;
write!(f, r"\{}", s.as_bytes()[idx] as char)?;
s = &s[idx + 1..];
}
f.write_str(s)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use similar_asserts::assert_eq;
#[test]
fn escape() {
let need_escape = [
'\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!',
];
assert_eq!(Escape(r"foo", &need_escape).to_string(), r"foo");
assert_eq!(Escape(r"`foobar", &need_escape).to_string(), r"\`foobar");
assert_eq!(Escape(r"foo*bar", &need_escape).to_string(), r"foo\*bar");
assert_eq!(Escape(r"foobar_", &need_escape).to_string(), r"foobar\_");
assert_eq!(
Escape(r"`foo*bar_", &need_escape).to_string(),
r"\`foo\*bar\_"
);
assert_eq!(
Escape(r"\foo\bar\", &need_escape).to_string(),
r"\\foo\\bar\\"
);
assert_eq!(Escape(r"*", &need_escape).to_string(), r"\*");
}
}