一键导入
error-handling
Implement or review error handling patterns in Ruby Fast LSP, including Result, Option, logging, failures, and exception flows.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Implement or review error handling patterns in Ruby Fast LSP, including Result, Option, logging, failures, and exception flows.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | error-handling |
| description | Implement or review error handling patterns in Ruby Fast LSP, including Result, Option, logging, failures, and exception flows. |
Use this skill when implementing error handling, debugging error flows, or standardizing error patterns in the Ruby Fast LSP project. Provides consistent patterns for Result vs Option, error logging, and graceful degradation. Triggers: error handling, Result, Option, error, exception, failure, logging errors, error pattern.
"Almost all catastrophic system failures result from incorrect handling of non-fatal errors explicitly signaled in software."
Never ignore errors. Every error must be:
Result<T, E> When:// File operations
fn read_config(path: &Path) -> Result<Config, ConfigError>
// Network operations
fn send_request(req: Request) -> Result<Response, NetworkError>
// Parsing operations
fn parse_ruby(source: &str) -> Result<Ast, ParseError>
// Validation
fn validate_fqn(name: &str) -> Result<Fqn, ValidationError>
Option<T> When:// Lookups (not finding is normal)
fn find_definition(name: &str) -> Option<Definition>
// Optional values
fn get_documentation(symbol: &Symbol) -> Option<String>
// First/last operations
fn first_method(&self) -> Option<&Method>
// Configuration with defaults
fn get_custom_path(&self) -> Option<PathBuf>
Create domain-specific error types:
// src/errors.rs
use thiserror::Error;
#[derive(Error, Debug)]
pub enum IndexError {
#[error("File not found: {path}")]
FileNotFound { path: PathBuf },
#[error("Parse error in {file}: {message}")]
ParseError { file: PathBuf, message: String },
#[error("Invalid FQN: {name}")]
InvalidFqn { name: String },
#[error("Index locked")]
IndexLocked,
}
#[derive(Error, Debug)]
pub enum QueryError {
#[error("Document not indexed: {uri}")]
DocumentNotIndexed { uri: Url },
#[error("Position out of bounds: line {line}, char {character}")]
PositionOutOfBounds { line: u32, character: u32 },
}
Always add context when propagating:
use anyhow::{Context, Result};
// GOOD: Context added
fn load_workspace(root: &Path) -> Result<Workspace> {
let config = read_config(root)
.context("Failed to read workspace configuration")?;
let files = discover_files(root)
.with_context(|| format!("Failed to discover files in {}", root.display()))?;
Ok(Workspace { config, files })
}
// BAD: No context
fn load_workspace(root: &Path) -> Result<Workspace> {
let config = read_config(root)?; // Which file?
let files = discover_files(root)?; // What went wrong?
Ok(Workspace { config, files })
}
For errors that should bubble up:
fn process_file(path: &Path) -> Result<ProcessedFile> {
let content = std::fs::read_to_string(path)?;
let ast = parse(&content)?;
let symbols = extract_symbols(&ast)?;
Ok(ProcessedFile { path: path.to_owned(), symbols })
}
For error type conversion:
fn find_definition(name: &str) -> Result<Location, QueryError> {
self.index
.get(name)
.ok_or_else(|| QueryError::NotFound { name: name.to_string() })
}
For non-critical operations:
fn index_file(&mut self, path: &Path) {
match self.process_file(path) {
Ok(processed) => {
self.add_to_index(processed);
}
Err(e) => {
warn!("Failed to index {}: {}", path.display(), e);
// Continue with other files
}
}
}
For optional enhancements:
fn get_hover_info(&self, pos: Position) -> Option<Hover> {
match self.compute_hover(pos) {
Ok(hover) => Some(hover),
Err(e) => {
debug!("Hover computation failed: {}", e);
None // Graceful degradation
}
}
}
For batch operations:
fn index_workspace(&mut self, files: &[PathBuf]) -> IndexResult {
let mut errors = Vec::new();
let mut indexed = 0;
for file in files {
match self.index_file(file) {
Ok(()) => indexed += 1,
Err(e) => errors.push((file.clone(), e)),
}
}
IndexResult { indexed, errors }
}
| Level | When to Use | Example |
|---|---|---|
error! | Unrecoverable failure | "Failed to start server" |
warn! | Recoverable failure | "Failed to index file, skipping" |
info! | Significant events | "Indexed 500 files in 2.3s" |
debug! | Development info | "Processing method call at line 42" |
trace! | Verbose tracing | "Entering visit_node" |
Always include:
// GOOD
warn!(
"Failed to resolve constant {} in {}: {}",
constant_name,
file_path.display(),
error
);
// BAD
warn!("Error: {}", error); // No context
Use consistent format for timing:
let start = Instant::now();
let result = expensive_operation();
info!("[PERF] Indexing completed in {:?}", start.elapsed());
Return Result with JSON-RPC errors:
use tower_lsp::jsonrpc::{Error, Result};
pub async fn handle_definition(
server: &Server,
params: GotoDefinitionParams,
) -> Result<Option<GotoDefinitionResponse>> {
let uri = ¶ms.text_document_position_params.text_document.uri;
// Document not found is not an error, just no result
let doc = match server.get_document(uri) {
Some(doc) => doc,
None => return Ok(None),
};
// Internal errors should be logged but return gracefully
match server.find_definition(&doc, params.position) {
Ok(location) => Ok(location.map(GotoDefinitionResponse::Scalar)),
Err(e) => {
error!("Definition lookup failed: {}", e);
Ok(None) // Graceful degradation
}
}
}
Log errors but don't fail:
pub async fn handle_did_save(
server: &Server,
params: DidSaveTextDocumentParams,
) {
let uri = ¶ms.text_document.uri;
if let Err(e) = server.reindex_document(uri).await {
warn!("Failed to reindex {} on save: {}", uri, e);
// Don't propagate - editor doesn't expect response
}
}
// BAD
let _ = file.write_all(data);
// GOOD
if let Err(e) = file.write_all(data) {
warn!("Failed to write cache: {}", e);
}
// BAD
fn get_index(&self) -> &Index {
self.index.as_ref().unwrap() // Panics!
}
// GOOD
fn get_index(&self) -> Option<&Index> {
self.index.as_ref()
}
// OR with invariant assertion
fn get_index(&self) -> &Index {
self.index.as_ref().expect("Index must be initialized before use")
}
// BAD - Panics crash the server
async fn handle_request(&self) {
let doc = self.docs.lock().unwrap(); // Don't unwrap locks
}
// GOOD
async fn handle_request(&self) {
let doc = match self.docs.lock() {
Ok(guard) => guard,
Err(poisoned) => {
error!("Lock poisoned, recovering");
poisoned.into_inner()
}
};
}
// BAD
Err(anyhow!("Failed"))
// GOOD
Err(anyhow!("Failed to parse method at {}:{}", file, line))
Design Ruby Fast LSP features, understand module responsibilities, apply layer boundaries, and make structural changes.
Create, update, validate, and maintain Ruby Fast LSP C4 architecture diagrams using LikeC4.
Optimize Ruby Fast LSP performance, profile latency and memory, benchmark changes, and make performance-critical decisions.
Refactor Ruby Fast LSP modules, extract abstractions, reduce complexity, and preserve test coverage during structural changes.
Release a new version of ruby-fast-lsp. Bumps version in Cargo.toml, commits, tags, and pushes to trigger CI. Use when the user says /release, 'release', 'publish', 'bump version', 'cut a release', or 'new version'.
Review Ruby Fast LSP code changes and PRs using TigerStyle principles, project standards, and correctness checks.