use std::sync::{Arc, Mutex};
use async_trait::async_trait;
use lspkit::Generation;
use lspkit_vfs::DocumentUri;
use crate::uri::path_to_uri;
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Severity {
Error,
Warning,
Information,
Hint,
}
#[non_exhaustive]
#[derive(Debug, Clone)]
pub struct Diagnostic {
pub severity: Severity,
pub message: String,
pub range: (u32, u32, u32, u32),
pub code: Option<String>,
pub source: Option<String>,
}
impl Diagnostic {
#[must_use]
pub fn new(
severity: Severity,
message: impl Into<String>,
range: (u32, u32, u32, u32),
) -> Self {
Self {
severity,
message: message.into(),
range,
code: None,
source: None,
}
}
#[must_use]
pub fn with_code(mut self, code: impl Into<String>) -> Self {
self.code = Some(code.into());
self
}
#[must_use]
pub fn with_source(mut self, source: impl Into<String>) -> Self {
self.source = Some(source.into());
self
}
}
#[non_exhaustive]
#[derive(Debug, Clone)]
pub struct DiagnosticsBatch {
pub uri: DocumentUri,
pub generation: Generation,
pub diagnostics: Vec<Diagnostic>,
}
impl DiagnosticsBatch {
#[must_use]
pub fn new(uri: DocumentUri, generation: Generation, diagnostics: Vec<Diagnostic>) -> Self {
Self {
uri,
generation,
diagnostics,
}
}
}
#[async_trait]
pub trait DiagnosticsSink: Send + Sync + 'static {
async fn publish(&self, batch: DiagnosticsBatch);
}
#[derive(Clone, Default)]
pub struct DiagnosticsBus {
sinks: Arc<Mutex<Vec<Arc<dyn DiagnosticsSink>>>>,
}
impl DiagnosticsBus {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn attach(&self, sink: Arc<dyn DiagnosticsSink>) -> usize {
if let Ok(mut guard) = self.sinks.lock() {
guard.push(sink);
guard.len()
} else {
0
}
}
pub async fn publish(&self, batch: DiagnosticsBatch) {
let snapshot: Vec<Arc<dyn DiagnosticsSink>> = self
.sinks
.lock()
.map(|guard| guard.clone())
.unwrap_or_default();
for sink in snapshot {
let sink_batch = batch.clone();
sink.publish(sink_batch).await;
}
}
}
impl std::fmt::Debug for DiagnosticsBus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let count = self.sinks.lock().map_or(0, |g| g.len());
f.debug_struct("DiagnosticsBus")
.field("sinks", &count)
.finish()
}
}
pub fn document_uri_from_path(path: &std::path::Path) -> Result<DocumentUri, crate::uri::UriError> {
Ok(DocumentUri::new(path_to_uri(path)?))
}