1use crate::api::{
9 BorrowAnalysisRequest, BorrowAnalysisResponse, CascadeRequest, CascadeResponse,
10 ChainAnalysisRequest, ChainAnalysisResponse, DiscoverRequest, DiscoverResponse,
11 FlowAnalysisRequest, FlowAnalysisResponse, GraphSummaryRequest, GraphSummaryResponse,
12 LiteralSearchRequest, LiteralSearchResponse, LockAnalysisRequest, LockAnalysisResponse,
13 OverviewRequest, OverviewResponse, PingResponse, QueryResponse, RunRequest, RunResponse,
14 RyoqlRequest, SpecRequest, SpecResponse, StatusResponse, SuggestApplyRequest,
15 SuggestApplyResponse, SuggestChoicesRequest, SuggestChoicesResponse, SuggestCompareRequest,
16 SuggestCompareResponse, SuggestGenerateRequest, SuggestGenerateResponse, SuggestRequest,
17 SuggestResponse, SuggestVerifyRequest, SuggestVerifyResponse, TypeAnalysisRequest,
18 TypeAnalysisResponse,
19};
20use serde::{Deserialize, Serialize};
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
28pub enum RyoError {
29 NotFound { name: String },
31 ParseError { message: String },
33 InvalidRequest { message: String },
35 Internal { message: String },
37}
38
39impl std::fmt::Display for RyoError {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 match self {
42 Self::NotFound { name } => write!(f, "not found: {}", name),
43 Self::ParseError { message } => write!(f, "parse error: {}", message),
44 Self::InvalidRequest { message } => write!(f, "invalid request: {}", message),
45 Self::Internal { message } => write!(f, "internal error: {}", message),
46 }
47 }
48}
49
50impl std::error::Error for RyoError {}
51
52impl From<crate::api::ApiError> for RyoError {
57 fn from(e: crate::api::ApiError) -> Self {
58 use crate::api::ApiError;
59 match e {
60 ApiError::NotFound(name) => RyoError::NotFound { name },
61 ApiError::InvalidGoal(msg) => RyoError::InvalidRequest { message: msg },
62 ApiError::Planning(e) => RyoError::Internal {
63 message: e.to_string(),
64 },
65 ApiError::Execution(msg) => RyoError::Internal { message: msg },
66 ApiError::Storage(e) => RyoError::Internal {
67 message: e.to_string(),
68 },
69 ApiError::Conflicts(n) => RyoError::Internal {
70 message: format!("{} conflicts detected", n),
71 },
72 ApiError::SyntaxError(msg) => RyoError::ParseError { message: msg },
73 ApiError::Graph(e) => RyoError::Internal {
74 message: e.to_string(),
75 },
76 ApiError::Discover(e) => RyoError::Internal {
77 message: e.to_string(),
78 },
79 ApiError::Project(e) => RyoError::Internal {
80 message: e.to_string(),
81 },
82 ApiError::Spec(e) => RyoError::Internal {
83 message: e.to_string(),
84 },
85 ApiError::Resolve(e) => RyoError::InvalidRequest {
86 message: e.to_string(),
87 },
88 ApiError::Sync(e) => RyoError::Internal {
89 message: e.to_string(),
90 },
91 }
92 }
93}
94
95#[tarpc::service]
107pub trait RyoService {
108 async fn ping() -> PingResponse;
110
111 async fn status() -> StatusResponse;
113
114 async fn shutdown();
116
117 async fn discover(req: DiscoverRequest) -> Result<DiscoverResponse, RyoError>;
119
120 async fn overview(req: OverviewRequest) -> Result<OverviewResponse, RyoError>;
122
123 async fn run(req: RunRequest) -> Result<RunResponse, RyoError>;
125
126 async fn cascade(req: CascadeRequest) -> Result<CascadeResponse, RyoError>;
128
129 async fn graph_summary(req: GraphSummaryRequest) -> Result<GraphSummaryResponse, RyoError>;
131
132 async fn graph_type(req: TypeAnalysisRequest) -> Result<TypeAnalysisResponse, RyoError>;
134
135 async fn graph_flow(req: FlowAnalysisRequest) -> Result<FlowAnalysisResponse, RyoError>;
137
138 async fn graph_borrow(req: BorrowAnalysisRequest) -> Result<BorrowAnalysisResponse, RyoError>;
140
141 async fn graph_lock(req: LockAnalysisRequest) -> Result<LockAnalysisResponse, RyoError>;
143
144 async fn graph_chain(req: ChainAnalysisRequest) -> Result<ChainAnalysisResponse, RyoError>;
146
147 async fn suggest(req: SuggestRequest) -> Result<SuggestResponse, RyoError>;
149
150 async fn suggest_apply(req: SuggestApplyRequest) -> Result<SuggestApplyResponse, RyoError>;
152
153 async fn suggest_choices(
155 req: SuggestChoicesRequest,
156 ) -> Result<SuggestChoicesResponse, RyoError>;
157
158 async fn suggest_verify(req: SuggestVerifyRequest) -> Result<SuggestVerifyResponse, RyoError>;
160
161 async fn suggest_compare(
163 req: SuggestCompareRequest,
164 ) -> Result<SuggestCompareResponse, RyoError>;
165
166 async fn suggest_generate(
168 req: SuggestGenerateRequest,
169 ) -> Result<SuggestGenerateResponse, RyoError>;
170
171 async fn spec(req: SpecRequest) -> Result<SpecResponse, RyoError>;
173
174 async fn query_ryoql(req: RyoqlRequest) -> Result<QueryResponse, RyoError>;
176
177 async fn search_literal(req: LiteralSearchRequest) -> Result<LiteralSearchResponse, RyoError>;
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184
185 #[test]
186 fn test_ryo_error_display() {
187 let err = RyoError::NotFound {
188 name: "foo".to_string(),
189 };
190 assert_eq!(err.to_string(), "not found: foo");
191
192 let err = RyoError::ParseError {
193 message: "syntax error".to_string(),
194 };
195 assert_eq!(err.to_string(), "parse error: syntax error");
196
197 let err = RyoError::InvalidRequest {
198 message: "bad input".to_string(),
199 };
200 assert_eq!(err.to_string(), "invalid request: bad input");
201
202 let err = RyoError::Internal {
203 message: "crash".to_string(),
204 };
205 assert_eq!(err.to_string(), "internal error: crash");
206 }
207
208 #[test]
209 fn test_ryo_error_from_api_error() {
210 use crate::api::ApiError;
211
212 let api_err = ApiError::NotFound("bar".to_string());
213 let ryo_err: RyoError = api_err.into();
214 assert!(matches!(ryo_err, RyoError::NotFound { name } if name == "bar"));
215 }
216}