Add new shop fields to create & update functions

This commit is contained in:
Tyler Hallada 2021-02-13 17:47:31 -05:00
parent 0e833ed273
commit e80fb6b9d0
2 changed files with 205 additions and 12 deletions

View File

@ -118,6 +118,11 @@ struct RawShop {
int32_t id; int32_t id;
const char *name; const char *name;
const char *description; const char *description;
int32_t gold;
const char *shop_type;
const char **vendor_keywords;
uintptr_t vendor_keywords_len;
bool vendor_keywords_exclude;
}; };
struct RawTransaction { struct RawTransaction {
@ -258,6 +263,11 @@ FFIResult<RawShop> update_shop(const char *api_url,
const char *api_key, const char *api_key,
uint32_t id, uint32_t id,
const char *name, const char *name,
const char *description); const char *description,
int32_t gold,
const char *shop_type,
const char **vendor_keywords,
uintptr_t vendor_keywords_len,
bool vendor_keywords_exclude);
} // extern "C" } // extern "C"

View File

@ -1,4 +1,4 @@
use std::{ffi::CStr, ffi::CString, os::raw::c_char}; use std::{ffi::CStr, ffi::CString, os::raw::c_char, slice};
use anyhow::Result; use anyhow::Result;
use chrono::NaiveDateTime; use chrono::NaiveDateTime;
@ -21,6 +21,10 @@ pub struct Shop {
pub name: String, pub name: String,
pub owner_id: Option<i32>, pub owner_id: Option<i32>,
pub description: Option<String>, pub description: Option<String>,
pub gold: Option<i32>,
pub shop_type: Option<String>,
pub vendor_keywords: Option<Vec<String>>,
pub vendor_keywords_exclude: Option<bool>,
} }
impl Shop { impl Shop {
@ -29,6 +33,10 @@ impl Shop {
name: name.to_string(), name: name.to_string(),
owner_id: None, owner_id: None,
description: Some(description.to_string()), description: Some(description.to_string()),
gold: None,
shop_type: None,
vendor_keywords: None,
vendor_keywords_exclude: None,
} }
} }
} }
@ -39,6 +47,10 @@ pub struct SavedShop {
pub name: String, pub name: String,
pub owner_id: i32, pub owner_id: i32,
pub description: Option<String>, pub description: Option<String>,
pub gold: i32,
pub shop_type: String,
pub vendor_keywords: Vec<String>,
pub vendor_keywords_exclude: bool,
pub created_at: NaiveDateTime, pub created_at: NaiveDateTime,
pub updated_at: NaiveDateTime, pub updated_at: NaiveDateTime,
} }
@ -49,16 +61,32 @@ pub struct RawShop {
pub id: i32, pub id: i32,
pub name: *const c_char, pub name: *const c_char,
pub description: *const c_char, pub description: *const c_char,
pub gold: i32,
pub shop_type: *const c_char,
pub vendor_keywords: *mut *const c_char,
pub vendor_keywords_len: usize,
pub vendor_keywords_exclude: bool,
} }
impl From<SavedShop> for RawShop { impl From<SavedShop> for RawShop {
fn from(shop: SavedShop) -> Self { fn from(shop: SavedShop) -> Self {
let (keywords_ptr, keywords_len, _) = shop
.vendor_keywords
.into_iter()
.map(|keyword| CString::new(keyword).unwrap_or_default().into_raw() as *const c_char)
.collect::<Vec<*const c_char>>()
.into_raw_parts();
Self { Self {
id: shop.id, id: shop.id,
name: CString::new(shop.name).unwrap_or_default().into_raw(), name: CString::new(shop.name).unwrap_or_default().into_raw(),
description: CString::new(shop.description.unwrap_or_else(|| "".to_string())) description: CString::new(shop.description.unwrap_or_else(|| "".to_string()))
.unwrap_or_default() .unwrap_or_default()
.into_raw(), .into_raw(),
gold: shop.gold,
shop_type: CString::new(shop.shop_type).unwrap_or_default().into_raw(),
vendor_keywords: keywords_ptr,
vendor_keywords_len: keywords_len,
vendor_keywords_exclude: shop.vendor_keywords_exclude,
} }
} }
} }
@ -143,30 +171,65 @@ pub extern "C" fn update_shop(
id: u32, id: u32,
name: *const c_char, name: *const c_char,
description: *const c_char, description: *const c_char,
gold: i32,
shop_type: *const c_char,
vendor_keywords: *mut *const c_char,
vendor_keywords_len: usize,
vendor_keywords_exclude: bool,
) -> FFIResult<RawShop> { ) -> FFIResult<RawShop> {
info!("update_shop begin"); info!("update_shop begin");
let api_url = unsafe { CStr::from_ptr(api_url) }.to_string_lossy(); let api_url = unsafe { CStr::from_ptr(api_url) }.to_string_lossy();
let api_key = unsafe { CStr::from_ptr(api_key) }.to_string_lossy(); let api_key = unsafe { CStr::from_ptr(api_key) }.to_string_lossy();
let name = unsafe { CStr::from_ptr(name) }.to_string_lossy(); let name = unsafe { CStr::from_ptr(name) }
let description = unsafe { CStr::from_ptr(description) }.to_string_lossy(); .to_string_lossy()
.to_string();
let description = unsafe { CStr::from_ptr(description) }
.to_string_lossy()
.to_string();
let shop_type = unsafe { CStr::from_ptr(shop_type) }
.to_string_lossy()
.to_string();
let keywords = match vendor_keywords.is_null() {
true => vec![],
false => unsafe { slice::from_raw_parts(vendor_keywords, vendor_keywords_len) }
.iter()
.map(|&keyword| {
unsafe { CStr::from_ptr(keyword) }
.to_string_lossy()
.to_string()
})
.collect(),
};
info!( info!(
"update_shop api_url: {:?}, api_key: {:?}, name: {:?}, description: {:?}", "update_shop api_url: {:?}, api_key: {:?}, name: {:?}, description: {:?}, gold: {:?}, shop_type: {:?}, keywords: {:?}, keywords_exclude: {:?}",
api_url, api_key, name, description api_url, api_key, name, description, gold, shop_type, keywords, vendor_keywords_exclude
); );
fn inner( fn inner(
api_url: &str, api_url: &str,
api_key: &str, api_key: &str,
id: u32, id: u32,
name: &str, name: String,
description: &str, description: String,
gold: i32,
shop_type: String,
vendor_keywords: Vec<String>,
vendor_keywords_exclude: bool,
) -> Result<SavedShop> { ) -> Result<SavedShop> {
#[cfg(not(test))] #[cfg(not(test))]
let url = Url::parse(api_url)?.join(&format!("v1/shops/{}", id))?; let url = Url::parse(api_url)?.join(&format!("v1/shops/{}", id))?;
#[cfg(test)] #[cfg(test)]
let url = Url::parse(&mockito::server_url())?.join(&format!("v1/shops/{}", id))?; let url = Url::parse(&mockito::server_url())?.join(&format!("v1/shops/{}", id))?;
let shop = Shop::from_game(name, description); let shop = Shop {
name,
owner_id: None,
description: Some(description),
gold: Some(gold),
shop_type: Some(shop_type),
vendor_keywords: Some(vendor_keywords),
vendor_keywords_exclude: Some(vendor_keywords_exclude),
};
info!("created shop from game: {:?}", &shop); info!("created shop from game: {:?}", &shop);
let client = reqwest::blocking::Client::new(); let client = reqwest::blocking::Client::new();
let resp = client let resp = client
@ -192,7 +255,17 @@ pub extern "C" fn update_shop(
} }
} }
match inner(&api_url, &api_key, id, &name, &description) { match inner(
&api_url,
&api_key,
id,
name,
description,
gold,
shop_type,
keywords,
vendor_keywords_exclude,
) {
Ok(shop) => { Ok(shop) => {
info!("update_shop successful"); info!("update_shop successful");
FFIResult::Ok(RawShop::from(shop)) FFIResult::Ok(RawShop::from(shop))
@ -370,6 +443,10 @@ mod tests {
owner_id: 1, owner_id: 1,
name: "name".to_string(), name: "name".to_string(),
description: Some("description".to_string()), description: Some("description".to_string()),
gold: 100,
shop_type: "general_store".to_string(),
vendor_keywords: vec!["VendorNoSale".to_string()],
vendor_keywords_exclude: true,
created_at: Utc::now().naive_utc(), created_at: Utc::now().naive_utc(),
updated_at: Utc::now().naive_utc(), updated_at: Utc::now().naive_utc(),
}; };
@ -396,6 +473,22 @@ mod tests {
unsafe { CStr::from_ptr(raw_shop.description).to_string_lossy() }, unsafe { CStr::from_ptr(raw_shop.description).to_string_lossy() },
"description" "description"
); );
assert_eq!(raw_shop.gold, 100);
assert_eq!(
unsafe { CStr::from_ptr(raw_shop.shop_type).to_string_lossy() },
"general_store"
);
assert!(!raw_shop.vendor_keywords.is_null());
let keywords_slice = unsafe {
slice::from_raw_parts(raw_shop.vendor_keywords, raw_shop.vendor_keywords_len)
};
assert_eq!(
unsafe { CStr::from_ptr(keywords_slice[0]) }
.to_string_lossy()
.to_string(),
"VendorNoSale".to_string(),
);
assert_eq!(raw_shop.vendor_keywords_exclude, true);
} }
FFIResult::Err(error) => panic!("create_shop returned error: {:?}", unsafe { FFIResult::Err(error) => panic!("create_shop returned error: {:?}", unsafe {
CStr::from_ptr(error).to_string_lossy() CStr::from_ptr(error).to_string_lossy()
@ -434,6 +527,10 @@ mod tests {
owner_id: 1, owner_id: 1,
name: "name".to_string(), name: "name".to_string(),
description: Some("description".to_string()), description: Some("description".to_string()),
gold: 100,
shop_type: "general_store".to_string(),
vendor_keywords: vec!["VendorNoSale".to_string()],
vendor_keywords_exclude: true,
created_at: Utc::now().naive_utc(), created_at: Utc::now().naive_utc(),
updated_at: Utc::now().naive_utc(), updated_at: Utc::now().naive_utc(),
}; };
@ -447,7 +544,22 @@ mod tests {
let api_key = CString::new("api-key").unwrap().into_raw(); let api_key = CString::new("api-key").unwrap().into_raw();
let name = CString::new("name").unwrap().into_raw(); let name = CString::new("name").unwrap().into_raw();
let description = CString::new("description").unwrap().into_raw(); let description = CString::new("description").unwrap().into_raw();
let result = update_shop(api_url, api_key, 1, name, description); let shop_type = CString::new("general_store").unwrap().into_raw();
let (keywords_ptr, keywords_len, _) =
vec![CString::new("VendorNoSale").unwrap().into_raw() as *const c_char]
.into_raw_parts();
let result = update_shop(
api_url,
api_key,
1,
name,
description,
100,
shop_type,
keywords_ptr,
keywords_len,
true,
);
mock.assert(); mock.assert();
match result { match result {
FFIResult::Ok(raw_shop) => { FFIResult::Ok(raw_shop) => {
@ -460,6 +572,22 @@ mod tests {
unsafe { CStr::from_ptr(raw_shop.description).to_string_lossy() }, unsafe { CStr::from_ptr(raw_shop.description).to_string_lossy() },
"description" "description"
); );
assert_eq!(raw_shop.gold, 100);
assert_eq!(
unsafe { CStr::from_ptr(raw_shop.shop_type).to_string_lossy() },
"general_store"
);
assert!(!raw_shop.vendor_keywords.is_null());
let keywords_slice = unsafe {
slice::from_raw_parts(raw_shop.vendor_keywords, raw_shop.vendor_keywords_len)
};
assert_eq!(
unsafe { CStr::from_ptr(keywords_slice[0]) }
.to_string_lossy()
.to_string(),
"VendorNoSale".to_string(),
);
assert_eq!(raw_shop.vendor_keywords_exclude, true);
} }
FFIResult::Err(error) => panic!("update_shop returned error: {:?}", unsafe { FFIResult::Err(error) => panic!("update_shop returned error: {:?}", unsafe {
CStr::from_ptr(error).to_string_lossy() CStr::from_ptr(error).to_string_lossy()
@ -478,7 +606,22 @@ mod tests {
let api_key = CString::new("api-key").unwrap().into_raw(); let api_key = CString::new("api-key").unwrap().into_raw();
let name = CString::new("name").unwrap().into_raw(); let name = CString::new("name").unwrap().into_raw();
let description = CString::new("description").unwrap().into_raw(); let description = CString::new("description").unwrap().into_raw();
let result = update_shop(api_url, api_key, 1, name, description); let shop_type = CString::new("general_store").unwrap().into_raw();
let (keywords_ptr, keywords_len, _) =
vec![CString::new("VendorNoSale").unwrap().into_raw() as *const c_char]
.into_raw_parts();
let result = update_shop(
api_url,
api_key,
1,
name,
description,
100,
shop_type,
keywords_ptr,
keywords_len,
true,
);
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),
@ -498,6 +641,10 @@ mod tests {
owner_id: 1, owner_id: 1,
name: "name".to_string(), name: "name".to_string(),
description: Some("description".to_string()), description: Some("description".to_string()),
gold: 100,
shop_type: "general_store".to_string(),
vendor_keywords: vec!["VendorNoSale".to_string()],
vendor_keywords_exclude: true,
created_at: Utc::now().naive_utc(), created_at: Utc::now().naive_utc(),
updated_at: Utc::now().naive_utc(), updated_at: Utc::now().naive_utc(),
}; };
@ -522,6 +669,22 @@ mod tests {
unsafe { CStr::from_ptr(raw_shop.description).to_string_lossy() }, unsafe { CStr::from_ptr(raw_shop.description).to_string_lossy() },
"description" "description"
); );
assert_eq!(raw_shop.gold, 100);
assert_eq!(
unsafe { CStr::from_ptr(raw_shop.shop_type).to_string_lossy() },
"general_store"
);
assert!(!raw_shop.vendor_keywords.is_null());
let keywords_slice = unsafe {
slice::from_raw_parts(raw_shop.vendor_keywords, raw_shop.vendor_keywords_len)
};
assert_eq!(
unsafe { CStr::from_ptr(keywords_slice[0]) }
.to_string_lossy()
.to_string(),
"VendorNoSale".to_string(),
);
assert_eq!(raw_shop.vendor_keywords_exclude, true);
} }
FFIResult::Err(error) => panic!("get_shop returned error: {:?}", unsafe { FFIResult::Err(error) => panic!("get_shop returned error: {:?}", unsafe {
CStr::from_ptr(error).to_string_lossy() CStr::from_ptr(error).to_string_lossy()
@ -558,6 +721,10 @@ mod tests {
owner_id: 1, owner_id: 1,
name: "name".to_string(), name: "name".to_string(),
description: Some("description".to_string()), description: Some("description".to_string()),
gold: 100,
shop_type: "general_store".to_string(),
vendor_keywords: vec!["VendorNoSale".to_string()],
vendor_keywords_exclude: true,
created_at: Utc::now().naive_utc(), created_at: Utc::now().naive_utc(),
updated_at: Utc::now().naive_utc(), updated_at: Utc::now().naive_utc(),
}]; }];
@ -587,6 +754,22 @@ mod tests {
unsafe { CStr::from_ptr(raw_shop.description).to_string_lossy() }, unsafe { CStr::from_ptr(raw_shop.description).to_string_lossy() },
"description" "description"
); );
assert_eq!(raw_shop.gold, 100);
assert_eq!(
unsafe { CStr::from_ptr(raw_shop.shop_type).to_string_lossy() },
"general_store"
);
assert!(!raw_shop.vendor_keywords.is_null());
let keywords_slice = unsafe {
slice::from_raw_parts(raw_shop.vendor_keywords, raw_shop.vendor_keywords_len)
};
assert_eq!(
unsafe { CStr::from_ptr(keywords_slice[0]) }
.to_string_lossy()
.to_string(),
"VendorNoSale".to_string(),
);
assert_eq!(raw_shop.vendor_keywords_exclude, true);
} }
FFIResult::Err(error) => panic!("list_shops returned error: {:?}", unsafe { FFIResult::Err(error) => panic!("list_shops returned error: {:?}", unsafe {
CStr::from_ptr(error).to_string_lossy() CStr::from_ptr(error).to_string_lossy()