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>
|
#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>
|
template<typename T>
|
||||||
struct FFIResult {
|
struct FFIResult {
|
||||||
enum class Tag : uint8_t {
|
enum class Tag : uint8_t {
|
||||||
@ -17,7 +76,7 @@ struct FFIResult {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct Err_Body {
|
struct Err_Body {
|
||||||
const char *_0;
|
FFIError _0;
|
||||||
};
|
};
|
||||||
|
|
||||||
Tag tag;
|
Tag tag;
|
||||||
@ -42,9 +101,9 @@ struct FFIResult {
|
|||||||
return ok._0;
|
return ok._0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static FFIResult Err(const char *const &_0) {
|
static FFIResult Err(const FFIError &_0) {
|
||||||
FFIResult result;
|
FFIResult result;
|
||||||
::new (&result.err._0) (const char*)(_0);
|
::new (&result.err._0) (FFIError)(_0);
|
||||||
result.tag = Tag::Err;
|
result.tag = Tag::Err;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -53,7 +112,7 @@ struct FFIResult {
|
|||||||
return tag == Tag::Err;
|
return tag == Tag::Err;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char*const & AsErr() const {
|
const FFIError& AsErr() const {
|
||||||
assert(IsErr());
|
assert(IsErr());
|
||||||
return err._0;
|
return err._0;
|
||||||
}
|
}
|
||||||
|
34
src/cache.rs
34
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> {
|
pub fn from_file_cache<T: for<'de> Deserialize<'de>>(cache_path: &Path) -> Result<T> {
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
let file = File::open(cache_path).context(format!(
|
let file = File::open(cache_path).with_context(|| {
|
||||||
"Object not found in API or in cache: {}",
|
format!(
|
||||||
cache_path.file_name().unwrap_or_default().to_string_lossy()
|
"Object not found in API or in cache: {}",
|
||||||
))?;
|
cache_path.file_name().unwrap_or_default().to_string_lossy()
|
||||||
|
)
|
||||||
|
})?;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
let file = tempfile()?; // cache always reads from an empty temp file in cfg(test)
|
let file = tempfile()?; // cache always reads from an empty temp file in cfg(test)
|
||||||
|
|
||||||
let reader = BufReader::new(file);
|
let reader = BufReader::new(file);
|
||||||
info!("returning value from cache: {:?}", cache_path);
|
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> {
|
pub fn load_metadata_from_file_cache(cache_path: &Path) -> Result<Metadata> {
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
let file = File::open(cache_path).context(format!(
|
let file = File::open(cache_path).with_context(|| {
|
||||||
"Object not found in API or in cache: {}",
|
format!(
|
||||||
cache_path.file_name().unwrap_or_default().to_string_lossy()
|
"Object not found in API or in cache: {}",
|
||||||
))?;
|
cache_path.file_name().unwrap_or_default().to_string_lossy()
|
||||||
|
)
|
||||||
|
})?;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
let file = tempfile()?; // cache always reads from an empty temp file in cfg(test)
|
let file = tempfile()?; // cache always reads from an empty temp file in cfg(test)
|
||||||
|
|
||||||
let reader = BufReader::new(file);
|
let reader = BufReader::new(file);
|
||||||
info!("returning value from cache: {:?}", cache_path);
|
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)
|
Ok(metadata)
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,11 @@ use log::{error, info};
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use std::{println as info, println as error};
|
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]
|
#[no_mangle]
|
||||||
pub extern "C" fn init() -> bool {
|
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();
|
let api_url = unsafe { CStr::from_ptr(api_url) }.to_string_lossy();
|
||||||
info!("status_check api_url: {:?}", api_url);
|
info!("status_check api_url: {:?}", api_url);
|
||||||
|
|
||||||
fn inner(api_url: &str) -> Result<Response> {
|
fn inner(api_url: &str) -> Result<()> {
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
let api_url = Url::parse(api_url)?.join("v1/status")?;
|
let api_url = Url::parse(api_url)?.join("v1/status")?;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
let api_url = Url::parse(&mockito::server_url())?.join("v1/status")?;
|
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) {
|
match inner(&api_url) {
|
||||||
Ok(resp) if resp.status() == 200 => {
|
Ok(()) => {
|
||||||
info!("status_check ok");
|
info!("status_check ok");
|
||||||
FFIResult::Ok(true)
|
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) => {
|
Err(err) => {
|
||||||
error!("status_check failed. {}", err);
|
error!("status_check failed. {}", err);
|
||||||
let err_string = CString::new(err.to_string())
|
FFIResult::Err(FFIError::from(err))
|
||||||
.expect("could not create CString")
|
|
||||||
.into_raw();
|
|
||||||
FFIResult::Err(err_string)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,9 +91,17 @@ mod tests {
|
|||||||
FFIResult::Ok(success) => {
|
FFIResult::Ok(success) => {
|
||||||
assert_eq!(success, true);
|
assert_eq!(success, true);
|
||||||
}
|
}
|
||||||
FFIResult::Err(error) => panic!("status_check returned error: {:?}", unsafe {
|
FFIResult::Err(error) => panic!(
|
||||||
CStr::from_ptr(error).to_string_lossy()
|
"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();
|
mock.assert();
|
||||||
match result {
|
match result {
|
||||||
FFIResult::Ok(success) => panic!("status_check returned Ok result: {:?}", success),
|
FFIResult::Ok(success) => panic!("status_check returned Ok result: {:?}", success),
|
||||||
FFIResult::Err(error) => {
|
FFIResult::Err(error) => match error {
|
||||||
assert_eq!(
|
FFIError::Server(server_error) => {
|
||||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
assert_eq!(server_error.status, 500);
|
||||||
"API returned a non-200 status code"
|
assert_eq!(
|
||||||
);
|
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 std::str;
|
||||||
|
|
||||||
use anyhow::{anyhow, Error};
|
use anyhow::{anyhow, Error};
|
||||||
@ -10,27 +11,51 @@ use log::error;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use std::println as error;
|
use std::println as error;
|
||||||
|
|
||||||
pub fn extract_error_from_response(status: StatusCode, bytes: &Bytes) -> Error {
|
#[derive(Debug)]
|
||||||
match serde_json::from_slice::<HttpApiProblem>(bytes) {
|
pub struct ServerError {
|
||||||
Ok(api_problem) => {
|
pub status: StatusCode,
|
||||||
let detail = api_problem.detail.unwrap_or("".to_string());
|
pub title: String,
|
||||||
error!(
|
pub detail: Option<String>,
|
||||||
"Server {}: {}. {}",
|
}
|
||||||
status.as_u16(),
|
|
||||||
api_problem.title,
|
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
|
detail
|
||||||
);
|
)
|
||||||
anyhow!(format!(
|
} else {
|
||||||
"Server {}: {}. {}",
|
write!(f, "Server {} {}", self.status.as_u16(), self.title)
|
||||||
status.as_u16(),
|
}
|
||||||
api_problem.title,
|
}
|
||||||
detail
|
}
|
||||||
))
|
|
||||||
}
|
pub fn extract_error_from_response(status: StatusCode, bytes: &Bytes) -> Error {
|
||||||
Err(_) => {
|
match serde_json::from_slice::<HttpApiProblem>(bytes) {
|
||||||
let detail = str::from_utf8(bytes).unwrap_or("unknown");
|
Ok(api_problem) => {
|
||||||
error!("Server {}: {}", status.as_u16(), detail);
|
let server_error = ServerError {
|
||||||
anyhow!(format!("Server {}: {}", status.as_u16(), detail))
|
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 std::{println as info, println as error};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
cache::file_cache_dir, cache::from_file_cache, cache::load_metadata_from_file_cache,
|
cache::file_cache_dir,
|
||||||
cache::update_file_caches, error::extract_error_from_response, log_server_error,
|
cache::from_file_cache,
|
||||||
result::FFIResult,
|
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)]
|
#[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),
|
Ok(interior_ref_list) => FFIResult::Ok(interior_ref_list.id),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("create_interior_ref_list failed. {}", err);
|
error!("create_interior_ref_list failed. {}", err);
|
||||||
// TODO: also need to drop this CString once C++ is done reading it
|
FFIResult::Err(FFIError::from(err))
|
||||||
let err_string = CString::new(err.to_string())
|
|
||||||
.expect("could not create CString")
|
|
||||||
.into_raw();
|
|
||||||
FFIResult::Err(err_string)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -419,11 +419,7 @@ pub extern "C" fn update_interior_ref_list(
|
|||||||
Ok(interior_ref_list) => FFIResult::Ok(interior_ref_list.id),
|
Ok(interior_ref_list) => FFIResult::Ok(interior_ref_list.id),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("update_interior_ref_list failed. {}", err);
|
error!("update_interior_ref_list failed. {}", err);
|
||||||
// TODO: also need to drop this CString once C++ is done reading it
|
FFIResult::Err(FFIError::from(err))
|
||||||
let err_string = CString::new(err.to_string())
|
|
||||||
.expect("could not create CString")
|
|
||||||
.into_raw();
|
|
||||||
FFIResult::Err(err_string)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -526,13 +522,8 @@ pub extern "C" fn get_interior_ref_list(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("interior_ref_list failed. {}", err);
|
error!("get_interior_ref_list failed. {}", err);
|
||||||
// TODO: how to do error handling?
|
FFIResult::Err(FFIError::from(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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -633,12 +624,7 @@ pub extern "C" fn get_interior_ref_list_by_shop_id(
|
|||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("get_interior_ref_list_by_shop_id failed. {}", err);
|
error!("get_interior_ref_list_by_shop_id failed. {}", err);
|
||||||
// TODO: how to do error handling?
|
FFIResult::Err(FFIError::from(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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -742,11 +728,17 @@ mod tests {
|
|||||||
FFIResult::Ok(interior_ref_list_id) => {
|
FFIResult::Ok(interior_ref_list_id) => {
|
||||||
assert_eq!(interior_ref_list_id, 1);
|
assert_eq!(interior_ref_list_id, 1);
|
||||||
}
|
}
|
||||||
FFIResult::Err(error) => {
|
FFIResult::Err(error) => panic!(
|
||||||
panic!("create_interior_ref_list returned error: {:?}", unsafe {
|
"create_interior_ref_list returned error: {:?}",
|
||||||
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(),
|
||||||
|
}
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -805,12 +797,16 @@ mod tests {
|
|||||||
"create_interior_ref_list returned Ok result: {:?}",
|
"create_interior_ref_list returned Ok result: {:?}",
|
||||||
interior_ref_list_id
|
interior_ref_list_id
|
||||||
),
|
),
|
||||||
FFIResult::Err(error) => {
|
FFIResult::Err(error) => match error {
|
||||||
assert_eq!(
|
FFIError::Server(server_error) => {
|
||||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
assert_eq!(server_error.status, 500);
|
||||||
"Server 500: Internal Server Error"
|
assert_eq!(
|
||||||
);
|
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) => {
|
FFIResult::Ok(interior_ref_list_id) => {
|
||||||
assert_eq!(interior_ref_list_id, 1);
|
assert_eq!(interior_ref_list_id, 1);
|
||||||
}
|
}
|
||||||
FFIResult::Err(error) => {
|
FFIResult::Err(error) => panic!(
|
||||||
panic!("update_interior_ref_list returned error: {:?}", unsafe {
|
"update_interior_ref_list returned error: {:?}",
|
||||||
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(),
|
||||||
|
}
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -968,12 +970,16 @@ mod tests {
|
|||||||
"update_interior_ref_list returned Ok result: {:?}",
|
"update_interior_ref_list returned Ok result: {:?}",
|
||||||
interior_ref_list_id
|
interior_ref_list_id
|
||||||
),
|
),
|
||||||
FFIResult::Err(error) => {
|
FFIResult::Err(error) => match error {
|
||||||
assert_eq!(
|
FFIError::Server(server_error) => {
|
||||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
assert_eq!(server_error.status, 500);
|
||||||
"Server 500: Internal Server Error"
|
assert_eq!(
|
||||||
);
|
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_on, std::ptr::null());
|
||||||
assert_eq!(raw_shelf.sort_asc, true);
|
assert_eq!(raw_shelf.sort_asc, true);
|
||||||
}
|
}
|
||||||
FFIResult::Err(error) => panic!("get_interior_ref_list returned error: {:?}", unsafe {
|
FFIResult::Err(error) => panic!(
|
||||||
CStr::from_ptr(error).to_string_lossy()
|
"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?}",
|
"get_interior_ref_list returned Ok result: {:#x?}",
|
||||||
raw_interior_ref_vec
|
raw_interior_ref_vec
|
||||||
),
|
),
|
||||||
FFIResult::Err(error) => {
|
FFIResult::Err(error) => match error {
|
||||||
assert_eq!(
|
FFIError::Network(network_error) => {
|
||||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
assert_eq!(
|
||||||
"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!(
|
FFIResult::Err(error) => panic!(
|
||||||
"get_interior_ref_list_by_shop_id returned error: {:?}",
|
"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?}",
|
"get_interior_ref_list_by_shop_id returned Ok result: {:#x?}",
|
||||||
raw_interior_ref_vec
|
raw_interior_ref_vec
|
||||||
),
|
),
|
||||||
FFIResult::Err(error) => {
|
FFIResult::Err(error) => match error {
|
||||||
assert_eq!(
|
FFIError::Network(network_error) => {
|
||||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
assert_eq!(
|
||||||
"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::{
|
use crate::{
|
||||||
cache::file_cache_dir, cache::from_file_cache, cache::load_metadata_from_file_cache,
|
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,
|
cache::update_file_caches, error::extract_error_from_response, log_server_error,
|
||||||
result::FFIResult,
|
result::{FFIError, FFIResult},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
@ -207,12 +207,7 @@ pub extern "C" fn create_merchandise_list(
|
|||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("create_merchandise_list failed. {}", err);
|
error!("create_merchandise_list failed. {}", err);
|
||||||
// TODO: how to do error handling?
|
FFIResult::Err(FFIError::from(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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -312,12 +307,7 @@ pub extern "C" fn update_merchandise_list(
|
|||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("update_merchandise_list failed. {}", err);
|
error!("update_merchandise_list failed. {}", err);
|
||||||
// TODO: how to do error handling?
|
FFIResult::Err(FFIError::from(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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -428,13 +418,8 @@ pub extern "C" fn get_merchandise_list(
|
|||||||
FFIResult::Ok(RawMerchandiseVec { ptr, len, cap })
|
FFIResult::Ok(RawMerchandiseVec { ptr, len, cap })
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("merchandise_list failed. {}", err);
|
error!("get_merchandise_list failed. {}", err);
|
||||||
// TODO: how to do error handling?
|
FFIResult::Err(FFIError::from(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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -539,12 +524,7 @@ pub extern "C" fn get_merchandise_list_by_shop_id(
|
|||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("get_merchandise_list_by_shop_id failed. {}", err);
|
error!("get_merchandise_list_by_shop_id failed. {}", err);
|
||||||
// TODO: how to do error handling?
|
FFIResult::Err(FFIError::from(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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -627,11 +607,17 @@ mod tests {
|
|||||||
assert_eq!(raw_merchandise.is_food, false);
|
assert_eq!(raw_merchandise.is_food, false);
|
||||||
assert_eq!(raw_merchandise.price, 100);
|
assert_eq!(raw_merchandise.price, 100);
|
||||||
}
|
}
|
||||||
FFIResult::Err(error) => {
|
FFIResult::Err(error) => panic!(
|
||||||
panic!("create_merchandise_list returned error: {:?}", unsafe {
|
"create_merchandise_list returned error: {:?}",
|
||||||
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(),
|
||||||
|
}
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -666,12 +652,16 @@ mod tests {
|
|||||||
"create_merchandise_list returned Ok result: {:#x?}",
|
"create_merchandise_list returned Ok result: {:#x?}",
|
||||||
raw_merchandise_vec
|
raw_merchandise_vec
|
||||||
),
|
),
|
||||||
FFIResult::Err(error) => {
|
FFIResult::Err(error) => match error {
|
||||||
assert_eq!(
|
FFIError::Server(server_error) => {
|
||||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
assert_eq!(server_error.status, 500);
|
||||||
"Server 500: Internal Server Error"
|
assert_eq!(
|
||||||
);
|
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.is_food, false);
|
||||||
assert_eq!(raw_merchandise.price, 100);
|
assert_eq!(raw_merchandise.price, 100);
|
||||||
}
|
}
|
||||||
FFIResult::Err(error) => {
|
FFIResult::Err(error) => panic!(
|
||||||
panic!("update_merchandise_list returned error: {:?}", unsafe {
|
"update_merchandise_list returned error: {:?}",
|
||||||
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(),
|
||||||
|
}
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -784,12 +780,16 @@ mod tests {
|
|||||||
"update_merchandise_list returned Ok result: {:#x?}",
|
"update_merchandise_list returned Ok result: {:#x?}",
|
||||||
raw_merchandise_vec
|
raw_merchandise_vec
|
||||||
),
|
),
|
||||||
FFIResult::Err(error) => {
|
FFIResult::Err(error) => match error {
|
||||||
assert_eq!(
|
FFIError::Server(server_error) => {
|
||||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
assert_eq!(server_error.status, 500);
|
||||||
"Server 500: Internal Server Error"
|
assert_eq!(
|
||||||
);
|
unsafe { CStr::from_ptr(server_error.title).to_string_lossy() },
|
||||||
}
|
"Internal Server Error"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => panic!("update_merchandise_list did not return a server error"),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
@ -857,9 +857,17 @@ mod tests {
|
|||||||
"VendorItemWeapon".to_string(),
|
"VendorItemWeapon".to_string(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
FFIResult::Err(error) => panic!("get_merchandise_list returned error: {:?}", unsafe {
|
FFIResult::Err(error) => panic!(
|
||||||
CStr::from_ptr(error).to_string_lossy()
|
"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?}",
|
"get_merchandise_list returned Ok result: {:#x?}",
|
||||||
raw_merchandise_vec
|
raw_merchandise_vec
|
||||||
),
|
),
|
||||||
FFIResult::Err(error) => {
|
FFIResult::Err(error) => match error {
|
||||||
assert_eq!(
|
FFIError::Network(network_error) => {
|
||||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
assert_eq!(
|
||||||
"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!(
|
FFIResult::Err(error) => panic!(
|
||||||
"get_merchandise_list_by_shop_id returned error: {:?}",
|
"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?}",
|
"get_merchandise_list_by_shop_id returned Ok result: {:#x?}",
|
||||||
raw_merchandise_vec
|
raw_merchandise_vec
|
||||||
),
|
),
|
||||||
FFIResult::Err(error) => {
|
FFIResult::Err(error) => match error {
|
||||||
assert_eq!(
|
FFIError::Network(network_error) => {
|
||||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
assert_eq!(
|
||||||
"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"),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
78
src/owner.rs
78
src/owner.rs
@ -11,8 +11,10 @@ use log::{error, info};
|
|||||||
use std::{println as info, println as error};
|
use std::{println as info, println as error};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
cache::file_cache_dir, cache::update_file_caches, error::extract_error_from_response,
|
cache::file_cache_dir,
|
||||||
result::FFIResult,
|
cache::update_file_caches,
|
||||||
|
error::extract_error_from_response,
|
||||||
|
result::{FFIError, FFIResult},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
@ -112,11 +114,7 @@ pub extern "C" fn create_owner(
|
|||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("create_owner failed. {}", err);
|
error!("create_owner failed. {}", err);
|
||||||
// TODO: also need to drop this CString once C++ is done reading it
|
FFIResult::Err(FFIError::from(err))
|
||||||
let err_string = CString::new(err.to_string())
|
|
||||||
.expect("could not create CString")
|
|
||||||
.into_raw();
|
|
||||||
FFIResult::Err(err_string)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,11 +180,7 @@ pub extern "C" fn update_owner(
|
|||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("update_owner failed. {}", err);
|
error!("update_owner failed. {}", err);
|
||||||
// TODO: also need to drop this CString once C++ is done reading it
|
FFIResult::Err(FFIError::from(err))
|
||||||
let err_string = CString::new(err.to_string())
|
|
||||||
.expect("could not create CString")
|
|
||||||
.into_raw();
|
|
||||||
FFIResult::Err(err_string)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -229,9 +223,17 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert_eq!(raw_owner.mod_version, 1);
|
assert_eq!(raw_owner.mod_version, 1);
|
||||||
}
|
}
|
||||||
FFIResult::Err(error) => panic!("create_owner returned error: {:?}", unsafe {
|
FFIResult::Err(error) => panic!(
|
||||||
CStr::from_ptr(error).to_string_lossy()
|
"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) => {
|
FFIResult::Ok(raw_owner) => {
|
||||||
panic!("create_owner returned Ok result: {:#x?}", raw_owner)
|
panic!("create_owner returned Ok result: {:#x?}", raw_owner)
|
||||||
}
|
}
|
||||||
FFIResult::Err(error) => {
|
FFIResult::Err(error) => match error {
|
||||||
assert_eq!(
|
FFIError::Server(server_error) => {
|
||||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
assert_eq!(server_error.status, 500);
|
||||||
"Server 500: Internal Server Error"
|
assert_eq!(
|
||||||
);
|
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);
|
assert_eq!(raw_owner.mod_version, 1);
|
||||||
}
|
}
|
||||||
FFIResult::Err(error) => panic!("update_owner returned error: {:?}", unsafe {
|
FFIResult::Err(error) => panic!(
|
||||||
CStr::from_ptr(error).to_string_lossy()
|
"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) => {
|
FFIResult::Ok(raw_owner) => {
|
||||||
panic!("update_owner returned Ok result: {:#x?}", raw_owner)
|
panic!("update_owner returned Ok result: {:#x?}", raw_owner)
|
||||||
}
|
}
|
||||||
FFIResult::Err(error) => {
|
FFIResult::Err(error) => match error {
|
||||||
assert_eq!(
|
FFIError::Server(server_error) => {
|
||||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
assert_eq!(server_error.status, 500);
|
||||||
"Server 500: Internal Server Error"
|
assert_eq!(
|
||||||
);
|
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::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)]
|
#[derive(Debug, PartialEq)]
|
||||||
#[repr(C, u8)]
|
#[repr(C, u8)]
|
||||||
pub enum FFIResult<T> {
|
pub enum FFIResult<T> {
|
||||||
Ok(T),
|
Ok(T),
|
||||||
Err(*const c_char),
|
Err(FFIError),
|
||||||
}
|
}
|
||||||
|
154
src/shop.rs
154
src/shop.rs
@ -11,9 +11,13 @@ use log::{error, info};
|
|||||||
use std::{println as info, println as error};
|
use std::{println as info, println as error};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
cache::file_cache_dir, cache::from_file_cache, cache::load_metadata_from_file_cache,
|
cache::file_cache_dir,
|
||||||
cache::update_file_caches, error::extract_error_from_response, log_server_error,
|
cache::from_file_cache,
|
||||||
result::FFIResult,
|
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)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
@ -155,11 +159,7 @@ pub extern "C" fn create_shop(
|
|||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("create_shop failed. {}", err);
|
error!("create_shop failed. {}", err);
|
||||||
// TODO: also need to drop this CString once C++ is done reading it
|
FFIResult::Err(FFIError::from(err))
|
||||||
let err_string = CString::new(err.to_string())
|
|
||||||
.expect("could not create CString")
|
|
||||||
.into_raw();
|
|
||||||
FFIResult::Err(err_string)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -272,11 +272,7 @@ pub extern "C" fn update_shop(
|
|||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("update_shop failed. {}", err);
|
error!("update_shop failed. {}", err);
|
||||||
// TODO: also need to drop this CString once C++ is done reading it
|
FFIResult::Err(FFIError::from(err))
|
||||||
let err_string = CString::new(err.to_string())
|
|
||||||
.expect("could not create CString")
|
|
||||||
.into_raw();
|
|
||||||
FFIResult::Err(err_string)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -346,12 +342,8 @@ pub extern "C" fn get_shop(
|
|||||||
FFIResult::Ok(RawShop::from(shop))
|
FFIResult::Ok(RawShop::from(shop))
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("get_shop_list failed. {}", err);
|
error!("get_shop failed. {}", err);
|
||||||
let err_string = CString::new(err.to_string())
|
FFIResult::Err(FFIError::from(err))
|
||||||
.expect("could not create CString")
|
|
||||||
.into_raw();
|
|
||||||
// TODO: also need to drop this CString once C++ is done reading it
|
|
||||||
FFIResult::Err(err_string)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -419,11 +411,7 @@ pub extern "C" fn list_shops(
|
|||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("list_shops failed. {}", err);
|
error!("list_shops failed. {}", err);
|
||||||
let err_string = CString::new(err.to_string())
|
FFIResult::Err(FFIError::from(err))
|
||||||
.expect("could not create CString")
|
|
||||||
.into_raw();
|
|
||||||
// TODO: also need to drop this CString once C++ is done reading it
|
|
||||||
FFIResult::Err(err_string)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -490,9 +478,17 @@ mod tests {
|
|||||||
);
|
);
|
||||||
assert_eq!(raw_shop.vendor_keywords_exclude, true);
|
assert_eq!(raw_shop.vendor_keywords_exclude, true);
|
||||||
}
|
}
|
||||||
FFIResult::Err(error) => panic!("create_shop returned error: {:?}", unsafe {
|
FFIResult::Err(error) => panic!(
|
||||||
CStr::from_ptr(error).to_string_lossy()
|
"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();
|
mock.assert();
|
||||||
match result {
|
match result {
|
||||||
FFIResult::Ok(raw_shop) => panic!("create_shop returned Ok result: {:#x?}", raw_shop),
|
FFIResult::Ok(raw_shop) => panic!("create_shop returned Ok result: {:#x?}", raw_shop),
|
||||||
FFIResult::Err(error) => {
|
FFIResult::Err(error) => match error {
|
||||||
assert_eq!(
|
FFIError::Server(server_error) => {
|
||||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
assert_eq!(server_error.status, 500);
|
||||||
"Server 500: Internal Server Error"
|
assert_eq!(
|
||||||
);
|
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);
|
assert_eq!(raw_shop.vendor_keywords_exclude, true);
|
||||||
}
|
}
|
||||||
FFIResult::Err(error) => panic!("update_shop returned error: {:?}", unsafe {
|
FFIResult::Err(error) => panic!(
|
||||||
CStr::from_ptr(error).to_string_lossy()
|
"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();
|
mock.assert();
|
||||||
match result {
|
match result {
|
||||||
FFIResult::Ok(raw_shop) => panic!("update_shop returned Ok result: {:#x?}", raw_shop),
|
FFIResult::Ok(raw_shop) => panic!("update_shop returned Ok result: {:#x?}", raw_shop),
|
||||||
FFIResult::Err(error) => {
|
FFIResult::Err(error) => match error {
|
||||||
assert_eq!(
|
FFIError::Server(server_error) => {
|
||||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
assert_eq!(server_error.status, 500);
|
||||||
"Server 500: Internal Server Error"
|
assert_eq!(
|
||||||
);
|
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);
|
assert_eq!(raw_shop.vendor_keywords_exclude, true);
|
||||||
}
|
}
|
||||||
FFIResult::Err(error) => panic!("get_shop returned error: {:?}", unsafe {
|
FFIResult::Err(error) => panic!(
|
||||||
CStr::from_ptr(error).to_string_lossy()
|
"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();
|
mock.assert();
|
||||||
match result {
|
match result {
|
||||||
FFIResult::Ok(raw_shop) => panic!("get_shop returned Ok result: {:#x?}", raw_shop),
|
FFIResult::Ok(raw_shop) => panic!("get_shop returned Ok result: {:#x?}", raw_shop),
|
||||||
FFIResult::Err(error) => {
|
FFIResult::Err(error) => match error {
|
||||||
assert_eq!(
|
FFIError::Network(network_error) => {
|
||||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
assert_eq!(
|
||||||
"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);
|
assert_eq!(raw_shop.vendor_keywords_exclude, true);
|
||||||
}
|
}
|
||||||
FFIResult::Err(error) => panic!("list_shops returned error: {:?}", unsafe {
|
FFIResult::Err(error) => panic!(
|
||||||
CStr::from_ptr(error).to_string_lossy()
|
"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();
|
mock.assert();
|
||||||
match result {
|
match result {
|
||||||
FFIResult::Ok(raw_shop) => panic!("list_shops returned Ok result: {:#x?}", raw_shop),
|
FFIResult::Ok(raw_shop) => panic!("list_shops returned Ok result: {:#x?}", raw_shop),
|
||||||
FFIResult::Err(error) => {
|
FFIResult::Err(error) => match error {
|
||||||
assert_eq!(
|
FFIError::Network(network_error) => {
|
||||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
assert_eq!(
|
||||||
"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 std::{println as info, println as error};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
cache::file_cache_dir, cache::update_file_caches, error::extract_error_from_response,
|
cache::file_cache_dir,
|
||||||
result::FFIResult,
|
cache::update_file_caches,
|
||||||
|
error::extract_error_from_response,
|
||||||
|
result::{FFIError, FFIResult},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
@ -193,11 +195,7 @@ pub extern "C" fn create_transaction(
|
|||||||
Ok(transaction) => FFIResult::Ok(RawTransaction::from(transaction)),
|
Ok(transaction) => FFIResult::Ok(RawTransaction::from(transaction)),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("create_transaction failed. {}", err);
|
error!("create_transaction failed. {}", err);
|
||||||
// TODO: also need to drop this CString once C++ is done reading it
|
FFIResult::Err(FFIError::from(err))
|
||||||
let err_string = CString::new(err.to_string())
|
|
||||||
.expect("could not create CString")
|
|
||||||
.into_raw();
|
|
||||||
FFIResult::Err(err_string)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -294,9 +292,17 @@ mod tests {
|
|||||||
vec!["VendorItemMisc".to_string()]
|
vec!["VendorItemMisc".to_string()]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
FFIResult::Err(error) => panic!("create_transaction returned error: {:?}", unsafe {
|
FFIResult::Err(error) => panic!(
|
||||||
CStr::from_ptr(error).to_string_lossy()
|
"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: {:#?}",
|
"create_transaction returned Ok result: {:#?}",
|
||||||
raw_transaction
|
raw_transaction
|
||||||
),
|
),
|
||||||
FFIResult::Err(error) => {
|
FFIResult::Err(error) => match error {
|
||||||
assert_eq!(
|
FFIError::Server(server_error) => {
|
||||||
unsafe { CStr::from_ptr(error).to_string_lossy() },
|
assert_eq!(server_error.status, 500);
|
||||||
"Server 500: Internal Server Error. Some error detail"
|
assert_eq!(
|
||||||
);
|
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