Return structured server errors in FFI
This commit is contained in:
parent
e80fb6b9d0
commit
dffe435c59
67
bindings.h
67
bindings.h
@ -5,6 +5,65 @@
|
||||
#include <cassert>
|
||||
|
||||
|
||||
struct FFIServerError {
|
||||
uint16_t status;
|
||||
const char *title;
|
||||
const char *detail;
|
||||
};
|
||||
|
||||
struct FFIError {
|
||||
enum class Tag : uint8_t {
|
||||
Server,
|
||||
Network,
|
||||
};
|
||||
|
||||
struct Server_Body {
|
||||
FFIServerError _0;
|
||||
};
|
||||
|
||||
struct Network_Body {
|
||||
const char *_0;
|
||||
};
|
||||
|
||||
Tag tag;
|
||||
union {
|
||||
Server_Body server;
|
||||
Network_Body network;
|
||||
};
|
||||
|
||||
static FFIError Server(const FFIServerError &_0) {
|
||||
FFIError result;
|
||||
::new (&result.server._0) (FFIServerError)(_0);
|
||||
result.tag = Tag::Server;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool IsServer() const {
|
||||
return tag == Tag::Server;
|
||||
}
|
||||
|
||||
const FFIServerError& AsServer() const {
|
||||
assert(IsServer());
|
||||
return server._0;
|
||||
}
|
||||
|
||||
static FFIError Network(const char *const &_0) {
|
||||
FFIError result;
|
||||
::new (&result.network._0) (const char*)(_0);
|
||||
result.tag = Tag::Network;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool IsNetwork() const {
|
||||
return tag == Tag::Network;
|
||||
}
|
||||
|
||||
const char*const & AsNetwork() const {
|
||||
assert(IsNetwork());
|
||||
return network._0;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct FFIResult {
|
||||
enum class Tag : uint8_t {
|
||||
@ -17,7 +76,7 @@ struct FFIResult {
|
||||
};
|
||||
|
||||
struct Err_Body {
|
||||
const char *_0;
|
||||
FFIError _0;
|
||||
};
|
||||
|
||||
Tag tag;
|
||||
@ -42,9 +101,9 @@ struct FFIResult {
|
||||
return ok._0;
|
||||
}
|
||||
|
||||
static FFIResult Err(const char *const &_0) {
|
||||
static FFIResult Err(const FFIError &_0) {
|
||||
FFIResult result;
|
||||
::new (&result.err._0) (const char*)(_0);
|
||||
::new (&result.err._0) (FFIError)(_0);
|
||||
result.tag = Tag::Err;
|
||||
return result;
|
||||
}
|
||||
@ -53,7 +112,7 @@ struct FFIResult {
|
||||
return tag == Tag::Err;
|
||||
}
|
||||
|
||||
const char*const & AsErr() const {
|
||||
const FFIError& AsErr() const {
|
||||
assert(IsErr());
|
||||
return err._0;
|
||||
}
|
||||
|
26
src/cache.rs
26
src/cache.rs
@ -83,29 +83,43 @@ pub fn update_file_caches(
|
||||
|
||||
pub fn from_file_cache<T: for<'de> Deserialize<'de>>(cache_path: &Path) -> Result<T> {
|
||||
#[cfg(not(test))]
|
||||
let file = File::open(cache_path).context(format!(
|
||||
let file = File::open(cache_path).with_context(|| {
|
||||
format!(
|
||||
"Object not found in API or in cache: {}",
|
||||
cache_path.file_name().unwrap_or_default().to_string_lossy()
|
||||
))?;
|
||||
)
|
||||
})?;
|
||||
#[cfg(test)]
|
||||
let file = tempfile()?; // cache always reads from an empty temp file in cfg(test)
|
||||
|
||||
let reader = BufReader::new(file);
|
||||
info!("returning value from cache: {:?}", cache_path);
|
||||
Ok(bincode::deserialize_from(reader)?)
|
||||
Ok(bincode::deserialize_from(reader).with_context(|| {
|
||||
format!(
|
||||
"Object not found in API or in cache: {}",
|
||||
cache_path.file_name().unwrap_or_default().to_string_lossy(),
|
||||
)
|
||||
})?)
|
||||
}
|
||||
|
||||
pub fn load_metadata_from_file_cache(cache_path: &Path) -> Result<Metadata> {
|
||||
#[cfg(not(test))]
|
||||
let file = File::open(cache_path).context(format!(
|
||||
let file = File::open(cache_path).with_context(|| {
|
||||
format!(
|
||||
"Object not found in API or in cache: {}",
|
||||
cache_path.file_name().unwrap_or_default().to_string_lossy()
|
||||
))?;
|
||||
)
|
||||
})?;
|
||||
#[cfg(test)]
|
||||
let file = tempfile()?; // cache always reads from an empty temp file in cfg(test)
|
||||
|
||||
let reader = BufReader::new(file);
|
||||
info!("returning value from cache: {:?}", cache_path);
|
||||
let metadata: Metadata = serde_json::from_reader(reader)?;
|
||||
let metadata: Metadata = serde_json::from_reader(reader).with_context(|| {
|
||||
format!(
|
||||
"Object not found in API or in cache: {}",
|
||||
cache_path.file_name().unwrap_or_default().to_string_lossy(),
|
||||
)
|
||||
})?;
|
||||
Ok(metadata)
|
||||
}
|
||||
|
@ -10,7 +10,11 @@ use log::{error, info};
|
||||
#[cfg(test)]
|
||||
use std::{println as info, println as error};
|
||||
|
||||
use crate::{log_server_error, result::FFIResult};
|
||||
use crate::{
|
||||
error::extract_error_from_response,
|
||||
log_server_error,
|
||||
result::{FFIError, FFIResult},
|
||||
};
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn init() -> bool {
|
||||
@ -33,34 +37,30 @@ pub extern "C" fn status_check(api_url: *const c_char) -> FFIResult<bool> {
|
||||
let api_url = unsafe { CStr::from_ptr(api_url) }.to_string_lossy();
|
||||
info!("status_check api_url: {:?}", api_url);
|
||||
|
||||
fn inner(api_url: &str) -> Result<Response> {
|
||||
fn inner(api_url: &str) -> Result<()> {
|
||||
#[cfg(not(test))]
|
||||
let api_url = Url::parse(api_url)?.join("v1/status")?;
|
||||
#[cfg(test)]
|
||||
let api_url = Url::parse(&mockito::server_url())?.join("v1/status")?;
|
||||
|
||||
Ok(reqwest::blocking::get(api_url)?)
|
||||
let resp = reqwest::blocking::get(api_url)?;
|
||||
let status = resp.status();
|
||||
let bytes = resp.bytes()?;
|
||||
if status.is_success() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(extract_error_from_response(status, &bytes))
|
||||
}
|
||||
}
|
||||
|
||||
match inner(&api_url) {
|
||||
Ok(resp) if resp.status() == 200 => {
|
||||
Ok(()) => {
|
||||
info!("status_check ok");
|
||||
FFIResult::Ok(true)
|
||||
}
|
||||
Ok(resp) => {
|
||||
error!("status_check failed. Server error");
|
||||
log_server_error(resp);
|
||||
let err_string = CString::new("API returned a non-200 status code".to_string())
|
||||
.expect("could not create CString")
|
||||
.into_raw();
|
||||
FFIResult::Err(err_string)
|
||||
}
|
||||
Err(err) => {
|
||||
error!("status_check failed. {}", err);
|
||||
let err_string = CString::new(err.to_string())
|
||||
.expect("could not create CString")
|
||||
.into_raw();
|
||||
FFIResult::Err(err_string)
|
||||
FFIResult::Err(FFIError::from(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -91,9 +91,17 @@ mod tests {
|
||||
FFIResult::Ok(success) => {
|
||||
assert_eq!(success, true);
|
||||
}
|
||||
FFIResult::Err(error) => panic!("status_check returned error: {:?}", unsafe {
|
||||
CStr::from_ptr(error).to_string_lossy()
|
||||
FFIResult::Err(error) => panic!(
|
||||
"status_check returned error: {:?}",
|
||||
match error {
|
||||
FFIError::Server(server_error) =>
|
||||
format!("{} {}", server_error.status, unsafe {
|
||||
CStr::from_ptr(server_error.title).to_string_lossy()
|
||||
}),
|
||||
FFIError::Network(network_error) =>
|
||||
unsafe { CStr::from_ptr(network_error).to_string_lossy() }.to_string(),
|
||||
}
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,12 +117,16 @@ mod tests {
|
||||
mock.assert();
|
||||
match result {
|
||||
FFIResult::Ok(success) => panic!("status_check returned Ok result: {:?}", success),
|
||||
FFIResult::Err(error) => {
|
||||
FFIResult::Err(error) => match error {
|
||||
FFIError::Server(server_error) => {
|
||||
assert_eq!(server_error.status, 500);
|
||||
assert_eq!(
|
||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
||||
"API returned a non-200 status code"
|
||||
unsafe { CStr::from_ptr(server_error.title).to_string_lossy() },
|
||||
"Internal Server Error"
|
||||
);
|
||||
}
|
||||
_ => panic!("status_check did not return a server error"),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
65
src/error.rs
65
src/error.rs
@ -1,3 +1,4 @@
|
||||
use std::fmt;
|
||||
use std::str;
|
||||
|
||||
use anyhow::{anyhow, Error};
|
||||
@ -10,27 +11,51 @@ use log::error;
|
||||
#[cfg(test)]
|
||||
use std::println as error;
|
||||
|
||||
pub fn extract_error_from_response(status: StatusCode, bytes: &Bytes) -> Error {
|
||||
match serde_json::from_slice::<HttpApiProblem>(bytes) {
|
||||
Ok(api_problem) => {
|
||||
let detail = api_problem.detail.unwrap_or("".to_string());
|
||||
error!(
|
||||
"Server {}: {}. {}",
|
||||
status.as_u16(),
|
||||
api_problem.title,
|
||||
#[derive(Debug)]
|
||||
pub struct ServerError {
|
||||
pub status: StatusCode,
|
||||
pub title: String,
|
||||
pub detail: Option<String>,
|
||||
}
|
||||
|
||||
impl fmt::Display for ServerError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if let Some(detail) = &self.detail {
|
||||
write!(
|
||||
f,
|
||||
"Server {} {}: {}",
|
||||
self.status.as_u16(),
|
||||
self.title,
|
||||
detail
|
||||
);
|
||||
anyhow!(format!(
|
||||
"Server {}: {}. {}",
|
||||
status.as_u16(),
|
||||
api_problem.title,
|
||||
detail
|
||||
))
|
||||
}
|
||||
Err(_) => {
|
||||
let detail = str::from_utf8(bytes).unwrap_or("unknown");
|
||||
error!("Server {}: {}", status.as_u16(), detail);
|
||||
anyhow!(format!("Server {}: {}", status.as_u16(), detail))
|
||||
)
|
||||
} else {
|
||||
write!(f, "Server {} {}", self.status.as_u16(), self.title)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_error_from_response(status: StatusCode, bytes: &Bytes) -> Error {
|
||||
match serde_json::from_slice::<HttpApiProblem>(bytes) {
|
||||
Ok(api_problem) => {
|
||||
let server_error = ServerError {
|
||||
status,
|
||||
title: api_problem.title,
|
||||
detail: api_problem.detail,
|
||||
};
|
||||
error!("{}", server_error);
|
||||
anyhow!(server_error)
|
||||
}
|
||||
Err(_) => {
|
||||
let title = str::from_utf8(bytes)
|
||||
.unwrap_or_else(|_| &status.canonical_reason().unwrap_or("unknown"))
|
||||
.to_string();
|
||||
let server_error = ServerError {
|
||||
status,
|
||||
title,
|
||||
detail: None,
|
||||
};
|
||||
error!("{}", server_error);
|
||||
anyhow!(server_error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,9 +11,13 @@ use log::{error, info};
|
||||
use std::{println as info, println as error};
|
||||
|
||||
use crate::{
|
||||
cache::file_cache_dir, cache::from_file_cache, cache::load_metadata_from_file_cache,
|
||||
cache::update_file_caches, error::extract_error_from_response, log_server_error,
|
||||
result::FFIResult,
|
||||
cache::file_cache_dir,
|
||||
cache::from_file_cache,
|
||||
cache::load_metadata_from_file_cache,
|
||||
cache::update_file_caches,
|
||||
error::extract_error_from_response,
|
||||
log_server_error,
|
||||
result::{FFIError, FFIResult},
|
||||
};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
@ -334,11 +338,7 @@ pub extern "C" fn create_interior_ref_list(
|
||||
Ok(interior_ref_list) => FFIResult::Ok(interior_ref_list.id),
|
||||
Err(err) => {
|
||||
error!("create_interior_ref_list failed. {}", err);
|
||||
// TODO: also need to drop this CString once C++ is done reading it
|
||||
let err_string = CString::new(err.to_string())
|
||||
.expect("could not create CString")
|
||||
.into_raw();
|
||||
FFIResult::Err(err_string)
|
||||
FFIResult::Err(FFIError::from(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -419,11 +419,7 @@ pub extern "C" fn update_interior_ref_list(
|
||||
Ok(interior_ref_list) => FFIResult::Ok(interior_ref_list.id),
|
||||
Err(err) => {
|
||||
error!("update_interior_ref_list failed. {}", err);
|
||||
// TODO: also need to drop this CString once C++ is done reading it
|
||||
let err_string = CString::new(err.to_string())
|
||||
.expect("could not create CString")
|
||||
.into_raw();
|
||||
FFIResult::Err(err_string)
|
||||
FFIResult::Err(FFIError::from(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -526,13 +522,8 @@ pub extern "C" fn get_interior_ref_list(
|
||||
})
|
||||
}
|
||||
Err(err) => {
|
||||
error!("interior_ref_list failed. {}", err);
|
||||
// TODO: how to do error handling?
|
||||
let err_string = CString::new(err.to_string())
|
||||
.expect("could not create CString")
|
||||
.into_raw();
|
||||
// TODO: also need to drop this CString once C++ is done reading it
|
||||
FFIResult::Err(err_string)
|
||||
error!("get_interior_ref_list failed. {}", err);
|
||||
FFIResult::Err(FFIError::from(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -633,12 +624,7 @@ pub extern "C" fn get_interior_ref_list_by_shop_id(
|
||||
}
|
||||
Err(err) => {
|
||||
error!("get_interior_ref_list_by_shop_id failed. {}", err);
|
||||
// TODO: how to do error handling?
|
||||
let err_string = CString::new(err.to_string())
|
||||
.expect("could not create CString")
|
||||
.into_raw();
|
||||
// TODO: also need to drop this CString once C++ is done reading it
|
||||
FFIResult::Err(err_string)
|
||||
FFIResult::Err(FFIError::from(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -742,11 +728,17 @@ mod tests {
|
||||
FFIResult::Ok(interior_ref_list_id) => {
|
||||
assert_eq!(interior_ref_list_id, 1);
|
||||
}
|
||||
FFIResult::Err(error) => {
|
||||
panic!("create_interior_ref_list returned error: {:?}", unsafe {
|
||||
CStr::from_ptr(error).to_string_lossy()
|
||||
})
|
||||
FFIResult::Err(error) => panic!(
|
||||
"create_interior_ref_list returned error: {:?}",
|
||||
match error {
|
||||
FFIError::Server(server_error) =>
|
||||
format!("{} {}", server_error.status, unsafe {
|
||||
CStr::from_ptr(server_error.title).to_string_lossy()
|
||||
}),
|
||||
FFIError::Network(network_error) =>
|
||||
unsafe { CStr::from_ptr(network_error).to_string_lossy() }.to_string(),
|
||||
}
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -805,12 +797,16 @@ mod tests {
|
||||
"create_interior_ref_list returned Ok result: {:?}",
|
||||
interior_ref_list_id
|
||||
),
|
||||
FFIResult::Err(error) => {
|
||||
FFIResult::Err(error) => match error {
|
||||
FFIError::Server(server_error) => {
|
||||
assert_eq!(server_error.status, 500);
|
||||
assert_eq!(
|
||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
||||
"Server 500: Internal Server Error"
|
||||
unsafe { CStr::from_ptr(server_error.title).to_string_lossy() },
|
||||
"Internal Server Error"
|
||||
);
|
||||
}
|
||||
_ => panic!("create_interior_ref_list did not return a server error"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -905,11 +901,17 @@ mod tests {
|
||||
FFIResult::Ok(interior_ref_list_id) => {
|
||||
assert_eq!(interior_ref_list_id, 1);
|
||||
}
|
||||
FFIResult::Err(error) => {
|
||||
panic!("update_interior_ref_list returned error: {:?}", unsafe {
|
||||
CStr::from_ptr(error).to_string_lossy()
|
||||
})
|
||||
FFIResult::Err(error) => panic!(
|
||||
"update_interior_ref_list returned error: {:?}",
|
||||
match error {
|
||||
FFIError::Server(server_error) =>
|
||||
format!("{} {}", server_error.status, unsafe {
|
||||
CStr::from_ptr(server_error.title).to_string_lossy()
|
||||
}),
|
||||
FFIError::Network(network_error) =>
|
||||
unsafe { CStr::from_ptr(network_error).to_string_lossy() }.to_string(),
|
||||
}
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -968,12 +970,16 @@ mod tests {
|
||||
"update_interior_ref_list returned Ok result: {:?}",
|
||||
interior_ref_list_id
|
||||
),
|
||||
FFIResult::Err(error) => {
|
||||
FFIResult::Err(error) => match error {
|
||||
FFIError::Server(server_error) => {
|
||||
assert_eq!(server_error.status, 500);
|
||||
assert_eq!(
|
||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
||||
"Server 500: Internal Server Error"
|
||||
unsafe { CStr::from_ptr(server_error.title).to_string_lossy() },
|
||||
"Internal Server Error"
|
||||
);
|
||||
}
|
||||
_ => panic!("update_interior_ref_list did not return a server error"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -1080,9 +1086,17 @@ mod tests {
|
||||
assert_eq!(raw_shelf.sort_on, std::ptr::null());
|
||||
assert_eq!(raw_shelf.sort_asc, true);
|
||||
}
|
||||
FFIResult::Err(error) => panic!("get_interior_ref_list returned error: {:?}", unsafe {
|
||||
CStr::from_ptr(error).to_string_lossy()
|
||||
FFIResult::Err(error) => panic!(
|
||||
"get_interior_ref_list returned error: {:?}",
|
||||
match error {
|
||||
FFIError::Server(server_error) =>
|
||||
format!("{} {}", server_error.status, unsafe {
|
||||
CStr::from_ptr(server_error.title).to_string_lossy()
|
||||
}),
|
||||
FFIError::Network(network_error) =>
|
||||
unsafe { CStr::from_ptr(network_error).to_string_lossy() }.to_string(),
|
||||
}
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1102,12 +1116,15 @@ mod tests {
|
||||
"get_interior_ref_list returned Ok result: {:#x?}",
|
||||
raw_interior_ref_vec
|
||||
),
|
||||
FFIResult::Err(error) => {
|
||||
FFIResult::Err(error) => match error {
|
||||
FFIError::Network(network_error) => {
|
||||
assert_eq!(
|
||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
||||
"io error: failed to fill whole buffer" // empty tempfile
|
||||
unsafe { CStr::from_ptr(network_error).to_string_lossy() },
|
||||
"Object not found in API or in cache: interior_ref_list_1.bin",
|
||||
);
|
||||
}
|
||||
_ => panic!("get_interior_ref_list did not return a network error"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -1216,7 +1233,14 @@ mod tests {
|
||||
}
|
||||
FFIResult::Err(error) => panic!(
|
||||
"get_interior_ref_list_by_shop_id returned error: {:?}",
|
||||
unsafe { CStr::from_ptr(error).to_string_lossy() }
|
||||
match error {
|
||||
FFIError::Server(server_error) =>
|
||||
format!("{} {}", server_error.status, unsafe {
|
||||
CStr::from_ptr(server_error.title).to_string_lossy()
|
||||
}),
|
||||
FFIError::Network(network_error) =>
|
||||
unsafe { CStr::from_ptr(network_error).to_string_lossy() }.to_string(),
|
||||
}
|
||||
),
|
||||
}
|
||||
}
|
||||
@ -1237,12 +1261,15 @@ mod tests {
|
||||
"get_interior_ref_list_by_shop_id returned Ok result: {:#x?}",
|
||||
raw_interior_ref_vec
|
||||
),
|
||||
FFIResult::Err(error) => {
|
||||
FFIResult::Err(error) => match error {
|
||||
FFIError::Network(network_error) => {
|
||||
assert_eq!(
|
||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
||||
"io error: failed to fill whole buffer" // empty tempfile
|
||||
unsafe { CStr::from_ptr(network_error).to_string_lossy() },
|
||||
"Object not found in API or in cache: shop_1_interior_ref_list.bin",
|
||||
);
|
||||
}
|
||||
_ => panic!("get_interior_ref_list_by_shop_id did not return a network error"),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ use std::{println as info, println as error};
|
||||
use crate::{
|
||||
cache::file_cache_dir, cache::from_file_cache, cache::load_metadata_from_file_cache,
|
||||
cache::update_file_caches, error::extract_error_from_response, log_server_error,
|
||||
result::FFIResult,
|
||||
result::{FFIError, FFIResult},
|
||||
};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
@ -207,12 +207,7 @@ pub extern "C" fn create_merchandise_list(
|
||||
}
|
||||
Err(err) => {
|
||||
error!("create_merchandise_list failed. {}", err);
|
||||
// TODO: how to do error handling?
|
||||
let err_string = CString::new(err.to_string())
|
||||
.expect("could not create CString")
|
||||
.into_raw();
|
||||
// TODO: also need to drop this CString once C++ is done reading it
|
||||
FFIResult::Err(err_string)
|
||||
FFIResult::Err(FFIError::from(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -312,12 +307,7 @@ pub extern "C" fn update_merchandise_list(
|
||||
}
|
||||
Err(err) => {
|
||||
error!("update_merchandise_list failed. {}", err);
|
||||
// TODO: how to do error handling?
|
||||
let err_string = CString::new(err.to_string())
|
||||
.expect("could not create CString")
|
||||
.into_raw();
|
||||
// TODO: also need to drop this CString once C++ is done reading it
|
||||
FFIResult::Err(err_string)
|
||||
FFIResult::Err(FFIError::from(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -428,13 +418,8 @@ pub extern "C" fn get_merchandise_list(
|
||||
FFIResult::Ok(RawMerchandiseVec { ptr, len, cap })
|
||||
}
|
||||
Err(err) => {
|
||||
error!("merchandise_list failed. {}", err);
|
||||
// TODO: how to do error handling?
|
||||
let err_string = CString::new(err.to_string())
|
||||
.expect("could not create CString")
|
||||
.into_raw();
|
||||
// TODO: also need to drop this CString once C++ is done reading it
|
||||
FFIResult::Err(err_string)
|
||||
error!("get_merchandise_list failed. {}", err);
|
||||
FFIResult::Err(FFIError::from(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -539,12 +524,7 @@ pub extern "C" fn get_merchandise_list_by_shop_id(
|
||||
}
|
||||
Err(err) => {
|
||||
error!("get_merchandise_list_by_shop_id failed. {}", err);
|
||||
// TODO: how to do error handling?
|
||||
let err_string = CString::new(err.to_string())
|
||||
.expect("could not create CString")
|
||||
.into_raw();
|
||||
// TODO: also need to drop this CString once C++ is done reading it
|
||||
FFIResult::Err(err_string)
|
||||
FFIResult::Err(FFIError::from(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -627,11 +607,17 @@ mod tests {
|
||||
assert_eq!(raw_merchandise.is_food, false);
|
||||
assert_eq!(raw_merchandise.price, 100);
|
||||
}
|
||||
FFIResult::Err(error) => {
|
||||
panic!("create_merchandise_list returned error: {:?}", unsafe {
|
||||
CStr::from_ptr(error).to_string_lossy()
|
||||
})
|
||||
FFIResult::Err(error) => panic!(
|
||||
"create_merchandise_list returned error: {:?}",
|
||||
match error {
|
||||
FFIError::Server(server_error) =>
|
||||
format!("{} {}", server_error.status, unsafe {
|
||||
CStr::from_ptr(server_error.title).to_string_lossy()
|
||||
}),
|
||||
FFIError::Network(network_error) =>
|
||||
unsafe { CStr::from_ptr(network_error).to_string_lossy() }.to_string(),
|
||||
}
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -666,12 +652,16 @@ mod tests {
|
||||
"create_merchandise_list returned Ok result: {:#x?}",
|
||||
raw_merchandise_vec
|
||||
),
|
||||
FFIResult::Err(error) => {
|
||||
FFIResult::Err(error) => match error {
|
||||
FFIError::Server(server_error) => {
|
||||
assert_eq!(server_error.status, 500);
|
||||
assert_eq!(
|
||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
||||
"Server 500: Internal Server Error"
|
||||
unsafe { CStr::from_ptr(server_error.title).to_string_lossy() },
|
||||
"Internal Server Error"
|
||||
);
|
||||
}
|
||||
_ => panic!("create_merchandise_list did not return a server error"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -745,11 +735,17 @@ mod tests {
|
||||
assert_eq!(raw_merchandise.is_food, false);
|
||||
assert_eq!(raw_merchandise.price, 100);
|
||||
}
|
||||
FFIResult::Err(error) => {
|
||||
panic!("update_merchandise_list returned error: {:?}", unsafe {
|
||||
CStr::from_ptr(error).to_string_lossy()
|
||||
})
|
||||
FFIResult::Err(error) => panic!(
|
||||
"update_merchandise_list returned error: {:?}",
|
||||
match error {
|
||||
FFIError::Server(server_error) =>
|
||||
format!("{} {}", server_error.status, unsafe {
|
||||
CStr::from_ptr(server_error.title).to_string_lossy()
|
||||
}),
|
||||
FFIError::Network(network_error) =>
|
||||
unsafe { CStr::from_ptr(network_error).to_string_lossy() }.to_string(),
|
||||
}
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -784,12 +780,16 @@ mod tests {
|
||||
"update_merchandise_list returned Ok result: {:#x?}",
|
||||
raw_merchandise_vec
|
||||
),
|
||||
FFIResult::Err(error) => {
|
||||
FFIResult::Err(error) => match error {
|
||||
FFIError::Server(server_error) => {
|
||||
assert_eq!(server_error.status, 500);
|
||||
assert_eq!(
|
||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
||||
"Server 500: Internal Server Error"
|
||||
unsafe { CStr::from_ptr(server_error.title).to_string_lossy() },
|
||||
"Internal Server Error"
|
||||
);
|
||||
}
|
||||
_ => panic!("update_merchandise_list did not return a server error"),
|
||||
},
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
@ -857,9 +857,17 @@ mod tests {
|
||||
"VendorItemWeapon".to_string(),
|
||||
);
|
||||
}
|
||||
FFIResult::Err(error) => panic!("get_merchandise_list returned error: {:?}", unsafe {
|
||||
CStr::from_ptr(error).to_string_lossy()
|
||||
FFIResult::Err(error) => panic!(
|
||||
"get_merchandise_list returned error: {:?}",
|
||||
match error {
|
||||
FFIError::Server(server_error) =>
|
||||
format!("{} {}", server_error.status, unsafe {
|
||||
CStr::from_ptr(server_error.title).to_string_lossy()
|
||||
}),
|
||||
FFIError::Network(network_error) =>
|
||||
unsafe { CStr::from_ptr(network_error).to_string_lossy() }.to_string(),
|
||||
}
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -879,12 +887,15 @@ mod tests {
|
||||
"get_merchandise_list returned Ok result: {:#x?}",
|
||||
raw_merchandise_vec
|
||||
),
|
||||
FFIResult::Err(error) => {
|
||||
FFIResult::Err(error) => match error {
|
||||
FFIError::Network(network_error) => {
|
||||
assert_eq!(
|
||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
||||
"io error: failed to fill whole buffer" // empty tempfile
|
||||
unsafe { CStr::from_ptr(network_error).to_string_lossy() },
|
||||
"Object not found in API or in cache: merchandise_list_1.bin",
|
||||
);
|
||||
}
|
||||
_ => panic!("get_merchandise_list did not return a network error"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -955,7 +966,14 @@ mod tests {
|
||||
}
|
||||
FFIResult::Err(error) => panic!(
|
||||
"get_merchandise_list_by_shop_id returned error: {:?}",
|
||||
unsafe { CStr::from_ptr(error).to_string_lossy() }
|
||||
match error {
|
||||
FFIError::Server(server_error) =>
|
||||
format!("{} {}", server_error.status, unsafe {
|
||||
CStr::from_ptr(server_error.title).to_string_lossy()
|
||||
}),
|
||||
FFIError::Network(network_error) =>
|
||||
unsafe { CStr::from_ptr(network_error).to_string_lossy() }.to_string(),
|
||||
}
|
||||
),
|
||||
}
|
||||
}
|
||||
@ -976,12 +994,15 @@ mod tests {
|
||||
"get_merchandise_list_by_shop_id returned Ok result: {:#x?}",
|
||||
raw_merchandise_vec
|
||||
),
|
||||
FFIResult::Err(error) => {
|
||||
FFIResult::Err(error) => match error {
|
||||
FFIError::Network(network_error) => {
|
||||
assert_eq!(
|
||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
||||
"io error: failed to fill whole buffer" // empty tempfile
|
||||
unsafe { CStr::from_ptr(network_error).to_string_lossy() },
|
||||
"Object not found in API or in cache: shop_1_merchandise_list.bin",
|
||||
);
|
||||
}
|
||||
_ => panic!("get_merchandise_list_by_shop_id did not return a network error"),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
62
src/owner.rs
62
src/owner.rs
@ -11,8 +11,10 @@ use log::{error, info};
|
||||
use std::{println as info, println as error};
|
||||
|
||||
use crate::{
|
||||
cache::file_cache_dir, cache::update_file_caches, error::extract_error_from_response,
|
||||
result::FFIResult,
|
||||
cache::file_cache_dir,
|
||||
cache::update_file_caches,
|
||||
error::extract_error_from_response,
|
||||
result::{FFIError, FFIResult},
|
||||
};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
@ -112,11 +114,7 @@ pub extern "C" fn create_owner(
|
||||
}
|
||||
Err(err) => {
|
||||
error!("create_owner failed. {}", err);
|
||||
// TODO: also need to drop this CString once C++ is done reading it
|
||||
let err_string = CString::new(err.to_string())
|
||||
.expect("could not create CString")
|
||||
.into_raw();
|
||||
FFIResult::Err(err_string)
|
||||
FFIResult::Err(FFIError::from(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -182,11 +180,7 @@ pub extern "C" fn update_owner(
|
||||
}
|
||||
Err(err) => {
|
||||
error!("update_owner failed. {}", err);
|
||||
// TODO: also need to drop this CString once C++ is done reading it
|
||||
let err_string = CString::new(err.to_string())
|
||||
.expect("could not create CString")
|
||||
.into_raw();
|
||||
FFIResult::Err(err_string)
|
||||
FFIResult::Err(FFIError::from(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -229,9 +223,17 @@ mod tests {
|
||||
);
|
||||
assert_eq!(raw_owner.mod_version, 1);
|
||||
}
|
||||
FFIResult::Err(error) => panic!("create_owner returned error: {:?}", unsafe {
|
||||
CStr::from_ptr(error).to_string_lossy()
|
||||
FFIResult::Err(error) => panic!(
|
||||
"create_owner returned error: {:?}",
|
||||
match error {
|
||||
FFIError::Server(server_error) =>
|
||||
format!("{} {}", server_error.status, unsafe {
|
||||
CStr::from_ptr(server_error.title).to_string_lossy()
|
||||
}),
|
||||
FFIError::Network(network_error) =>
|
||||
unsafe { CStr::from_ptr(network_error).to_string_lossy() }.to_string(),
|
||||
}
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -252,12 +254,16 @@ mod tests {
|
||||
FFIResult::Ok(raw_owner) => {
|
||||
panic!("create_owner returned Ok result: {:#x?}", raw_owner)
|
||||
}
|
||||
FFIResult::Err(error) => {
|
||||
FFIResult::Err(error) => match error {
|
||||
FFIError::Server(server_error) => {
|
||||
assert_eq!(server_error.status, 500);
|
||||
assert_eq!(
|
||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
||||
"Server 500: Internal Server Error"
|
||||
unsafe { CStr::from_ptr(server_error.title).to_string_lossy() },
|
||||
"Internal Server Error"
|
||||
);
|
||||
}
|
||||
_ => panic!("create_owner did not return a server error"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -291,9 +297,17 @@ mod tests {
|
||||
);
|
||||
assert_eq!(raw_owner.mod_version, 1);
|
||||
}
|
||||
FFIResult::Err(error) => panic!("update_owner returned error: {:?}", unsafe {
|
||||
CStr::from_ptr(error).to_string_lossy()
|
||||
FFIResult::Err(error) => panic!(
|
||||
"update_owner returned error: {:?}",
|
||||
match error {
|
||||
FFIError::Server(server_error) =>
|
||||
format!("{} {}", server_error.status, unsafe {
|
||||
CStr::from_ptr(server_error.title).to_string_lossy()
|
||||
}),
|
||||
FFIError::Network(network_error) =>
|
||||
unsafe { CStr::from_ptr(network_error).to_string_lossy() }.to_string(),
|
||||
}
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -314,12 +328,16 @@ mod tests {
|
||||
FFIResult::Ok(raw_owner) => {
|
||||
panic!("update_owner returned Ok result: {:#x?}", raw_owner)
|
||||
}
|
||||
FFIResult::Err(error) => {
|
||||
FFIResult::Err(error) => match error {
|
||||
FFIError::Server(server_error) => {
|
||||
assert_eq!(server_error.status, 500);
|
||||
assert_eq!(
|
||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
||||
"Server 500: Internal Server Error"
|
||||
unsafe { CStr::from_ptr(server_error.title).to_string_lossy() },
|
||||
"Internal Server Error"
|
||||
);
|
||||
}
|
||||
_ => panic!("update_owner did not return a server error"),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,62 @@
|
||||
use anyhow::Error;
|
||||
|
||||
use std::convert::From;
|
||||
use std::ffi::CString;
|
||||
use std::os::raw::c_char;
|
||||
use std::ptr::null;
|
||||
|
||||
use crate::error::ServerError;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub struct FFIServerError {
|
||||
pub status: u16,
|
||||
pub title: *const c_char,
|
||||
pub detail: *const c_char,
|
||||
}
|
||||
|
||||
impl From<&ServerError> for FFIServerError {
|
||||
fn from(server_error: &ServerError) -> Self {
|
||||
FFIServerError {
|
||||
status: server_error.status.as_u16(),
|
||||
// TODO: may need to drop these CStrings once C++ is done reading them
|
||||
title: CString::new(server_error.title.clone())
|
||||
.expect("could not create CString")
|
||||
.into_raw(),
|
||||
detail: match &server_error.detail {
|
||||
Some(detail) => CString::new(detail.clone())
|
||||
.expect("could not create CString")
|
||||
.into_raw(),
|
||||
None => null(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[repr(C, u8)]
|
||||
pub enum FFIError {
|
||||
Server(FFIServerError),
|
||||
Network(*const c_char),
|
||||
}
|
||||
|
||||
impl From<Error> for FFIError {
|
||||
fn from(error: Error) -> Self {
|
||||
if let Some(server_error) = error.downcast_ref::<ServerError>() {
|
||||
FFIError::Server(FFIServerError::from(server_error))
|
||||
} else {
|
||||
// TODO: also need to drop this CString once C++ is done reading it
|
||||
let err_string = CString::new(error.to_string())
|
||||
.expect("could not create CString")
|
||||
.into_raw();
|
||||
FFIError::Network(err_string)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[repr(C, u8)]
|
||||
pub enum FFIResult<T> {
|
||||
Ok(T),
|
||||
Err(*const c_char),
|
||||
Err(FFIError),
|
||||
}
|
||||
|
122
src/shop.rs
122
src/shop.rs
@ -11,9 +11,13 @@ use log::{error, info};
|
||||
use std::{println as info, println as error};
|
||||
|
||||
use crate::{
|
||||
cache::file_cache_dir, cache::from_file_cache, cache::load_metadata_from_file_cache,
|
||||
cache::update_file_caches, error::extract_error_from_response, log_server_error,
|
||||
result::FFIResult,
|
||||
cache::file_cache_dir,
|
||||
cache::from_file_cache,
|
||||
cache::load_metadata_from_file_cache,
|
||||
cache::update_file_caches,
|
||||
error::extract_error_from_response,
|
||||
log_server_error,
|
||||
result::{FFIError, FFIResult},
|
||||
};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
@ -155,11 +159,7 @@ pub extern "C" fn create_shop(
|
||||
}
|
||||
Err(err) => {
|
||||
error!("create_shop failed. {}", err);
|
||||
// TODO: also need to drop this CString once C++ is done reading it
|
||||
let err_string = CString::new(err.to_string())
|
||||
.expect("could not create CString")
|
||||
.into_raw();
|
||||
FFIResult::Err(err_string)
|
||||
FFIResult::Err(FFIError::from(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -272,11 +272,7 @@ pub extern "C" fn update_shop(
|
||||
}
|
||||
Err(err) => {
|
||||
error!("update_shop failed. {}", err);
|
||||
// TODO: also need to drop this CString once C++ is done reading it
|
||||
let err_string = CString::new(err.to_string())
|
||||
.expect("could not create CString")
|
||||
.into_raw();
|
||||
FFIResult::Err(err_string)
|
||||
FFIResult::Err(FFIError::from(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -346,12 +342,8 @@ pub extern "C" fn get_shop(
|
||||
FFIResult::Ok(RawShop::from(shop))
|
||||
}
|
||||
Err(err) => {
|
||||
error!("get_shop_list failed. {}", err);
|
||||
let err_string = CString::new(err.to_string())
|
||||
.expect("could not create CString")
|
||||
.into_raw();
|
||||
// TODO: also need to drop this CString once C++ is done reading it
|
||||
FFIResult::Err(err_string)
|
||||
error!("get_shop failed. {}", err);
|
||||
FFIResult::Err(FFIError::from(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -419,11 +411,7 @@ pub extern "C" fn list_shops(
|
||||
}
|
||||
Err(err) => {
|
||||
error!("list_shops failed. {}", err);
|
||||
let err_string = CString::new(err.to_string())
|
||||
.expect("could not create CString")
|
||||
.into_raw();
|
||||
// TODO: also need to drop this CString once C++ is done reading it
|
||||
FFIResult::Err(err_string)
|
||||
FFIResult::Err(FFIError::from(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -490,9 +478,17 @@ mod tests {
|
||||
);
|
||||
assert_eq!(raw_shop.vendor_keywords_exclude, true);
|
||||
}
|
||||
FFIResult::Err(error) => panic!("create_shop returned error: {:?}", unsafe {
|
||||
CStr::from_ptr(error).to_string_lossy()
|
||||
FFIResult::Err(error) => panic!(
|
||||
"create_shop returned error: {:?}",
|
||||
match error {
|
||||
FFIError::Server(server_error) =>
|
||||
format!("{} {}", server_error.status, unsafe {
|
||||
CStr::from_ptr(server_error.title).to_string_lossy()
|
||||
}),
|
||||
FFIError::Network(network_error) =>
|
||||
unsafe { CStr::from_ptr(network_error).to_string_lossy() }.to_string(),
|
||||
}
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -511,12 +507,16 @@ mod tests {
|
||||
mock.assert();
|
||||
match result {
|
||||
FFIResult::Ok(raw_shop) => panic!("create_shop returned Ok result: {:#x?}", raw_shop),
|
||||
FFIResult::Err(error) => {
|
||||
FFIResult::Err(error) => match error {
|
||||
FFIError::Server(server_error) => {
|
||||
assert_eq!(server_error.status, 500);
|
||||
assert_eq!(
|
||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
||||
"Server 500: Internal Server Error"
|
||||
unsafe { CStr::from_ptr(server_error.title).to_string_lossy() },
|
||||
"Internal Server Error"
|
||||
);
|
||||
}
|
||||
_ => panic!("create_shop did not return a server error"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -589,9 +589,17 @@ mod tests {
|
||||
);
|
||||
assert_eq!(raw_shop.vendor_keywords_exclude, true);
|
||||
}
|
||||
FFIResult::Err(error) => panic!("update_shop returned error: {:?}", unsafe {
|
||||
CStr::from_ptr(error).to_string_lossy()
|
||||
FFIResult::Err(error) => panic!(
|
||||
"update_shop returned error: {:?}",
|
||||
match error {
|
||||
FFIError::Server(server_error) =>
|
||||
format!("{} {}", server_error.status, unsafe {
|
||||
CStr::from_ptr(server_error.title).to_string_lossy()
|
||||
}),
|
||||
FFIError::Network(network_error) =>
|
||||
unsafe { CStr::from_ptr(network_error).to_string_lossy() }.to_string(),
|
||||
}
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -625,12 +633,16 @@ mod tests {
|
||||
mock.assert();
|
||||
match result {
|
||||
FFIResult::Ok(raw_shop) => panic!("update_shop returned Ok result: {:#x?}", raw_shop),
|
||||
FFIResult::Err(error) => {
|
||||
FFIResult::Err(error) => match error {
|
||||
FFIError::Server(server_error) => {
|
||||
assert_eq!(server_error.status, 500);
|
||||
assert_eq!(
|
||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
||||
"Server 500: Internal Server Error"
|
||||
unsafe { CStr::from_ptr(server_error.title).to_string_lossy() },
|
||||
"Internal Server Error"
|
||||
);
|
||||
}
|
||||
_ => panic!("update_shop did not return a server error"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -686,9 +698,17 @@ mod tests {
|
||||
);
|
||||
assert_eq!(raw_shop.vendor_keywords_exclude, true);
|
||||
}
|
||||
FFIResult::Err(error) => panic!("get_shop returned error: {:?}", unsafe {
|
||||
CStr::from_ptr(error).to_string_lossy()
|
||||
FFIResult::Err(error) => panic!(
|
||||
"get_shop returned error: {:?}",
|
||||
match error {
|
||||
FFIError::Server(server_error) =>
|
||||
format!("{} {}", server_error.status, unsafe {
|
||||
CStr::from_ptr(server_error.title).to_string_lossy()
|
||||
}),
|
||||
FFIError::Network(network_error) =>
|
||||
unsafe { CStr::from_ptr(network_error).to_string_lossy() }.to_string(),
|
||||
}
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -705,12 +725,15 @@ mod tests {
|
||||
mock.assert();
|
||||
match result {
|
||||
FFIResult::Ok(raw_shop) => panic!("get_shop returned Ok result: {:#x?}", raw_shop),
|
||||
FFIResult::Err(error) => {
|
||||
FFIResult::Err(error) => match error {
|
||||
FFIError::Network(network_error) => {
|
||||
assert_eq!(
|
||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
||||
"io error: failed to fill whole buffer" // empty tempfile
|
||||
unsafe { CStr::from_ptr(network_error).to_string_lossy() },
|
||||
"Object not found in API or in cache: shop_1.bin",
|
||||
);
|
||||
}
|
||||
_ => panic!("get_shop did not return a network error"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -771,9 +794,17 @@ mod tests {
|
||||
);
|
||||
assert_eq!(raw_shop.vendor_keywords_exclude, true);
|
||||
}
|
||||
FFIResult::Err(error) => panic!("list_shops returned error: {:?}", unsafe {
|
||||
CStr::from_ptr(error).to_string_lossy()
|
||||
FFIResult::Err(error) => panic!(
|
||||
"list_shops returned error: {:?}",
|
||||
match error {
|
||||
FFIError::Server(server_error) =>
|
||||
format!("{} {}", server_error.status, unsafe {
|
||||
CStr::from_ptr(server_error.title).to_string_lossy()
|
||||
}),
|
||||
FFIError::Network(network_error) =>
|
||||
unsafe { CStr::from_ptr(network_error).to_string_lossy() }.to_string(),
|
||||
}
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -790,12 +821,15 @@ mod tests {
|
||||
mock.assert();
|
||||
match result {
|
||||
FFIResult::Ok(raw_shop) => panic!("list_shops returned Ok result: {:#x?}", raw_shop),
|
||||
FFIResult::Err(error) => {
|
||||
FFIResult::Err(error) => match error {
|
||||
FFIError::Network(network_error) => {
|
||||
assert_eq!(
|
||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
||||
"io error: failed to fill whole buffer" // empty tempfile
|
||||
unsafe { CStr::from_ptr(network_error).to_string_lossy() },
|
||||
"Object not found in API or in cache: shops.bin",
|
||||
);
|
||||
}
|
||||
_ => panic!("list_shops did not return a network error"),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,8 +11,10 @@ use log::{error, info};
|
||||
use std::{println as info, println as error};
|
||||
|
||||
use crate::{
|
||||
cache::file_cache_dir, cache::update_file_caches, error::extract_error_from_response,
|
||||
result::FFIResult,
|
||||
cache::file_cache_dir,
|
||||
cache::update_file_caches,
|
||||
error::extract_error_from_response,
|
||||
result::{FFIError, FFIResult},
|
||||
};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
@ -193,11 +195,7 @@ pub extern "C" fn create_transaction(
|
||||
Ok(transaction) => FFIResult::Ok(RawTransaction::from(transaction)),
|
||||
Err(err) => {
|
||||
error!("create_transaction failed. {}", err);
|
||||
// TODO: also need to drop this CString once C++ is done reading it
|
||||
let err_string = CString::new(err.to_string())
|
||||
.expect("could not create CString")
|
||||
.into_raw();
|
||||
FFIResult::Err(err_string)
|
||||
FFIResult::Err(FFIError::from(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -294,9 +292,17 @@ mod tests {
|
||||
vec!["VendorItemMisc".to_string()]
|
||||
);
|
||||
}
|
||||
FFIResult::Err(error) => panic!("create_transaction returned error: {:?}", unsafe {
|
||||
CStr::from_ptr(error).to_string_lossy()
|
||||
FFIResult::Err(error) => panic!(
|
||||
"create_transaction returned error: {:?}",
|
||||
match error {
|
||||
FFIError::Server(server_error) =>
|
||||
format!("{} {}", server_error.status, unsafe {
|
||||
CStr::from_ptr(server_error.title).to_string_lossy()
|
||||
}),
|
||||
FFIError::Network(network_error) =>
|
||||
unsafe { CStr::from_ptr(network_error).to_string_lossy() }.to_string(),
|
||||
}
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@ -344,12 +350,16 @@ mod tests {
|
||||
"create_transaction returned Ok result: {:#?}",
|
||||
raw_transaction
|
||||
),
|
||||
FFIResult::Err(error) => {
|
||||
FFIResult::Err(error) => match error {
|
||||
FFIError::Server(server_error) => {
|
||||
assert_eq!(server_error.status, 500);
|
||||
assert_eq!(
|
||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
||||
"Server 500: Internal Server Error. Some error detail"
|
||||
unsafe { CStr::from_ptr(server_error.title).to_string_lossy() },
|
||||
"Internal Server Error"
|
||||
);
|
||||
}
|
||||
_ => panic!("create_transaction did not return a server error"),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user