use crate::api::{
BorrowAnalysisRequest, BorrowAnalysisResponse, CascadeRequest, CascadeResponse,
ChainAnalysisRequest, ChainAnalysisResponse, DiscoverRequest, DiscoverResponse,
FlowAnalysisRequest, FlowAnalysisResponse, GraphSummaryRequest, GraphSummaryResponse,
LiteralSearchRequest, LiteralSearchResponse, LockAnalysisRequest, LockAnalysisResponse,
OverviewRequest, OverviewResponse, PingResponse, QueryResponse, RunRequest, RunResponse,
RyoqlRequest, SpecRequest, SpecResponse, StatusResponse, SuggestApplyRequest,
SuggestApplyResponse, SuggestChoicesRequest, SuggestChoicesResponse, SuggestCompareRequest,
SuggestCompareResponse, SuggestGenerateRequest, SuggestGenerateResponse, SuggestRequest,
SuggestResponse, SuggestVerifyRequest, SuggestVerifyResponse, TypeAnalysisRequest,
TypeAnalysisResponse,
};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum RyoError {
NotFound { name: String },
ParseError { message: String },
InvalidRequest { message: String },
Internal { message: String },
}
impl std::fmt::Display for RyoError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::NotFound { name } => write!(f, "not found: {}", name),
Self::ParseError { message } => write!(f, "parse error: {}", message),
Self::InvalidRequest { message } => write!(f, "invalid request: {}", message),
Self::Internal { message } => write!(f, "internal error: {}", message),
}
}
}
impl std::error::Error for RyoError {}
impl From<crate::api::ApiError> for RyoError {
fn from(e: crate::api::ApiError) -> Self {
use crate::api::ApiError;
match e {
ApiError::NotFound(name) => RyoError::NotFound { name },
ApiError::InvalidGoal(msg) => RyoError::InvalidRequest { message: msg },
ApiError::Planning(e) => RyoError::Internal {
message: e.to_string(),
},
ApiError::Execution(msg) => RyoError::Internal { message: msg },
ApiError::Storage(e) => RyoError::Internal {
message: e.to_string(),
},
ApiError::Conflicts(n) => RyoError::Internal {
message: format!("{} conflicts detected", n),
},
ApiError::SyntaxError(msg) => RyoError::ParseError { message: msg },
ApiError::Graph(e) => RyoError::Internal {
message: e.to_string(),
},
ApiError::Discover(e) => RyoError::Internal {
message: e.to_string(),
},
ApiError::Project(e) => RyoError::Internal {
message: e.to_string(),
},
ApiError::Spec(e) => RyoError::Internal {
message: e.to_string(),
},
ApiError::Resolve(e) => RyoError::InvalidRequest {
message: e.to_string(),
},
ApiError::Sync(e) => RyoError::Internal {
message: e.to_string(),
},
}
}
}
#[tarpc::service]
pub trait RyoService {
async fn ping() -> PingResponse;
async fn status() -> StatusResponse;
async fn shutdown();
async fn discover(req: DiscoverRequest) -> Result<DiscoverResponse, RyoError>;
async fn overview(req: OverviewRequest) -> Result<OverviewResponse, RyoError>;
async fn run(req: RunRequest) -> Result<RunResponse, RyoError>;
async fn cascade(req: CascadeRequest) -> Result<CascadeResponse, RyoError>;
async fn graph_summary(req: GraphSummaryRequest) -> Result<GraphSummaryResponse, RyoError>;
async fn graph_type(req: TypeAnalysisRequest) -> Result<TypeAnalysisResponse, RyoError>;
async fn graph_flow(req: FlowAnalysisRequest) -> Result<FlowAnalysisResponse, RyoError>;
async fn graph_borrow(req: BorrowAnalysisRequest) -> Result<BorrowAnalysisResponse, RyoError>;
async fn graph_lock(req: LockAnalysisRequest) -> Result<LockAnalysisResponse, RyoError>;
async fn graph_chain(req: ChainAnalysisRequest) -> Result<ChainAnalysisResponse, RyoError>;
async fn suggest(req: SuggestRequest) -> Result<SuggestResponse, RyoError>;
async fn suggest_apply(req: SuggestApplyRequest) -> Result<SuggestApplyResponse, RyoError>;
async fn suggest_choices(
req: SuggestChoicesRequest,
) -> Result<SuggestChoicesResponse, RyoError>;
async fn suggest_verify(req: SuggestVerifyRequest) -> Result<SuggestVerifyResponse, RyoError>;
async fn suggest_compare(
req: SuggestCompareRequest,
) -> Result<SuggestCompareResponse, RyoError>;
async fn suggest_generate(
req: SuggestGenerateRequest,
) -> Result<SuggestGenerateResponse, RyoError>;
async fn spec(req: SpecRequest) -> Result<SpecResponse, RyoError>;
async fn query_ryoql(req: RyoqlRequest) -> Result<QueryResponse, RyoError>;
async fn search_literal(req: LiteralSearchRequest) -> Result<LiteralSearchResponse, RyoError>;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ryo_error_display() {
let err = RyoError::NotFound {
name: "foo".to_string(),
};
assert_eq!(err.to_string(), "not found: foo");
let err = RyoError::ParseError {
message: "syntax error".to_string(),
};
assert_eq!(err.to_string(), "parse error: syntax error");
let err = RyoError::InvalidRequest {
message: "bad input".to_string(),
};
assert_eq!(err.to_string(), "invalid request: bad input");
let err = RyoError::Internal {
message: "crash".to_string(),
};
assert_eq!(err.to_string(), "internal error: crash");
}
#[test]
fn test_ryo_error_from_api_error() {
use crate::api::ApiError;
let api_err = ApiError::NotFound("bar".to_string());
let ryo_err: RyoError = api_err.into();
assert!(matches!(ryo_err, RyoError::NotFound { name } if name == "bar"));
}
}