use core::fmt::Write;
use core::ops::Range;

/// A type-erased wrapper around `String`.
///
/// This is used in replacer function signatures so that closures can write
/// replacement text without exposing full access to the underlying `String` type.
#[repr(transparent)]
pub struct Writable(String);

impl Writable {
    pub(crate) fn new(dst: &mut String) -> &mut Self {
        unsafe { &mut *(dst as *mut String as *mut Writable) }
    }
}

impl core::fmt::Write for Writable {
    #[inline]
    fn write_str(&mut self, s: &str) -> core::fmt::Result {
        self.0.write_str(s)
    }

    #[inline]
    fn write_char(&mut self, c: char) -> core::fmt::Result {
        self.0.write_char(c)
    }

    #[inline]
    fn write_fmt(&mut self, args: core::fmt::Arguments<'_>) -> core::fmt::Result {
        self.0.write_fmt(args)
    }
}

/// A type that can produce replacement text for a matched pattern.
///
/// Implemented for `&str` (constant replacement) and for closures of the form
/// `FnMut(&str, Range<usize>, &mut Writable) -> fmt::Result` (dynamic replacement).
pub trait Replacer {
    /// Write the replacement for the matched text `from` (at the given byte
    /// `range` in the stripped string) to `dst`.
    fn replace(&mut self, from: &str, range: Range<usize>, dst: &mut Writable);
}

impl Replacer for &str {
    fn replace(&mut self, _from: &str, _range: Range<usize>, dst: &mut Writable) {
        let _ = write!(dst, "{}", self);
    }
}

impl<F> Replacer for F
where
    F: FnMut(&'_ str, Range<usize>, &mut Writable) -> core::fmt::Result,
{
    fn replace(&mut self, from: &str, range: Range<usize>, dst: &mut Writable) {
        let _ = self(from, range, dst);
    }
}
