use std::{
convert::TryFrom,
fmt,
path::{Path, PathBuf},
str::FromStr,
};
#[derive(Debug, Clone)]
pub struct ManSource {
section: u8,
target_file_name: String,
path: PathBuf,
format: ManSourceFormat,
}
impl ManSource {
pub fn section(&self) -> u8 {
self.section
}
pub fn target_file_name(&self) -> &str {
&self.target_file_name
}
pub fn path(&self) -> &Path {
&self.path
}
pub fn format(&self) -> ManSourceFormat {
self.format
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ManSourceFormat {
Markdown,
Verbatim,
}
impl FromStr for ManSource {
type Err = ParseManSourceError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let path = Path::new(s);
let (stem, ext) = match (path.file_stem(), path.extension()) {
(Some(stem), Some(ext)) => (stem.to_str().unwrap(), ext.to_str().unwrap()),
_ => return Err(ParseManSourceError::missing_extension()),
};
let (format, target_name, ext) = match ext {
"md" => {
if let Some(dot) = stem.rfind('.') {
(ManSourceFormat::Markdown, stem, &stem[dot + 1..])
} else {
return Err(ParseManSourceError::missing_section());
}
}
_ => {
let target_name = path.file_name().unwrap().to_str().unwrap();
(ManSourceFormat::Verbatim, target_name, ext)
}
};
let section = if let Some(pos) = ext.as_bytes().iter().position(|c| !c.is_ascii_digit()) {
&ext[..pos]
} else {
ext
};
let section = section
.parse()
.map_err(|_| ParseManSourceError::invalid_extension())?;
Ok(ManSource {
target_file_name: target_name.into(),
section,
path: path.to_path_buf(),
format,
})
}
}
impl TryFrom<&str> for ManSource {
type Error = ParseManSourceError;
fn try_from(s: &str) -> Result<Self, Self::Error> {
s.parse()
}
}
#[derive(Debug, Clone)]
pub struct ParseManSourceError(ParseManSourceErrorInner);
impl ParseManSourceError {
fn missing_extension() -> Self {
ParseManSourceError(ParseManSourceErrorInner::MissingExtension)
}
fn missing_section() -> Self {
ParseManSourceError(ParseManSourceErrorInner::MissingSection)
}
fn invalid_extension() -> Self {
ParseManSourceError(ParseManSourceErrorInner::InvalidExtension)
}
}
#[derive(Debug, Clone)]
enum ParseManSourceErrorInner {
MissingExtension,
MissingSection,
InvalidExtension,
}
impl fmt::Display for ParseManSourceError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use ParseManSourceErrorInner::*;
match self.0 {
MissingExtension => write!(f, "missing extension"),
MissingSection => write!(f, "missing section"),
InvalidExtension => write!(f, "invalid extension"),
}
}
}
impl std::error::Error for ParseManSourceError {}