#![allow(dead_code)]
use thiserror::Error;
#[derive(Error, Debug)]
pub enum CcgoError {
#[error("Configuration error: {message}")]
Config {
message: String,
#[source]
source: Option<anyhow::Error>,
hint: Option<String>,
},
#[error("Dependency error for '{dependency}': {message}")]
Dependency {
dependency: String,
message: String,
#[source]
source: Option<anyhow::Error>,
hint: Option<String>,
},
#[error("Missing tool: {tool}")]
MissingTool {
tool: String,
required_for: String,
hint: String,
},
#[error("Build failed for {platform}: {message}")]
BuildFailure {
platform: String,
message: String,
#[source]
source: Option<anyhow::Error>,
diagnostics: Vec<String>,
hint: Option<String>,
},
#[error("Invalid project structure: {message}")]
ProjectStructure {
message: String,
expected: Vec<String>,
hint: String,
},
#[error("Version error: {message}")]
Version {
message: String,
current: Option<String>,
expected: Option<String>,
hint: String,
},
}
impl CcgoError {
pub fn config_error(message: impl Into<String>) -> Self {
Self::Config {
message: message.into(),
source: None,
hint: None,
}
}
pub fn config_error_with_hint(
message: impl Into<String>,
source: Option<anyhow::Error>,
hint: impl Into<String>,
) -> Self {
Self::Config {
message: message.into(),
source,
hint: Some(hint.into()),
}
}
pub fn dependency_error(dependency: impl Into<String>, message: impl Into<String>) -> Self {
Self::Dependency {
dependency: dependency.into(),
message: message.into(),
source: None,
hint: None,
}
}
pub fn dependency_error_with_hint(
dependency: impl Into<String>,
message: impl Into<String>,
hint: impl Into<String>,
) -> Self {
Self::Dependency {
dependency: dependency.into(),
message: message.into(),
source: None,
hint: Some(hint.into()),
}
}
pub fn missing_tool(
tool: impl Into<String>,
required_for: impl Into<String>,
hint: impl Into<String>,
) -> Self {
Self::MissingTool {
tool: tool.into(),
required_for: required_for.into(),
hint: hint.into(),
}
}
pub fn build_failure(platform: impl Into<String>, message: impl Into<String>) -> Self {
Self::BuildFailure {
platform: platform.into(),
message: message.into(),
source: None,
diagnostics: Vec::new(),
hint: None,
}
}
pub fn build_failure_with_diagnostics(
platform: impl Into<String>,
message: impl Into<String>,
diagnostics: Vec<String>,
hint: Option<String>,
) -> Self {
Self::BuildFailure {
platform: platform.into(),
message: message.into(),
source: None,
diagnostics,
hint,
}
}
pub fn project_structure_error(
message: impl Into<String>,
expected: Vec<String>,
hint: impl Into<String>,
) -> Self {
Self::ProjectStructure {
message: message.into(),
expected,
hint: hint.into(),
}
}
pub fn version_error(message: impl Into<String>, hint: impl Into<String>) -> Self {
Self::Version {
message: message.into(),
current: None,
expected: None,
hint: hint.into(),
}
}
pub fn display_with_hints(&self) {
use console::style;
eprintln!("\n{} {}", style("ERROR:").red().bold(), self);
match self {
CcgoError::Config { hint, .. }
| CcgoError::Dependency { hint, .. }
| CcgoError::BuildFailure { hint, .. } => {
if let Some(h) = hint {
eprintln!("\n{} {}", style("HINT:").yellow().bold(), h);
}
}
CcgoError::MissingTool { hint, .. }
| CcgoError::ProjectStructure { hint, .. }
| CcgoError::Version { hint, .. } => {
eprintln!("\n{} {}", style("HINT:").yellow().bold(), hint);
}
}
if let CcgoError::BuildFailure { diagnostics, .. } = self {
if !diagnostics.is_empty() {
eprintln!("\n{}", style("DIAGNOSTICS:").cyan().bold());
for diag in diagnostics {
eprintln!(" • {}", diag);
}
}
}
if let CcgoError::ProjectStructure { expected, .. } = self {
if !expected.is_empty() {
eprintln!("\n{}", style("EXPECTED:").cyan().bold());
for exp in expected {
eprintln!(" • {}", exp);
}
}
}
eprintln!();
}
}
pub trait ResultExt<T> {
fn with_hint(self, hint: impl Into<String>) -> Result<T, CcgoError>;
fn context_with_hint(
self,
context: impl Into<String>,
hint: impl Into<String>,
) -> Result<T, CcgoError>;
}
impl<T, E> ResultExt<T> for Result<T, E>
where
E: std::error::Error + Send + Sync + 'static,
{
fn with_hint(self, hint: impl Into<String>) -> Result<T, CcgoError> {
self.map_err(|e| CcgoError::config_error_with_hint(e.to_string(), Some(e.into()), hint))
}
fn context_with_hint(
self,
context: impl Into<String>,
hint: impl Into<String>,
) -> Result<T, CcgoError> {
self.map_err(|e| {
CcgoError::config_error_with_hint(
format!("{}: {}", context.into(), e),
Some(e.into()),
hint,
)
})
}
}
pub mod hints {
pub fn cmake() -> &'static str {
"Install CMake from https://cmake.org/ or use your package manager:\n\
• macOS: brew install cmake\n\
• Ubuntu: sudo apt install cmake\n\
• Windows: winget install Kitware.CMake"
}
pub fn git() -> &'static str {
"Install Git from https://git-scm.com/ or use your package manager:\n\
• macOS: brew install git\n\
• Ubuntu: sudo apt install git\n\
• Windows: winget install Git.Git"
}
pub fn android_ndk() -> &'static str {
"Install Android NDK via Android Studio SDK Manager:\n\
1. Open Android Studio\n\
2. Go to Tools → SDK Manager\n\
3. Select SDK Tools tab\n\
4. Check 'NDK (Side by side)' and 'CMake'\n\
5. Click Apply\n\
\n\
Or set ANDROID_NDK environment variable to your NDK installation path."
}
pub fn xcode() -> &'static str {
"Install Xcode from the App Store:\n\
1. Open App Store\n\
2. Search for 'Xcode'\n\
3. Click Install\n\
4. Run: sudo xcode-select --install"
}
pub fn visual_studio() -> &'static str {
"Install Visual Studio with C++ support:\n\
1. Download from https://visualstudio.microsoft.com/\n\
2. Select 'Desktop development with C++' workload\n\
3. Install\n\
\n\
Or use Visual Studio Build Tools for CI/headless environments."
}
pub fn mingw() -> &'static str {
"Install MinGW-w64:\n\
• Windows: winget install mingw-w64\n\
• Or download from: https://www.mingw-w64.org/\n\
\n\
Ensure MinGW bin directory is in your PATH."
}
pub fn gradle() -> &'static str {
"Install Gradle:\n\
• macOS: brew install gradle\n\
• Or use gradlew wrapper if available in the project"
}
pub fn python() -> &'static str {
"Install Python 3.7+ from https://www.python.org/ or use your package manager:\n\
• macOS: brew install python3\n\
• Ubuntu: sudo apt install python3\n\
• Windows: winget install Python.Python.3.11"
}
pub fn doxygen() -> &'static str {
"Install Doxygen for documentation generation:\n\
• macOS: brew install doxygen\n\
• Ubuntu: sudo apt install doxygen\n\
• Windows: winget install Doxygen.Doxygen"
}
pub fn ccgo_toml_not_found() -> &'static str {
"Could not find CCGO.toml in current directory or any parent directory.\n\
\n\
To create a new CCGO project:\n\
• Run: ccgo new my-project\n\
\n\
To initialize CCGO in an existing project:\n\
• Run: ccgo init"
}
pub fn invalid_ccgo_toml() -> &'static str {
"CCGO.toml is invalid. Common issues:\n\
• Missing [package] section\n\
• Invalid TOML syntax (check quotes, brackets, commas)\n\
• Invalid version string (must be semver like '1.0.0')\n\
\n\
See documentation: https://ccgo.dev/reference/ccgo-toml/"
}
pub fn dependency_resolution() -> &'static str {
"Dependency resolution failed. Try:\n\
• Check dependency versions in CCGO.toml\n\
• Run: ccgo update to update to latest versions\n\
• Check network connection for git dependencies\n\
• Check that dependency paths exist for path dependencies"
}
pub fn build_config() -> &'static str {
"Build configuration is invalid. Common issues:\n\
• Unsupported architecture specified\n\
• Invalid CMake variables in [build] section\n\
• Missing platform-specific configuration\n\
\n\
See documentation: https://ccgo.dev/reference/build-config/"
}
pub fn lockfile_mismatch() -> &'static str {
"CCGO.lock is out of sync with CCGO.toml.\n\
\n\
To fix:\n\
• Run: ccgo fetch to update lockfile\n\
• Or delete CCGO.lock and run ccgo fetch again\n\
\n\
In CI, use --locked flag to enforce lockfile usage."
}
}