rspack_binding_api 0.100.7

Rspack shared binding API
Documentation
use napi::{Either, bindgen_prelude::WeakReference};
use rspack_core::Compilation;
use rspack_error::Severity;

use crate::{compilation::JsCompilation, error::RspackError};

#[napi]
pub struct Diagnostics {
  severity: Severity,
  compiler_reference: WeakReference<JsCompilation>,
}

impl Diagnostics {
  pub fn new(severity: Severity, compiler_reference: WeakReference<JsCompilation>) -> Self {
    Self {
      severity,
      compiler_reference,
    }
  }

  fn as_ref(&self) -> napi::Result<&Compilation> {
    match self.compiler_reference.get() {
      Some(wrapped_value) => Ok(wrapped_value.as_ref()?),
      None => Err(napi::Error::from_reason(
        "Unable to access compilation.errors now. The Compilation has been garbage collected by JavaScript.",
      )),
    }
  }

  fn as_mut(&mut self) -> napi::Result<&mut Compilation> {
    match self.compiler_reference.get_mut() {
      Some(wrapped_value) => Ok(wrapped_value.as_mut()?),
      None => Err(napi::Error::from_reason(
        "Unable to access compilation.errors now. The Compilation has been garbage collected by JavaScript.",
      )),
    }
  }
}

#[napi]
impl Diagnostics {
  #[napi(getter)]
  pub fn length(&self) -> napi::Result<u32> {
    let compilation = self.as_ref()?;

    let diagnostics = compilation.diagnostics();
    let len = diagnostics
      .iter()
      .filter(|diagnostic| diagnostic.severity == self.severity)
      .count();
    Ok(len as u32)
  }

  #[napi]
  pub fn values(&self) -> napi::Result<Vec<RspackError>> {
    let compilation = self.as_ref()?;

    let diagnostics = compilation.diagnostics();
    diagnostics
      .iter()
      .filter(|diagnostic| diagnostic.severity == self.severity)
      .map(|diagnostic| RspackError::try_from_diagnostic(compilation, diagnostic))
      .collect::<napi::Result<Vec<RspackError>>>()
  }

  #[napi]
  pub fn get(&self, index: f64) -> napi::Result<Either<RspackError, ()>> {
    if index < 0f64 || index.is_infinite() || index.abs() != index {
      return Ok(Either::B(()));
    }

    let compilation = self.as_ref()?;
    let diagnostics = compilation.diagnostics();
    let diagnostic = diagnostics
      .iter()
      .filter(|diagnostic| diagnostic.severity == self.severity)
      .nth(index as usize);
    Ok(match diagnostic {
      Some(diagnostic) => {
        let js_rspack_error = RspackError::try_from_diagnostic(compilation, diagnostic)?;
        Either::A(js_rspack_error)
      }
      None => Either::B(()),
    })
  }

  #[napi]
  pub fn set(&mut self, index: f64, error: RspackError) -> napi::Result<()> {
    if index < 0f64 || index.is_infinite() || index.abs() != index {
      return Ok(());
    }

    let severity = self.severity;
    let compilation = self.as_mut()?;
    let diagnostics = compilation.diagnostics_mut();
    let len = diagnostics
      .iter()
      .filter(|diagnostic| diagnostic.severity == severity)
      .count();

    let index = index as usize;
    if index > len {
      return Ok(());
    }

    if index == len {
      diagnostics.push(error.into_diagnostic(severity));
      return Ok(());
    }

    let mut i = 0;
    for diagnostic in diagnostics.iter_mut() {
      if diagnostic.severity == severity {
        if i == index {
          *diagnostic = error.into_diagnostic(severity);
          break;
        }
        i += 1;
      }
    }

    Ok(())
  }

  #[napi]
  pub fn splice_with_array(
    &mut self,
    index: f64,
    delete_count: Option<f64>,
    new_items: Option<Vec<RspackError>>,
  ) -> napi::Result<Vec<RspackError>> {
    let severity = self.severity;
    let compilation = self.as_mut()?;

    let diagnostics = compilation.diagnostics_mut();

    let len = diagnostics
      .iter()
      .filter(|diagnostic| diagnostic.severity == severity)
      .count();
    let len_f64 = len as f64;

    let index = if index < 0f64 {
      (len_f64 + index).max(0f64)
    } else {
      index.min(len_f64)
    };

    let mut delete_count = match delete_count {
      Some(dc) => dc.min(len_f64 - index),
      None => len_f64 - index,
    } as usize;
    let index = index as usize;

    let mut to_insert = match new_items {
      Some(items) => items
        .into_iter()
        .map(|error| error.into_diagnostic(severity))
        .collect::<Vec<_>>(),
      None => vec![],
    };

    let mut removed = Vec::with_capacity(delete_count);
    let mut new_diagnostics = Vec::with_capacity(len - delete_count + to_insert.len());
    let mut i = 0;
    for diagnostic in diagnostics.drain(..) {
      if diagnostic.severity != severity {
        new_diagnostics.push(diagnostic);
        continue;
      }

      if i >= index && delete_count > 0 {
        delete_count -= 1;
        removed.push(diagnostic);
      } else {
        new_diagnostics.push(diagnostic);
      }

      if i == index && delete_count == 0 && !to_insert.is_empty() {
        new_diagnostics.append(&mut to_insert);
      }

      i += 1;
    }

    for diagnostic in to_insert {
      new_diagnostics.push(diagnostic);
    }

    *diagnostics = new_diagnostics;

    removed
      .into_iter()
      .map(|d| RspackError::try_from_diagnostic(compilation, &d))
      .collect::<napi::Result<Vec<_>>>()
  }
}