use std::error::Error;
use std::fmt;
use std::hash::Hasher;
use std::num::{NonZeroU8, NonZeroU16, ParseIntError};
use std::str::FromStr;
use serde::{Deserialize, Serialize};
use unicode_width::UnicodeWidthChar;
use ruff_cache::{CacheKey, CacheKeyHasher};
use ruff_macros::CacheKey;
use ruff_python_trivia::tab_offset;
#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Serialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct LineLength(NonZeroU16);
impl LineLength {
const MAX: u16 = u16::MAX;
pub fn value(&self) -> u16 {
self.0.get()
}
}
impl Default for LineLength {
fn default() -> Self {
Self(NonZeroU16::new(88).unwrap())
}
}
impl fmt::Display for LineLength {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl<'de> serde::Deserialize<'de> for LineLength {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = i64::deserialize(deserializer)?;
u16::try_from(value)
.ok()
.and_then(|u16_value| Self::try_from(u16_value).ok())
.ok_or_else(|| {
serde::de::Error::custom(format!(
"line-length must be between 1 and {} (got {value})",
Self::MAX,
))
})
}
}
impl CacheKey for LineLength {
fn cache_key(&self, state: &mut CacheKeyHasher) {
state.write_u16(self.0.get());
}
}
pub enum ParseLineWidthError {
ParseError(ParseIntError),
TryFromIntError(LineLengthFromIntError),
}
impl std::fmt::Debug for ParseLineWidthError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self, f)
}
}
impl std::fmt::Display for ParseLineWidthError {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ParseLineWidthError::ParseError(err) => std::fmt::Display::fmt(err, fmt),
ParseLineWidthError::TryFromIntError(err) => std::fmt::Display::fmt(err, fmt),
}
}
}
impl Error for ParseLineWidthError {}
impl FromStr for LineLength {
type Err = ParseLineWidthError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let value = u16::from_str(s).map_err(ParseLineWidthError::ParseError)?;
let value = Self::try_from(value).map_err(ParseLineWidthError::TryFromIntError)?;
Ok(value)
}
}
#[derive(Clone, Copy, Debug)]
pub struct LineLengthFromIntError(pub u16);
impl TryFrom<u16> for LineLength {
type Error = LineLengthFromIntError;
fn try_from(value: u16) -> Result<Self, Self::Error> {
match NonZeroU16::try_from(value) {
Ok(value) => Ok(LineLength(value)),
Err(_) => Err(LineLengthFromIntError(value)),
}
}
}
impl std::fmt::Display for LineLengthFromIntError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(
f,
"The line width must be a value between 1 and {}.",
LineLength::MAX
)
}
}
impl From<LineLength> for u16 {
fn from(value: LineLength) -> Self {
value.0.get()
}
}
impl From<LineLength> for NonZeroU16 {
fn from(value: LineLength) -> Self {
value.0
}
}
#[derive(Clone, Copy, Debug)]
pub struct LineWidthBuilder {
width: usize,
column: usize,
tab_size: IndentWidth,
}
impl Default for LineWidthBuilder {
fn default() -> Self {
Self::new(IndentWidth::default())
}
}
impl PartialEq for LineWidthBuilder {
fn eq(&self, other: &Self) -> bool {
self.width == other.width
}
}
impl Eq for LineWidthBuilder {}
impl PartialOrd for LineWidthBuilder {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for LineWidthBuilder {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.width.cmp(&other.width)
}
}
impl LineWidthBuilder {
pub(crate) fn get(&self) -> usize {
self.width
}
pub(crate) fn new(tab_size: IndentWidth) -> Self {
LineWidthBuilder {
width: 0,
column: 0,
tab_size,
}
}
fn update(mut self, chars: impl Iterator<Item = char>) -> Self {
let tab_size: usize = self.tab_size.as_usize();
for c in chars {
match c {
'\t' => {
let tab_offset = tab_offset(self.column, tab_size);
self.width += tab_offset;
self.column += tab_offset;
}
'\n' | '\r' => {
self.width = 0;
self.column = 0;
}
_ => {
self.width += c.width().unwrap_or(0);
self.column += 1;
}
}
}
self
}
#[must_use]
pub(crate) fn add_str(self, text: &str) -> Self {
self.update(text.chars())
}
#[must_use]
pub(crate) fn add_char(self, c: char) -> Self {
self.update(std::iter::once(c))
}
#[must_use]
pub(crate) fn add_width(mut self, width: usize) -> Self {
self.width += width;
self.column += width;
self
}
}
impl PartialEq<LineLength> for LineWidthBuilder {
fn eq(&self, other: &LineLength) -> bool {
self.width == (other.value() as usize)
}
}
impl PartialOrd<LineLength> for LineWidthBuilder {
fn partial_cmp(&self, other: &LineLength) -> Option<std::cmp::Ordering> {
self.width.partial_cmp(&(other.value() as usize))
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, CacheKey)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct IndentWidth(NonZeroU8);
impl IndentWidth {
pub(crate) fn as_usize(self) -> usize {
self.0.get() as usize
}
}
impl Default for IndentWidth {
fn default() -> Self {
Self(NonZeroU8::new(4).unwrap())
}
}
impl fmt::Display for IndentWidth {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl From<NonZeroU8> for IndentWidth {
fn from(tab_size: NonZeroU8) -> Self {
Self(tab_size)
}
}
impl From<IndentWidth> for NonZeroU8 {
fn from(value: IndentWidth) -> Self {
value.0
}
}