use super::diagnostic::{
CompilerDiagnostic, CompilerSuggestion, DiagnosticSeverity, SourceSpan, TypeInfo,
};
use super::error::{CITLError, CITLResult};
use super::ErrorCode;
use std::collections::HashMap;
use std::path::PathBuf;
use std::process::{Command, Stdio};
use std::time::{Duration, Instant};
pub trait CompilerInterface: Send + Sync {
fn compile(&self, source: &str, options: &CompileOptions) -> CITLResult<CompilationResult>;
fn parse_diagnostic(&self, raw: &str) -> Option<CompilerDiagnostic>;
fn version(&self) -> CITLResult<CompilerVersion>;
fn name(&self) -> &'static str;
fn is_available(&self) -> bool;
}
#[derive(Debug, Clone)]
pub struct CompileOptions {
pub opt_level: OptLevel,
pub extra_flags: Vec<String>,
pub env: HashMap<String, String>,
pub working_dir: Option<PathBuf>,
}
impl Default for CompileOptions {
fn default() -> Self {
Self {
opt_level: OptLevel::Debug,
extra_flags: Vec::new(),
env: HashMap::new(),
working_dir: None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OptLevel {
Debug,
Release,
}
#[derive(Debug, Clone)]
pub enum CompilationResult {
Success {
artifact: Option<CompiledArtifact>,
warnings: Vec<CompilerDiagnostic>,
metrics: CompilationMetrics,
},
Failure {
errors: Vec<CompilerDiagnostic>,
warnings: Vec<CompilerDiagnostic>,
raw_output: String,
},
}
impl CompilationResult {
#[must_use]
pub fn is_success(&self) -> bool {
matches!(self, CompilationResult::Success { .. })
}
#[must_use]
pub fn error_count(&self) -> usize {
match self {
CompilationResult::Success { .. } => 0,
CompilationResult::Failure { errors, .. } => errors.len(),
}
}
#[must_use]
pub fn warning_count(&self) -> usize {
self.warnings().len()
}
#[must_use]
pub fn errors(&self) -> &[CompilerDiagnostic] {
match self {
CompilationResult::Success { .. } => &[],
CompilationResult::Failure { errors, .. } => errors,
}
}
#[must_use]
pub fn warnings(&self) -> &[CompilerDiagnostic] {
match self {
CompilationResult::Success { warnings, .. }
| CompilationResult::Failure { warnings, .. } => warnings,
}
}
}
#[derive(Debug, Clone)]
pub struct CompiledArtifact {
pub artifact_type: ArtifactType,
pub path: Option<PathBuf>,
pub size: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ArtifactType {
StaticLib,
DynamicLib,
Binary,
Object,
Wasm,
}
#[derive(Debug, Clone)]
pub struct CompilationMetrics {
pub duration: Duration,
pub memory_bytes: Option<usize>,
pub units: usize,
}
impl Default for CompilationMetrics {
fn default() -> Self {
Self {
duration: Duration::ZERO,
memory_bytes: None,
units: 1,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CompilerVersion {
pub major: u32,
pub minor: u32,
pub patch: u32,
pub full: String,
pub commit: Option<String>,
}
impl CompilerVersion {
#[must_use]
pub fn parse(version_str: &str) -> Option<Self> {
let parts: Vec<&str> = version_str.trim().split('.').collect();
if parts.len() >= 3 {
let major = parts[0].parse().ok()?;
let minor = parts[1].parse().ok()?;
let patch_str = parts[2].split('-').next()?;
let patch = patch_str.parse().ok()?;
Some(Self {
major,
minor,
patch,
full: version_str.to_string(),
commit: None,
})
} else {
None
}
}
}
impl std::fmt::Display for CompilerVersion {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
}
}
#[derive(Debug, Clone, Default)]
pub enum CompilationMode {
#[default]
Standalone,
Cargo {
manifest_path: PathBuf,
},
CargoCheck {
manifest_path: PathBuf,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum RustEdition {
E2015,
E2018,
#[default]
E2021,
E2024,
}
impl RustEdition {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
RustEdition::E2015 => "2015",
RustEdition::E2018 => "2018",
RustEdition::E2021 => "2021",
RustEdition::E2024 => "2024",
}
}
}
#[derive(Debug, Clone)]
pub struct RustCompiler {
rustc_path: PathBuf,
cargo_path: PathBuf,
edition: RustEdition,
target: Option<String>,
timeout: Duration,
mode: CompilationMode,
extra_flags: Vec<String>,
}
include!("cargo_project.rs");
include!("json.rs");