use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
use thiserror::Error;
use ::config::ConfigError;
#[derive(Debug, Serialize, Deserialize, Clone)] #[serde(default)] pub struct Settings {
pub compiler_repository_url: String,
pub compiler_storage_path: Option<PathBuf>, pub installed_compilers: HashMap<String, CompilerInfo>,
pub file_associations: HashMap<String, String>, pub wine_path: Option<String>,
}
impl Default for Settings {
fn default() -> Self {
Settings {
compiler_repository_url: "https://raw.githubusercontent.com/RileyLeff/campbell-scientific-compilers/refs/heads/main/compilers.toml".to_string(),
compiler_storage_path: None, installed_compilers: HashMap::new(),
file_associations: HashMap::new(),
wine_path: None, }
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct CompilerInfo {
pub id: String, pub description: String, pub version: String, pub install_subdir: PathBuf, pub executable_name: String, pub requires_wine: bool, pub supported_loggers: Option<Vec<String>>, }
fn default_true() -> bool {
true
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Manifest {
pub manifest_version: String,
#[serde(default)] pub compilers: HashMap<String, ManifestCompilerEntry>, }
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ManifestCompilerEntry {
pub description: String,
pub version: String,
pub download_url: String,
pub executable_name: String, #[serde(default = "default_true")] pub requires_wine: bool,
#[serde(default)]
pub supported_loggers: Option<Vec<String>>,
#[serde(default)]
pub sha256: Option<String>, }
#[derive(Debug, Clone)] pub struct CompilationErrorDetail {
pub file_path_in_log: String, pub line: Option<u32>,
pub message: String,
}
#[derive(Error, Debug)]
pub enum Error {
#[error("Configuration error: {0}")]
Config(#[from] ConfigError),
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("Network error: {0}")]
Network(#[from] reqwest::Error),
#[error("Failed to process ZIP archive: {0}")]
Zip(#[from] zip::result::ZipError),
#[error("Compiler '{0}' not found in configuration.")]
CompilerNotFound(String),
#[error("SHA256 checksum mismatch for compiler '{compiler_id}'. Expected: '{expected}', Got: '{actual}'.")]
ChecksumMismatch { compiler_id: String,
expected: String,
actual: String,
},
#[error("No compiler associated with file extension '.{0}'. Please configure an association.")]
NoCompilerForExtension(String),
#[error("Could not find Wine executable. Please install Wine or set the path in configuration.")]
WineNotFound,
#[error("Failed to execute subprocess: {0}")]
Subprocess(std::io::Error),
#[error("Compilation of '{file_path}' failed.")]
CompilationFailed {
file_path: PathBuf, errors: Vec<CompilationErrorDetail>,
raw_log: String, },
#[error("Compiler reported failure for '{file_path}'. Log:\n{raw_log}")]
GenericCompilationFailedWithLog {
file_path: PathBuf,
raw_log: String,
},
#[error("Compiler execution failed. Output Log:\n{log_content}")]
CompilationFailedWithLog { log_content: String },
#[error("Invalid compiler source or manifest: {0}")]
InvalidCompilerSource(String),
#[error("Failed to determine application directories.")]
DirectoryResolutionFailed,
#[error("Compiler ID '{0}' not found in the repository manifest.")]
CompilerIdNotFoundInManifest(String),
#[error("Invalid file extension: '{0}'.")]
InvalidExtension(String),
}
pub mod config;
pub mod compiler;
pub mod installer;
pub fn compile_file(
input_file: PathBuf,
output_log: Option<PathBuf>,
compiler_id: Option<String>,
settings: &Settings,
) -> Result<(), Error> {
compiler::compile_file_impl(
&input_file,
output_log.as_deref(), compiler_id.as_deref(), settings,
)
}
#[cfg(test)]
mod tests {
}