use super::{Error, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Tab {
Hard, Soft, }
impl Tab {
fn as_char(&self) -> char {
match self {
Tab::Hard => '\t',
Tab::Soft => ' ',
}
}
fn from_str(s: &str) -> Option<Self> {
if s.contains('\t') && s.contains(' ') {
None } else if s.contains('\t') {
Some(Tab::Hard)
} else if s.contains(' ') {
Some(Tab::Soft)
} else {
None }
}
}
impl std::fmt::Display for Tab {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Tab::Hard => write!(f, "{:?}", self.as_char()),
Tab::Soft => write!(f, "{:?}", self.as_char()),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Style {
pub count: usize,
pub tab: Tab,
}
impl Style {
pub fn new(count: usize, tab: Tab) -> Self {
Self { count, tab }
}
}
impl std::fmt::Display for Style {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let indent_str = self.tab.as_char().to_string().repeat(self.count);
write!(f, "{:?}", indent_str)
}
}
#[derive(Debug, Default)]
pub struct Tracker {
style: Option<Style>,
}
impl Tracker {
pub fn validate(&mut self, indent_str: &str) -> Result<usize> {
if indent_str.is_empty() {
return Ok(0);
}
let tab =
Tab::from_str(indent_str).ok_or_else(|| Error::MixedIndent(indent_str.to_string()))?;
match self.style {
None => {
let count = indent_str.len();
self.style = Some(Style::new(count, tab));
Ok(1) }
Some(style) => {
if tab != style.tab {
return Err(Error::InconsistentIndentTab(style.tab, tab));
}
let count = indent_str.len();
if !count.is_multiple_of(style.count) {
return Err(Error::InvalidIndentCount(style.count, count));
}
Ok(count / style.count)
}
}
}
}