///|
/// Error formatting for CLI output
fn is_missing_file(path : String, cause : String, filename : String) -> Bool {
if not(path.has_suffix(filename)) {
return false
}
let missing = cause.contains("No such file") ||
cause.contains("no such file") ||
cause.contains("not found")
missing
}
///|
/// Format errors for user-facing output
pub fn format_error(
err : Error,
verbose : Bool,
) -> (String, Array[String], String?) {
let err_string = err.to_string()
let hints : Array[String] = []
let mut detail : String? = None
let message = match err {
@cli.ParseError::UnknownCommand(cmd) => {
hints.push("Run `moon run . --help` to see available commands.")
"Unknown command: " + cmd
}
@cli.ParseError::MissingArgument(msg) => {
hints.push("Run `moon run . --help` to see required arguments.")
msg
}
@cli.ParseError::UnknownFlag(flag) => {
hints.push("Run `moon run . --help` to see supported flags.")
"Unknown flag: " + flag
}
@config.ConfigError::ReadFile(path, cause) => {
detail = Some(cause)
if is_missing_file(path, cause, "moon.lib.json") {
hints.push("Create `moon.lib.json` in the current directory.")
hints.push("Run the command from the project root if needed.")
"No `moon.lib.json` found in the current directory."
} else {
hints.push("Check that the file exists and is readable.")
"Failed to read " + path
}
}
@config.ConfigError::ParseJson(path, cause) => {
detail = Some(cause)
hints.push("Check JSON syntax in " + path + ".")
"Invalid JSON in " + path
}
@config.ConfigError::DecodeJson(path, cause) => {
detail = Some(cause)
hints.push("Check required fields and types in " + path + ".")
"Invalid config format in " + path
}
@config.ConfigError::WriteFile(path, cause) => {
detail = Some(cause)
hints.push("Check that the path is writable.")
"Failed to write " + path
}
@builder.BuildError::EmptyRunArray => {
hints.push("Check build steps in the package manifest.")
"Build step has no command."
}
@builder.BuildError::CommandFailed(exit_code, cmd) => {
detail = Some("exit code \{exit_code}: \{cmd}")
hints.push("Run the command manually to debug.")
"Build step failed."
}
@cache.CacheError::CreateDir(path, cause) => {
detail = Some(cause)
hints.push("Check directory permissions.")
"Failed to create cache directory " + path
}
@cache.CacheError::CreateTarball(path, cause) => {
detail = Some(cause)
hints.push("Check available disk space.")
"Failed to create cache tarball " + path
}
@cache.CacheError::CacheFileNotFound(path) => {
hints.push("Try reinstalling the package or clean the cache.")
"Cache file not found: " + path
}
@cache.CacheError::ExtractTarball(path, cause) => {
detail = Some(cause)
hints.push("Check that the cache file is not corrupted.")
"Failed to extract cache tarball " + path
}
@cache.CacheError::CleanFailed(kind) => {
hints.push("Check file permissions for the cache directory.")
"Failed to clean " + kind + " cache"
}
@installer.InstallerError::CreateDir(path, cause) => {
detail = Some(cause)
hints.push("Check directory permissions.")
"Failed to create directory " + path
}
@installer.InstallerError::ReadManifest(path, cause) => {
detail = Some(cause)
hints.push("Check that the manifest file exists and is readable.")
"Failed to read manifest " + path
}
@installer.InstallerError::ParseManifest(path, cause) => {
detail = Some(cause)
hints.push("Check JSON syntax in the manifest.")
"Failed to parse manifest " + path
}
@installer.InstallerError::WriteManifest(path, cause) => {
detail = Some(cause)
hints.push("Check that the manifest path is writable.")
"Failed to write manifest " + path
}
@installer.InstallerError::CopyFiles(path, cause) => {
detail = Some(cause)
hints.push("Check write permissions for the install prefix.")
"Failed to copy files to " + path
}
@installer.InstallerError::TempDirCreate(cause) => {
detail = Some(cause)
hints.push("Check temporary directory permissions.")
"Failed to create temporary directory."
}
@installer.InstallerError::PackageNotInstalled(name) => {
hints.push("Run `moon run . list` to see installed packages.")
"Package '\{name}' is not installed."
}
@repository.RepositoryError::PackageNotFound(name, version) => {
hints.push("Check repository URLs and package versions.")
"Package not found: \{name}@\{version}"
}
@repository.RepositoryError::FetchError(msg) => {
detail = Some(msg)
hints.push("Check repository URL and network access.")
"Failed to fetch repository data."
}
@repository.RepositoryError::ParseError(msg) => {
detail = Some(msg)
hints.push("Repository data may be corrupted or incompatible.")
"Failed to parse repository manifest."
}
@resolver.ResolverError::CircularDependency(msg) => {
detail = Some(msg)
hints.push("Check for circular dependencies in `moon.lib.json`.")
"Circular dependency detected."
}
@resolver.ResolverError::PackageNotFound(name, version) => {
hints.push("Check repository list and package versions.")
"Package not found: \{name}@\{version}"
}
@resolver.ResolverError::ResolutionError(msg) => {
detail = Some(msg)
hints.push("Check repository list and network access.")
"Failed to resolve dependencies."
}
@builtin.Failure(msg) => {
detail = Some(msg)
"Unexpected error."
}
_ => "Unexpected error."
}
if not(verbose) {
match detail {
Some(_) => hints.push("Run with `--verbose` for more details.")
None => ()
}
}
let details = if verbose {
match detail {
Some(d) => Some(d)
None => Some(err_string)
}
} else {
None
}
(message, hints, details)
}