#![cfg(feature = "pulldown-cmark")]
#[cfg(feature = "thiserror")]
use std::path::Path;
use std::sync::Arc;
use pulldown_cmark::Event;
#[cfg(feature = "thiserror")]
use thiserror::Error;
use crate::{CMarkData, CMarkDataIter, File, Manifest, Package};
#[cfg(feature = "thiserror")]
use crate::{DisallowUrlsWithPrefixError, FileFromPathError};
#[derive(Clone, Debug, Default, PartialEq)]
pub struct CMarkReadme<P, M> {
data: CMarkData,
package_path: P,
manifest: M,
}
#[cfg(feature = "thiserror")]
impl<'a> CMarkReadme<&'a Path, &'a Manifest> {
pub fn from_package(package: &'a Package) -> Result<Self, CMarkReadmeFromPackageError> {
let path = package
.relative_readme_path()
.ok_or(CMarkReadmeFromPackageError::NotFound)?;
let file = Arc::new(File::from_path(path.to_path_buf(), Some(package.path()))?);
let package_path = package.path();
let manifest = package.manifest();
Ok(Self::from_file_and_package_path_and_manifest(
file,
package_path,
manifest,
))
}
}
impl<'a> CMarkReadme<(), ()> {
pub fn from_file(file: Arc<File>) -> Self {
Self::from_file_and_package_path_and_manifest(file, (), ())
}
}
impl<'a, P, M> CMarkReadme<P, M> {
pub fn with_package_path(self, package_path: &'a Package) -> CMarkReadme<&'a Package, M> {
CMarkReadme {
data: self.data,
package_path,
manifest: self.manifest,
}
}
pub fn with_manifest(self, manifest: &'a Manifest) -> CMarkReadme<P, &'a Manifest> {
CMarkReadme {
data: self.data,
package_path: self.package_path,
manifest,
}
}
pub fn from_file_and_package_path_and_manifest(
file: Arc<File>,
package_path: P,
manifest: M,
) -> Self {
let data = CMarkData::from_file(file);
Self::from_data_and_package_path_and_manifest(data, package_path, manifest)
}
pub fn from_data_and_package_path_and_manifest(
data: CMarkData,
package_path: P,
manifest: M,
) -> Self {
Self {
data,
package_path,
manifest,
}
}
pub fn data(&self) -> &CMarkData {
&self.data
}
pub fn into_data(self) -> CMarkData {
self.data
}
pub fn package_path(&self) -> &P {
&self.package_path
}
pub fn manifest(&self) -> &M {
&self.manifest
}
pub fn iter(&self) -> CMarkDataIter<'_> {
self.data.iter()
}
pub fn iter_events(&self) -> impl Iterator<Item = &Event<'_>> {
self.data.iter().filter_map(|item| item.event())
}
fn map<F>(mut self, func: F) -> CMarkReadme<P, M>
where
F: FnOnce(CMarkData) -> CMarkData,
{
self.data = func(self.data);
self
}
#[cfg(feature = "thiserror")]
fn map_result<F, E>(mut self, func: F) -> Result<CMarkReadme<P, M>, E>
where
F: FnOnce(CMarkData) -> Result<CMarkData, E>,
{
self.data = func(self.data)?;
Ok(self)
}
pub fn concat_texts(self) -> CMarkReadme<P, M> {
self.map(|data| data.concat_texts())
}
pub fn remove_images_only_paragraph<F>(self, predicate: F) -> CMarkReadme<P, M>
where
F: FnMut(&[&str]) -> bool,
{
self.map(|data| data.remove_images_only_paragraph(predicate))
}
#[cfg(feature = "glob")]
pub fn remove_badges_paragraph(self) -> CMarkReadme<P, M> {
self.map(|data| data.remove_badges_paragraph())
}
pub fn remove_section(self, heading: &str, level: u32) -> Self {
self.map(|data| data.remove_section(heading, level))
}
pub fn remove_codeblock_tag(self, tag: &str) -> CMarkReadme<P, M> {
self.map(|data| data.remove_codeblock_tag(tag))
}
pub fn remove_codeblock_tags(self, tags: &[&str]) -> CMarkReadme<P, M> {
self.map(|data| data.remove_codeblock_tags(tags))
}
pub fn remove_documentation_section(self) -> Self {
self.map(|data| data.remove_documentation_section())
}
#[cfg(feature = "thiserror")]
pub fn disallow_absolute_blob_links(
self,
repository_url: &str,
) -> Result<CMarkReadme<P, M>, DisallowUrlsWithPrefixError> {
self.map_result(|data| data.disallow_absolute_blob_links(repository_url))
}
pub fn use_absolute_blob_urls(self, repository_url: &str) -> CMarkReadme<P, M> {
self.map(|data| data.use_absolute_blob_urls(repository_url))
}
}
#[cfg(feature = "thiserror")]
impl<'a, P> CMarkReadme<P, &'a Manifest> {
pub fn disallow_absolute_repository_blob_links(
self,
) -> Result<CMarkReadme<P, &'a Manifest>, DisallowAbsoluteRepositoryBlobLinksError> {
let repository = self
.manifest
.package
.repository
.clone()
.ok_or(DisallowAbsoluteRepositoryBlobLinksError::DocsUrlNotFound)?;
Ok(self.disallow_absolute_blob_links(&repository)?)
}
pub fn use_absolute_repository_blob_urls(
self,
) -> Result<CMarkReadme<P, &'a Manifest>, UseAbsoluteRepositoryBlobUrlsError> {
let repository = self
.manifest
.package
.repository
.clone()
.ok_or(UseAbsoluteRepositoryBlobUrlsError::DocsUrlNotFound)?;
Ok(self.use_absolute_blob_urls(&repository))
}
}
#[cfg(feature = "thiserror")]
#[derive(Debug, Error)]
pub enum CMarkReadmeFromPackageError {
#[error(transparent)]
FileError(#[from] FileFromPathError),
#[error("CMarkReadme not found.")]
NotFound,
}
#[cfg(feature = "thiserror")]
#[derive(Clone, Debug, Error)]
pub enum DisallowAbsoluteRepositoryBlobLinksError {
#[error(transparent)]
DisallowUrlsWithPrefixError(#[from] DisallowUrlsWithPrefixError),
#[error("Manifest does not contain package.documentation field")]
DocsUrlNotFound,
}
#[cfg(feature = "thiserror")]
#[derive(Clone, Copy, Debug, Error)]
pub enum UseAbsoluteRepositoryBlobUrlsError {
#[error("Manifest does not contain package.documentation field")]
DocsUrlNotFound,
}