mod session; use regex::Regex; use serde::{Deserialize, Serialize, de::DeserializeOwned}; use serde_json::{Result, Value}; use session::IsabelleSession; use std::{io::{BufRead, BufReader, BufWriter, Write}, path::PathBuf, process::{ChildStdout, Command, Stdio}}; use std::fmt::Debug; use isabelle_unicode::PrettyUnicode; use structopt::StructOpt; type SessionID = String; type TaskID = String; #[derive(Serialize, Deserialize)] #[serde(untagged)] enum ClientCommand { UseTheories { session_id : SessionID, theories : Vec, master_dir : Option }, SessionStart { session : String, include_sessions : Vec }, Echo (String), ShutDown } #[derive(Debug)] enum SyncAnswer { Ok (T), Error (E), } enum AsyncAnswer { Finished (T), Failed (E), Note (N) } #[derive(Deserialize, Debug)] struct ClientHello { isabelle_id : String, isabelle_version : String } #[derive(Deserialize)] struct SessionStartedOk { session_id : String, tmp_dir : String, task : String } #[derive(Deserialize)] struct SessionStartedNote { percentage : u8, task : TaskID, message : String, kind : String, session : String, theory : String } #[derive(Deserialize, Clone, Debug)] struct SessionStartedFinished { session_id : SessionID, tmp_dir : String, task : TaskID } #[derive(Deserialize, Clone, Debug)] struct UseTheoriesFinished { ok : bool, errors : Vec, nodes : Vec, task : TaskID, } #[derive(Deserialize, Clone, Debug)] struct IsabelleError { kind : String, message : String, pos : Span } #[derive(Deserialize, Clone, Debug)] struct IsabelleNode { messages : Vec, exports : Vec<()>, status : Status, theory_name : String, node_name : String } #[derive(Deserialize, Clone, Debug)] struct Status { percentage : u8, unprocessed : u64, running : u64, finished : u64, failed : u64, total : u64, consolidated : bool, canceled : bool, ok : bool, warned : u64 } #[derive(Deserialize,Clone, Debug)] struct Span { line : u64, offset : u64, end_offset : u64, file : String } #[derive(Deserialize)] struct AsyncStartOk { task : TaskID } fn decode_sync<'a, T> (msg: &'a str) -> Option> where T: Deserialize<'a> { let regex = Regex::new(r"^[a-zA-Z]+\b").unwrap(); let mat = regex.find(msg)?; let ty = &msg[..mat.end()]; let rest = &msg[mat.end()..]; match ty { "OK" => Some(SyncAnswer::Ok(serde_json::from_str(&rest).ok()?)), "ERROR" => Some(SyncAnswer::Error(rest.to_owned())), _ => None } } fn decode_async<'a,T,E,N> (msg: &'a str) -> Option> where T: Deserialize<'a>, N: Deserialize<'a>, E: Deserialize<'a> { let regex = Regex::new(r"^[a-zA-Z]+\b").unwrap(); let mat = regex.find(msg)?; let ty = &msg[..mat.end()]; let rest = &msg[mat.end()..]; match ty { "NOTE" => Some(AsyncAnswer::Note(serde_json::from_str(&rest).ok()?)), "FINISHED" => Some(AsyncAnswer::Finished(serde_json::from_str(&rest).ok()?)), "FAILED" => Some(AsyncAnswer::Failed(serde_json::from_str(&rest).ok()?)), _ => None } } trait Encode { fn encode (&self) -> String; } impl Encode for ClientCommand { fn encode (&self) -> String { let ty = match self { ClientCommand::UseTheories {..} => "use_theories", ClientCommand::SessionStart {..} => "session_start", ClientCommand::Echo (..) => "echo", ClientCommand::ShutDown => "shutdown" }; let blob = serde_json::to_string(self).unwrap(); let res = ty.to_owned() + &blob + "\n"; println!("encoded: {}", res); res } } fn wait_for_client(pipe: &mut BufReader) -> Option { for res in pipe.lines() { match res { Err(_) => (), Ok(line) => { let hello : Option> = decode_sync(&line); if let Some(SyncAnswer::Ok(data)) = hello { return Some(data); } } } } None } fn wait_for_async_task<'a,T,E,N,F> (reader: &mut BufReader, task: &str, mut prog: F) -> Option where T: DeserializeOwned, E: DeserializeOwned + Debug, N: DeserializeOwned, F: FnMut(N) { for res in reader.lines() { match res { Err(_) => (), Ok(line) => { let regex = Regex::new(r"^[0-9]+$").unwrap(); if regex.is_match(&line) { } else { match decode_async::(&line)? { AsyncAnswer::Finished(data) => return Some(data), AsyncAnswer::Failed(failed) => panic!("building session failed: {:?}", failed), AsyncAnswer::Note(val) => prog(val) } } } } }; None } fn get_async_task_id (reader: &mut BufReader) -> TaskID { let mut res = String::new(); reader.read_line(&mut res).unwrap(); match decode_sync(&res).unwrap() { SyncAnswer::Ok(AsyncStartOk { task }) => task, SyncAnswer::Error(_) => panic!("failed to start async task!") } } #[derive(StructOpt)] #[structopt(name = "isabelle-proto-POC", about="proof of concept for communicating with an Isabelle server")] struct Options { theory : String, #[structopt(name="directory", default_value=Box::leak(Box::new(std::env::current_dir().unwrap().into_os_string().into_string().unwrap())))] directory : String } fn main() { let options = Options::from_args(); let mut session = IsabelleSession::start_with_client(); let started = session.start_session("HOL".to_owned(),vec![]).unwrap(); println!("{:?}", started); let theoryresults = session.load_theory( options.theory, Some(options.directory) ); println!("loaded theory: {:?}", theoryresults); }