string-width 0.1.0

Accurate Unicode string width calculation for terminal applications, handling emoji, East Asian characters, combining marks, and ANSI escape sequences
Documentation
/// How to treat ambiguous width characters
///
/// Some Unicode characters have ambiguous width properties, meaning their
/// display width depends on the context (terminal, font, locale, etc.).
/// This enum allows you to specify how such characters should be treated.
///
/// # Examples
///
/// ```rust
/// use string_width::{AmbiguousWidthTreatment, StringWidthOptions, string_width_with_options};
///
/// let text = "±×÷";  // Ambiguous width characters
///
/// let narrow_options = StringWidthOptions {
///     count_ansi: false,
///     ambiguous_width: AmbiguousWidthTreatment::Narrow,
/// };
/// assert_eq!(string_width_with_options(text, narrow_options), 3);
///
/// let wide_options = StringWidthOptions {
///     count_ansi: false,
///     ambiguous_width: AmbiguousWidthTreatment::Wide,
/// };
/// assert_eq!(string_width_with_options(text, wide_options), 6);
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AmbiguousWidthTreatment {
    /// Treat ambiguous characters as narrow (width 1)
    Narrow,
    /// Treat ambiguous characters as wide (width 2)
    Wide,
}

impl Default for AmbiguousWidthTreatment {
    /// Returns the default treatment for ambiguous width characters.
    ///
    /// The default is `Narrow`, which treats ambiguous characters as having width 1.
    /// This is the most common behavior in terminal applications.
    fn default() -> Self {
        Self::Narrow
    }
}

/// Configuration options for string width calculation
///
/// This struct contains all the options that control how string width is calculated.
/// It provides fine-grained control over various aspects of width calculation.
///
/// # Examples
///
/// ```rust
/// use string_width::{StringWidthOptions, AmbiguousWidthTreatment, string_width_with_options};
///
/// // Default options
/// let options = StringWidthOptions::default();
/// assert_eq!(string_width_with_options("Hello", options), 5);
///
/// // Custom options
/// let options = StringWidthOptions {
///     count_ansi: true,  // Count ANSI escape sequences
///     ambiguous_width: AmbiguousWidthTreatment::Wide,  // Treat ambiguous as wide
/// };
/// ```
#[derive(Debug, Clone, Default)]
pub struct StringWidthOptions {
    /// Whether to count ANSI escape sequences in width calculation
    ///
    /// When `false` (default), ANSI escape sequences are stripped before
    /// calculating width. When `true`, they are included in the calculation.
    pub count_ansi: bool,

    /// How to treat ambiguous width characters
    ///
    /// Controls whether characters with ambiguous width properties
    /// are treated as narrow (width 1) or wide (width 2).
    pub ambiguous_width: AmbiguousWidthTreatment,
}

impl StringWidthOptions {
    /// Creates a new builder for StringWidthOptions
    ///
    /// The builder pattern provides a fluent API for configuring options.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use string_width::StringWidthOptions;
    ///
    /// let options = StringWidthOptions::builder()
    ///     .count_ansi(true)
    ///     .ambiguous_as_wide()
    ///     .build();
    /// ```
    pub fn builder() -> StringWidthOptionsBuilder {
        StringWidthOptionsBuilder::default()
    }
}

/// Builder for StringWidthOptions following the builder pattern
///
/// Provides a fluent API for configuring string width calculation options.
///
/// # Examples
///
/// ```rust
/// use string_width::{StringWidthOptions, AmbiguousWidthTreatment};
///
/// // Basic usage
/// let options = StringWidthOptions::builder()
///     .count_ansi(true)
///     .ambiguous_as_wide()
///     .build();
///
/// // All options
/// let options = StringWidthOptions::builder()
///     .count_ansi(false)
///     .ambiguous_width(AmbiguousWidthTreatment::Narrow)
///     .build();
/// ```
#[derive(Debug, Clone, Default)]
pub struct StringWidthOptionsBuilder {
    count_ansi: bool,
    ambiguous_width: AmbiguousWidthTreatment,
}

