use connection::APIArguments;
use connection::APIConnectionFactory;
use connection::HyperAPIConnectionFactory;
use connection::ResponseVerifyingAPIConnectionFactory;
use decompilation::Decompilation;
use decompilation::DecompilationArguments;
use error::Result;
use error::ResultExt;
use settings::Settings;
pub struct Decompiler {
conn_factory: Box<APIConnectionFactory>,
}
impl Decompiler {
pub fn new(settings: Settings) -> Self {
Decompiler {
conn_factory: Box::new(
ResponseVerifyingAPIConnectionFactory::new(
Box::new(HyperAPIConnectionFactory::new(settings))
)
),
}
}
pub fn start_decompilation(&self, args: DecompilationArguments) -> Result<Decompilation> {
let mut conn = self.conn_factory.new_connection();
let url = format!("{}/decompiler/decompilations", conn.api_url());
let api_args = self.create_api_args(args)?;
let response = conn.send_post_request(&url, api_args)
.chain_err(|| "failed to start a decompilation")?;
let id = response.json_value_as_string("id")
.ok_or_else(|| format!("{} returned invalid JSON response", url))?;
Ok(Decompilation::new(id, conn))
}
fn create_api_args(&self, mut args: DecompilationArguments) -> Result<APIArguments> {
let mut api_args = APIArguments::new();
api_args.add_string_arg("mode", "bin");
match args.take_input_file() {
Some(input_file) => {
api_args.add_file("input", input_file);
}
None => {
bail!("no input file given");
}
}
Ok(api_args)
}
#[cfg(test)]
fn with_conn_factory(conn_factory: Box<APIConnectionFactory>) -> Self {
Decompiler { conn_factory: conn_factory }
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::RefCell;
use std::rc::Rc;
use connection::tests::APIArgumentsBuilder;
use connection::tests::APIConnectionFactoryMock;
use connection::tests::APIConnectionMock;
use connection::tests::APIResponseBuilder;
use decompilation::DecompilationArguments;
use file::File;
fn create_decompiler() -> (Rc<RefCell<APIConnectionMock>>, Decompiler) {
let settings = Settings::new()
.with_api_key("test")
.with_api_url("https://retdec.com/service/api");
let conn = Rc::new(RefCell::new(APIConnectionMock::new(settings.clone())));
let conn_factory = Box::new(APIConnectionFactoryMock::new(conn.clone()));
(conn, Decompiler::with_conn_factory(conn_factory))
}
#[test]
fn decompiler_start_decompilation_starts_decompilation_with_correct_arguments() {
let (conn, decompiler) = create_decompiler();
let input_file = File::from_content_with_name(b"content", "file.exe");
let args = DecompilationArguments::new()
.with_input_file(input_file.clone());
conn.borrow_mut().add_response(
"POST",
"https://retdec.com/service/api/decompiler/decompilations",
Ok(
APIResponseBuilder::new()
.with_status_code(200)
.with_body(br#"{
"id": "ID"
}"#)
.build()
)
);
let decompilation = decompiler.start_decompilation(args)
.expect("decompilation should have succeeded");
assert_eq!(*decompilation.id(), "ID");
assert!(conn.borrow_mut().request_sent(
"POST",
"https://retdec.com/service/api/decompiler/decompilations",
APIArgumentsBuilder::new()
.with_string_arg("mode", "bin")
.with_file("input", input_file)
.build()
));
}
#[test]
fn decompiler_start_decompilation_returns_error_when_input_file_is_not_given() {
let (conn, decompiler) = create_decompiler();
let args = DecompilationArguments::new();
conn.borrow_mut().add_response(
"POST",
"https://retdec.com/service/api/decompiler/decompilations",
Ok(
APIResponseBuilder::new()
.with_status_code(200)
.with_body(br#"{
"id": "ID"
}"#)
.build()
)
);
let result = decompiler.start_decompilation(args);
let err = result.err().expect("expected start_decompilation() to fail");
assert_eq!(err.description(), "no input file given");
}
#[test]
fn decompiler_start_decompilation_returns_error_when_returned_json_does_not_contain_id() {
let (conn, decompiler) = create_decompiler();
let input_file = File::from_content_with_name(b"content", "file.exe");
let args = DecompilationArguments::new()
.with_input_file(input_file);
conn.borrow_mut().add_response(
"POST",
"https://retdec.com/service/api/decompiler/decompilations",
Ok(
APIResponseBuilder::new()
.with_status_code(200)
.with_body(b"{}")
.build()
)
);
let result = decompiler.start_decompilation(args);
let err = result.err().expect("expected start_decompilation() to fail");
assert_eq!(
err.description(),
"https://retdec.com/service/api/decompiler/decompilations returned invalid JSON response"
);
}
}