1use std::{
2 convert::TryFrom,
3 fmt,
4 path::{Path, PathBuf},
5 str::FromStr,
6};
7
8#[derive(Debug, Clone)]
10pub struct ManSource {
11 section: u8,
12 target_file_name: String,
13 path: PathBuf,
14 format: ManSourceFormat,
15}
16
17impl ManSource {
18 pub fn section(&self) -> u8 {
20 self.section
21 }
22
23 pub fn target_file_name(&self) -> &str {
28 &self.target_file_name
29 }
30
31 pub fn path(&self) -> &Path {
33 &self.path
34 }
35
36 pub fn format(&self) -> ManSourceFormat {
38 self.format
39 }
40}
41
42#[derive(Debug, Copy, Clone, Eq, PartialEq)]
44pub enum ManSourceFormat {
45 Markdown,
48 Verbatim,
50}
51
52impl FromStr for ManSource {
53 type Err = ParseManSourceError;
54
55 fn from_str(s: &str) -> Result<Self, Self::Err> {
56 let path = Path::new(s);
57 let (stem, ext) = match (path.file_stem(), path.extension()) {
58 (Some(stem), Some(ext)) => (stem.to_str().unwrap(), ext.to_str().unwrap()),
60 _ => return Err(ParseManSourceError::missing_extension()),
61 };
62 let (format, target_name, ext) = match ext {
63 "md" => {
64 if let Some(dot) = stem.rfind('.') {
65 (ManSourceFormat::Markdown, stem, &stem[dot + 1..])
66 } else {
67 return Err(ParseManSourceError::missing_section());
68 }
69 }
70 _ => {
71 let target_name = path.file_name().unwrap().to_str().unwrap();
75 (ManSourceFormat::Verbatim, target_name, ext)
76 }
77 };
78 let section = if let Some(pos) = ext.as_bytes().iter().position(|c| !c.is_ascii_digit()) {
79 &ext[..pos]
80 } else {
81 ext
82 };
83 let section = section
84 .parse()
85 .map_err(|_| ParseManSourceError::invalid_extension())?;
86 Ok(ManSource {
87 target_file_name: target_name.into(),
88 section,
89 path: path.to_path_buf(),
90 format,
91 })
92 }
93}
94
95impl TryFrom<&str> for ManSource {
96 type Error = ParseManSourceError;
97
98 fn try_from(s: &str) -> Result<Self, Self::Error> {
99 s.parse()
100 }
101}
102
103#[derive(Debug, Clone)]
105pub struct ParseManSourceError(ParseManSourceErrorInner);
106
107impl ParseManSourceError {
108 fn missing_extension() -> Self {
109 ParseManSourceError(ParseManSourceErrorInner::MissingExtension)
110 }
111 fn missing_section() -> Self {
112 ParseManSourceError(ParseManSourceErrorInner::MissingSection)
113 }
114 fn invalid_extension() -> Self {
115 ParseManSourceError(ParseManSourceErrorInner::InvalidExtension)
116 }
117}
118
119#[derive(Debug, Clone)]
120enum ParseManSourceErrorInner {
121 MissingExtension,
122 MissingSection,
123 InvalidExtension,
124}
125
126impl fmt::Display for ParseManSourceError {
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 use ParseManSourceErrorInner::*;
129 match self.0 {
130 MissingExtension => write!(f, "missing extension"),
131 MissingSection => write!(f, "missing section"),
132 InvalidExtension => write!(f, "invalid extension"),
133 }
134 }
135}
136
137impl std::error::Error for ParseManSourceError {}