impl StringWidthOptionsBuilder {
    /// Creates a new builder with default values
    ///
    /// This is equivalent to calling `StringWidthOptionsBuilder::default()`.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use string_width::{StringWidthOptions, StringWidthOptionsBuilder};
    ///
    /// let builder = StringWidthOptions::builder();
    /// // or
    /// let builder = StringWidthOptionsBuilder::new();
    /// ```
    pub fn new() -> Self {
        Self::default()
    }

    /// Sets whether to count ANSI escape sequences in width calculation
    ///
    /// When set to `true`, ANSI escape sequences will be included in the width
    /// calculation. When `false` (default), they are stripped and ignored.
    ///
    /// # Arguments
    ///
    /// * `count_ansi` - Whether to count ANSI sequences
    ///
    /// # Examples
    ///
    /// ```rust
    /// use string_width::{StringWidthOptions, string_width_with_options};
    ///
    /// let options = StringWidthOptions::builder()
    ///     .count_ansi(true)
    ///     .build();
    ///
    /// // ANSI sequences are counted
    /// assert_eq!(string_width_with_options("\x1b[31mRed\x1b[0m", options), 12);
    /// ```
    pub fn count_ansi(mut self, count_ansi: bool) -> Self {
        self.count_ansi = count_ansi;
        self
    }

    /// Sets how ambiguous width characters should be treated
    ///
    /// Allows explicit control over ambiguous width character treatment.
    ///
    /// # Arguments
    ///
    /// * `ambiguous_width` - The treatment strategy for ambiguous characters
    ///
    /// # Examples
    ///
    /// ```rust
    /// use string_width::{StringWidthOptions, AmbiguousWidthTreatment, string_width_with_options};
    ///
    /// let options = StringWidthOptions::builder()
    ///     .ambiguous_width(AmbiguousWidthTreatment::Wide)
    ///     .build();
    ///
    /// assert_eq!(string_width_with_options("±", options), 2);
    /// ```
    pub fn ambiguous_width(mut self, ambiguous_width: AmbiguousWidthTreatment) -> Self {
        self.ambiguous_width = ambiguous_width;
        self
    }

    /// Sets ambiguous width characters to be treated as narrow (convenience method)
    ///
    /// This is a convenience method equivalent to calling
    /// `.ambiguous_width(AmbiguousWidthTreatment::Narrow)`.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use string_width::{StringWidthOptions, string_width_with_options};
    ///
    /// let options = StringWidthOptions::builder()
    ///     .ambiguous_as_narrow()
    ///     .build();
    ///
    /// assert_eq!(string_width_with_options("±", options), 1);
    /// ```
    pub fn ambiguous_as_narrow(mut self) -> Self {
        self.ambiguous_width = AmbiguousWidthTreatment::Narrow;
        self
    }

    /// Sets ambiguous width characters to be treated as wide (convenience method)
    ///
    /// This is a convenience method equivalent to calling
    /// `.ambiguous_width(AmbiguousWidthTreatment::Wide)`.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use string_width::{StringWidthOptions, string_width_with_options};
    ///
    /// let options = StringWidthOptions::builder()
    ///     .ambiguous_as_wide()
    ///     .build();
    ///
    /// assert_eq!(string_width_with_options("±", options), 2);
    /// ```
    pub fn ambiguous_as_wide(mut self) -> Self {
        self.ambiguous_width = AmbiguousWidthTreatment::Wide;
        self
    }

    /// Builds the final StringWidthOptions instance
    ///
    /// Consumes the builder and returns the configured `StringWidthOptions`.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use string_width::StringWidthOptions;
    ///
    /// let options = StringWidthOptions::builder()
    ///     .count_ansi(true)
    ///     .ambiguous_as_wide()
    ///     .build();
    /// ```
    pub fn build(self) -> StringWidthOptions {
        StringWidthOptions {
            count_ansi: self.count_ansi,
            ambiguous_width: self.ambiguous_width,
        }
    }
}