1pub mod args;
3pub mod results;
5
6use serde::Deserialize;
7use serde::Serialize;
8
9use crate::server::IsabelleServer;
10
11use std::fmt::Display;
12use std::io;
13use std::{
14 io::{BufRead, BufReader, BufWriter, Write},
15 net::TcpStream,
16};
17
18use self::args::*;
19use self::results::*;
20
21struct Command<T: serde::Serialize> {
24 pub name: String,
25 pub args: Option<T>,
26}
27
28impl<T: serde::Serialize> Command<T> {
29 fn as_string(&self) -> String {
31 let args = match &self.args {
32 Some(arg) => serde_json::to_string(&arg).expect("Could not serialize"),
33 None => "".to_owned(),
34 };
35 format!("{} {}\n", self.name, args)
36 }
37
38 fn as_bytes(&self) -> Vec<u8> {
40 self.as_string().as_bytes().to_owned()
41 }
42}
43
44impl<T: serde::Serialize> Display for Command<T> {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 write!(f, "{}", self.as_string().trim())
47 }
48}
49
50#[derive(Debug)]
52pub enum SyncResult<T, E> {
53 Ok(T),
55 Error(E),
57}
58
59impl<T, E> SyncResult<T, E> {
60 pub fn ok(&self) -> &T {
62 match self {
63 SyncResult::Ok(t) => t,
64 SyncResult::Error(_) => panic!("Called unwrap on error value"),
65 }
66 }
67}
68
69#[derive(Debug)]
71pub enum AsyncResult<T, F> {
72 Finished(T),
74 Failed(FailedResult<F>),
76 Error(Message),
78}
79
80impl<T, F> AsyncResult<T, F> {
81 pub fn finished(&self) -> &T {
83 match self {
84 AsyncResult::Finished(t) => t,
85 AsyncResult::Failed(_) => panic!("Called unwrap on Failed result"),
86 AsyncResult::Error(_) => panic!("Called unwrap on Error result"),
87 }
88 }
89}
90
91#[derive(Serialize, Deserialize, Debug)]
93pub struct FailedResult<T> {
94 #[serde(flatten)]
96 task: Task,
97 #[serde(flatten)]
99 pub message: Message,
100 #[serde(flatten)]
102 pub context: Option<T>,
103}
104
105pub struct IsabelleClient {
107 addr: String,
109 pass: String,
111}
112
113impl IsabelleClient {
114 pub fn connect(address: Option<&str>, port: u32, pass: &str) -> Self {
122 let addr = format!("{}:{}", address.unwrap_or("127.0.0.1"), port);
123
124 Self {
125 addr,
126 pass: pass.to_owned(),
127 }
128 }
129
130 pub fn for_server(server: &IsabelleServer) -> Self {
132 Self::connect(None, server.port(), server.password())
133 }
134
135 fn handshake(&self, stream: &TcpStream) -> io::Result<()> {
138 let mut writer = BufWriter::new(stream.try_clone().unwrap());
139 let mut reader = BufReader::new(stream.try_clone().unwrap());
140
141 writer.write_all(format!("{}\n", self.pass).as_bytes())?;
142 writer.flush()?;
143
144 if let Some(e) = stream.take_error()? {
145 return Err(e);
146 }
147
148 let mut res = String::new();
149 reader.read_line(&mut res)?;
150 log::trace!("Handshake result: {}", res.trim());
151 if !res.starts_with("OK") {
152 return Err(io::Error::new(
153 io::ErrorKind::PermissionDenied,
154 "Handshake failed",
155 ));
156 }
157 log::trace!("Handshake ok");
158 Ok(())
159 }
160
161 fn parse_response<T: serde::de::DeserializeOwned>(
163 &self,
164 mut res: &str,
165 ) -> Result<T, io::Error> {
166 if res.is_empty() {
167 res = "null";
169 }
170 match serde_json::from_str::<T>(res) {
171 Ok(r) => Ok(r),
172 Err(e) => Err(io::Error::new(
173 io::ErrorKind::InvalidData,
174 format!("{}: {}", e, res),
175 )),
176 }
177 }
178
179 fn new_connection(&self) -> io::Result<(BufReader<TcpStream>, BufWriter<TcpStream>)> {
183 let con = TcpStream::connect(&self.addr)?;
184
185 self.handshake(&con)?;
187
188 let writer = BufWriter::new(con.try_clone().unwrap());
189 let reader = BufReader::new(con.try_clone().unwrap());
190
191 Ok((reader, writer))
192 }
193
194 async fn dispatch_async<
205 T: Serialize,
206 R: serde::de::DeserializeOwned,
207 F: serde::de::DeserializeOwned,
208 >(
209 &self,
210 cmd: &Command<T>,
211 reader: &mut BufReader<TcpStream>,
212 writer: &mut BufWriter<TcpStream>,
213 ) -> Result<AsyncResult<R, F>, io::Error> {
214 if let SyncResult::Error(e) = self
216 .dispatch_sync::<T, Task, Message>(cmd, reader, writer)
217 .await?
218 {
219 return Ok(AsyncResult::Error(e));
221 };
222
223 let mut res = String::new();
225 loop {
226 res.clear();
227 reader.read_line(&mut res)?;
228 let res = res.trim();
229 if let Some(finish_response) = res.strip_prefix("FINISHED") {
230 let parsed = self.parse_response(finish_response.trim())?;
232 return Ok(AsyncResult::Finished(parsed));
233 } else if let Some(failed_response) = res.strip_prefix("FAILED") {
234 let parsed = self.parse_response(failed_response.trim())?;
236 return Ok(AsyncResult::Failed(parsed));
237 } else if let Some(note) = res.strip_prefix("NOTE") {
238 log::trace!("{}", note);
240 } else {
241 log::trace!("Unknown message format: {}", res);
244 }
245 }
246 }
247
248 async fn dispatch_sync<
255 T: Serialize,
256 R: serde::de::DeserializeOwned,
257 E: serde::de::DeserializeOwned,
258 >(
259 &self,
260 cmd: &Command<T>,
261 reader: &mut BufReader<TcpStream>,
262 writer: &mut BufWriter<TcpStream>,
263 ) -> Result<SyncResult<R, E>, io::Error> {
264 writer.write_all(&cmd.as_bytes())?;
265 writer.flush()?;
266 loop {
267 let mut res = String::new();
268 reader.read_line(&mut res)?;
269 let res = res.trim();
270 if let Some(response_ok) = res.strip_prefix("OK") {
271 let res = self.parse_response(response_ok.trim())?;
272 return Ok(SyncResult::Ok(res));
273 } else if let Some(response_er) = res.strip_prefix("ERROR") {
274 let res = self.parse_response(response_er.trim())?;
275 return Ok(SyncResult::Error(res));
276 } else {
277 log::trace!("Unknown message format: {}", res);
280 }
281 }
282 }
283
284 pub async fn echo(&mut self, echo: &str) -> Result<SyncResult<String, String>, io::Error> {
286 let cmd = Command {
287 name: "echo".to_owned(),
288 args: Some(echo.to_owned()),
289 };
290 let (mut reader, mut writer) = self.new_connection()?;
291 self.dispatch_sync(&cmd, &mut reader, &mut writer).await
292 }
293
294 pub async fn shutdown(&mut self) -> Result<SyncResult<(), String>, io::Error> {
297 let cmd: Command<()> = Command {
298 name: "shutdown".to_owned(),
299 args: None,
300 };
301 let (mut reader, mut writer) = self.new_connection()?;
302 self.dispatch_sync(&cmd, &mut reader, &mut writer).await
303 }
304
305 pub async fn cancel(&mut self, task_id: String) -> Result<SyncResult<(), ()>, io::Error> {
308 let cmd = Command {
309 name: "cancel".to_owned(),
310 args: Some(CancelArgs { task: task_id }),
311 };
312 let (mut reader, mut writer) = self.new_connection()?;
313 self.dispatch_sync(&cmd, &mut reader, &mut writer).await
314 }
315
316 pub async fn session_build(
318 &mut self,
319 args: &SessionBuildArgs,
320 ) -> Result<AsyncResult<SessionBuildResults, SessionBuildResults>, io::Error> {
321 let cmd = Command {
322 name: "session_build".to_owned(),
323 args: Some(args),
324 };
325 let (mut reader, mut writer) = self.new_connection()?;
326 self.dispatch_async(&cmd, &mut reader, &mut writer).await
327 }
328
329 pub async fn session_start(
332 &mut self,
333 args: &SessionBuildArgs,
334 ) -> Result<AsyncResult<SessionStartResult, ()>, io::Error> {
335 let cmd = Command {
336 name: "session_start".to_owned(),
337 args: Some(args),
338 };
339
340 let (mut reader, mut writer) = self.new_connection()?;
341 self.dispatch_async(&cmd, &mut reader, &mut writer).await
342 }
343
344 pub async fn session_stop(
346 &mut self,
347 args: &SessionStopArgs,
348 ) -> Result<AsyncResult<SessionStopResult, SessionStopResult>, io::Error> {
349 let cmd = Command {
350 name: "session_stop".to_owned(),
351 args: Some(args),
352 };
353
354 let (mut reader, mut writer) = self.new_connection()?;
355 self.dispatch_async(&cmd, &mut reader, &mut writer).await
356 }
357
358 pub async fn use_theories(
360 &mut self,
361 args: &UseTheoriesArgs,
362 ) -> Result<AsyncResult<UseTheoryResults, ()>, io::Error> {
363 let cmd = Command {
364 name: "use_theories".to_owned(),
365 args: Some(args),
366 };
367
368 let (mut reader, mut writer) = self.new_connection()?;
369 self.dispatch_async(&cmd, &mut reader, &mut writer).await
370 }
371
372 pub async fn purge_theories(
375 &mut self,
376 args: PurgeTheoryArgs,
377 ) -> Result<SyncResult<PurgeTheoryResults, ()>, io::Error> {
378 let cmd = Command {
379 name: "purge_theories".to_owned(),
380 args: Some(args),
381 };
382
383 let (mut reader, mut writer) = self.new_connection()?;
384 self.dispatch_sync(&cmd, &mut reader, &mut writer).await
385 }
386}
387
388#[cfg(test)]
389mod test {
390
391 use super::*;
392 use crate::server::run_server;
393 use serial_test::serial;
394
395 struct TestContext {
396 server: IsabelleServer,
397 client: IsabelleClient,
398 }
399
400 impl Drop for TestContext {
401 fn drop(&mut self) {
402 self.server.exit().unwrap();
403 }
404 }
405
406 fn setup_test() -> TestContext {
407 let server = run_server(Some("test")).unwrap();
408 let client = IsabelleClient::for_server(&server);
409 TestContext { server, client }
410 }
411
412 #[tokio::test]
413 #[serial]
414 async fn test_echo() {
415 let client = &mut setup_test().client;
416
417 let res = client.echo("echo").await.unwrap();
418
419 match res {
420 SyncResult::Ok(r) => assert_eq!(r, "echo".to_owned()),
421 SyncResult::Error(_) => unreachable!(),
422 }
423 }
424
425 #[tokio::test]
426 #[serial]
427 async fn test_shutdown() {
428 let client = &mut setup_test().client;
429
430 let res = client.shutdown().await.unwrap();
431 assert!(matches!(res, SyncResult::Ok(())));
432 }
433
434 #[tokio::test]
435 #[serial]
436 async fn test_session_build_hol() {
437 let client = &mut setup_test().client;
438
439 let arg = SessionBuildArgs::session("HOL");
440
441 let res = client.session_build(&arg).await.unwrap();
442 match res {
443 AsyncResult::Finished(res) => {
444 assert!(res.ok);
445 for s in res.sessions {
446 assert!(s.ok);
447 assert!(s.return_code == 0);
448 }
449 }
450 AsyncResult::Failed(_) | AsyncResult::Error(_) => unreachable!(),
451 }
452 }
453
454 #[tokio::test]
455 #[serial]
456 async fn test_session_build_unknown() {
457 let client = &mut setup_test().client;
458 let arg = SessionBuildArgs::session("unknown_sessions");
459
460 let res = client.session_build(&arg).await.unwrap();
461
462 assert!(matches!(res, AsyncResult::Failed(_)));
463 }
464
465 #[tokio::test]
466 #[serial]
467 async fn test_session_start_hol() {
468 let client = &mut setup_test().client;
469
470 let arg = SessionBuildArgs::session("HOL");
471
472 let res = client.session_start(&arg).await.unwrap();
473 assert!(matches!(res, AsyncResult::Finished(_)));
474 }
475
476 #[tokio::test]
477 #[serial]
478 async fn test_session_start_unknown() {
479 let client = &mut setup_test().client;
480 let arg = SessionBuildArgs::session("unknown_sessions");
481
482 let res = client.session_start(&arg).await.unwrap();
483
484 assert!(matches!(res, AsyncResult::Failed(_)));
485 }
486
487 #[tokio::test]
488 #[serial]
489 async fn test_session_stop_active() {
490 let client = &mut setup_test().client;
491
492 let arg = SessionBuildArgs::session("HOL");
493 let res = client.session_start(&arg).await.unwrap();
494 if let AsyncResult::Finished(res) = res {
495 let arg = SessionStopArgs {
496 session_id: res.session_id,
497 };
498 if let AsyncResult::Finished(stop_res) = client.session_stop(&arg).await.unwrap() {
499 assert!(stop_res.ok);
500 } else {
501 unreachable!();
502 }
503 } else {
504 unreachable!()
505 }
506 }
507
508 #[tokio::test]
509 #[serial]
510 async fn test_session_stop_inactive() {
511 let client = &mut setup_test().client;
512
513 let arg = SessionStopArgs {
514 session_id: "03202b1a-bde6-4d84-926b-d435aac365fe".to_owned(),
515 };
516 let got = client.session_stop(&arg).await.unwrap();
517 assert!(matches!(got, AsyncResult::Failed(_)));
518 }
519
520 #[tokio::test]
521 #[serial]
522 async fn test_session_stop_invalid() {
523 let client = &mut setup_test().client;
524
525 let arg = SessionStopArgs {
526 session_id: "abc".to_owned(),
527 };
528 let got = client.session_stop(&arg).await.unwrap();
529 assert!(matches!(got, AsyncResult::Error(_)));
530 }
531
532 #[tokio::test]
533 #[serial]
534 async fn use_theory_in_hol() {
535 let client = &mut setup_test().client;
536
537 let arg = SessionBuildArgs::session("HOL");
538 let res = client.session_start(&arg).await.unwrap();
539 if let AsyncResult::Finished(res) = res {
540 let arg =
541 UseTheoriesArgs::for_session(&res.session_id, &["~~/src/HOL/Examples/Drinker"]);
542
543 match client.use_theories(&arg).await.unwrap() {
544 AsyncResult::Error(e) => unreachable!("{:?}", e),
545 AsyncResult::Finished(got) => assert!(got.ok),
546 AsyncResult::Failed(f) => unreachable!("{:?}", f),
547 }
548 } else {
549 unreachable!()
550 }
551 }
552
553 #[tokio::test]
554 #[serial]
555 async fn use_theory_unknown() {
556 let client = &mut setup_test().client;
557
558 let arg = SessionBuildArgs::session("HOL");
559 let res = client.session_start(&arg).await.unwrap();
560 if let AsyncResult::Finished(res) = res {
561 let arg = UseTheoriesArgs::for_session(&res.session_id, &["~~/src/HOL/foo"]);
562 let got = client.use_theories(&arg).await.unwrap();
563
564 assert!(matches!(got, AsyncResult::Failed(_)));
565 } else {
566 unreachable!()
567 }
568 }
569}