use std::borrow::Cow;
use std::path::PathBuf;
use serde::Deserialize;
use serde::Serialize;
use mago_database::DatabaseReader;
use mago_database::ReadDatabase;
use mago_database::error::DatabaseError;
use mago_database::file::FileId;
use mago_database::file::FileType;
use mago_span::Span;
use mago_text_edit::TextEdit;
use crate::Annotation;
use crate::AnnotationKind;
use crate::Issue;
use crate::IssueCollection;
use crate::Level;
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
pub struct ExpandedFileId {
pub name: Cow<'static, str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub path: Option<PathBuf>,
pub size: u32,
pub file_type: FileType,
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
pub struct ExpandedPosition {
pub offset: u32,
pub line: u32,
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
pub struct ExpandedSpan {
pub file_id: ExpandedFileId,
pub start: ExpandedPosition,
pub end: ExpandedPosition,
}
#[derive(Debug, PartialEq, Eq, Ord, Clone, Hash, PartialOrd, Deserialize, Serialize)]
pub struct ExpandedAnnotation {
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
pub kind: AnnotationKind,
pub span: ExpandedSpan,
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct ExpandedIssue {
pub level: Level,
#[serde(skip_serializing_if = "Option::is_none")]
pub code: Option<String>,
pub message: String,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub notes: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub help: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub link: Option<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub annotations: Vec<ExpandedAnnotation>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub edits: Vec<(ExpandedFileId, Vec<TextEdit>)>,
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct ExpandedIssueCollection {
issues: Vec<ExpandedIssue>,
}
impl ExpandedIssueCollection {
pub fn from_iter(issues: impl IntoIterator<Item = ExpandedIssue>) -> Self {
Self { issues: issues.into_iter().collect() }
}
}
pub trait Expandable<T> {
fn expand(&self, database: &ReadDatabase) -> Result<T, DatabaseError>;
}
impl Expandable<ExpandedFileId> for FileId {
fn expand(&self, database: &ReadDatabase) -> Result<ExpandedFileId, DatabaseError> {
let file = database.get(self)?;
Ok(ExpandedFileId {
name: file.name.clone(),
path: file.path.clone(),
size: file.size,
file_type: file.file_type,
})
}
}
impl Expandable<ExpandedSpan> for Span {
fn expand(&self, database: &ReadDatabase) -> Result<ExpandedSpan, DatabaseError> {
let file = database.get(&self.file_id)?;
Ok(ExpandedSpan {
file_id: self.file_id.expand(database)?,
start: ExpandedPosition { offset: self.start.offset, line: file.line_number(self.start.offset) },
end: ExpandedPosition { offset: self.end.offset, line: file.line_number(self.end.offset) },
})
}
}
impl Expandable<ExpandedAnnotation> for Annotation {
fn expand(&self, database: &ReadDatabase) -> Result<ExpandedAnnotation, DatabaseError> {
Ok(ExpandedAnnotation { message: self.message.clone(), kind: self.kind, span: self.span.expand(database)? })
}
}
impl Expandable<ExpandedIssue> for Issue {
fn expand(&self, database: &ReadDatabase) -> Result<ExpandedIssue, DatabaseError> {
let mut annotations = Vec::new();
for annotation in &self.annotations {
annotations.push(annotation.expand(database)?);
}
let mut edits = Vec::new();
for (file_id, edit_list) in &self.edits {
edits.push((file_id.expand(database)?, edit_list.clone()));
}
Ok(ExpandedIssue {
level: self.level,
code: self.code.clone(),
message: self.message.clone(),
notes: self.notes.clone(),
help: self.help.clone(),
link: self.link.clone(),
annotations,
edits,
})
}
}
impl Expandable<ExpandedIssueCollection> for IssueCollection {
fn expand(&self, database: &ReadDatabase) -> Result<ExpandedIssueCollection, DatabaseError> {
let mut expanded_issues = Vec::new();
for issue in &self.issues {
expanded_issues.push(issue.expand(database)?);
}
Ok(ExpandedIssueCollection { issues: expanded_issues })
}
}