use std::{
boxed::Box,
cell::RefCell,
convert::TryInto,
error::Error,
ffi::CString,
fmt,
time::{SystemTime, UNIX_EPOCH}, };
use libc::c_int;
use kpal_plugin::{error_codes::*, *};
#[derive(Debug)]
#[repr(C)]
struct Basic {
attributes: Attributes<Self, BasicError>,
}
impl PluginAPI<BasicError> for Basic {
fn new() -> Result<Basic, BasicError> {
Ok(Basic {
attributes: RefCell::new(multimap! {
0, "x" => Attribute {
name: CString::new("x").unwrap(),
value: Value::Double(0.0),
callbacks_init: Callbacks::Update,
callbacks_run: Callbacks::GetAndSet(on_get_x, on_set_x),
},
1, "y" => Attribute {
name: CString::new("y").unwrap(),
value: Value::Int(0),
callbacks_init: Callbacks::Constant,
callbacks_run: Callbacks::Get(on_get_y),
},
2, "z" => Attribute {
name: CString::new("z").unwrap(),
value: Value::Int(42),
callbacks_init: Callbacks::Constant,
callbacks_run: Callbacks::Constant,
},
3, "msg" => Attribute {
name: CString::new("msg").unwrap(),
value: Value::String(CString::new("foobar").unwrap()),
callbacks_init: Callbacks::Constant,
callbacks_run: Callbacks::GetAndSet(on_get_msg, on_set_msg),
},
}),
})
}
fn init(&mut self) -> Result<(), BasicError> {
println!("Initializing the BasicPlugin... Done!");
Ok(())
}
fn attributes(&self) -> &Attributes<Basic, BasicError> {
&self.attributes
}
}
fn on_get_x(_plugin: &Basic, _cached: &Value) -> Result<Value, BasicError> {
println!("Getting the value of attribute x");
Ok(_cached.clone())
}
fn on_set_x(_plugin: &Basic, _cached: &Value, _val: &Val) -> Result<(), BasicError> {
println!("Setting the value of attribute x");
Ok(())
}
fn on_get_y(_plugin: &Basic, _cached: &Value) -> Result<Value, BasicError> {
println!("Getting the value of attribute y");
let rand_int: c_int = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.subsec_nanos()
.try_into()
.unwrap_or(42);
let value = Value::Int(rand_int);
Ok(value)
}
fn on_get_msg(_plugin: &Basic, _cached: &Value) -> Result<Value, BasicError> {
println!("Getting the value of attribute msg");
Ok(_cached.clone())
}
fn on_set_msg(_plugin: &Basic, _cached: &Value, _val: &Val) -> Result<(), BasicError> {
println!("Setting the value of attribute msg");
Ok(())
}
#[derive(Debug)]
struct BasicError {
error_code: c_int,
}
impl Error for BasicError {}
impl fmt::Display for BasicError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Basic error {{ error_code: {} }}", self.error_code)
}
}
impl PluginError for BasicError {
fn new(error_code: c_int) -> BasicError {
BasicError { error_code }
}
fn error_code(&self) -> c_int {
self.error_code
}
}
declare_plugin!(Basic, BasicError);
#[cfg(test)]
mod tests {
use libc::c_uchar;
use crate::RUN_PHASE;
use super::*;
#[test]
fn test_kpal_error() {
struct Case {
description: &'static str,
error_code: c_int,
want_null: bool,
};
let cases = vec![
Case {
description: "a valid error code is passed to kpal_error",
error_code: 0,
want_null: false,
},
Case {
description: "an invalid and negative error code is passed to kpal_error",
error_code: -1,
want_null: true,
},
Case {
description: "an invalid and positive error code is passed to kpal_error",
error_code: 99999,
want_null: true,
},
];
let mut msg: *const c_uchar;
for case in &cases {
log::info!("{}", case.description);
msg = error_message_ns(case.error_code);
if case.want_null {
assert!(msg.is_null());
} else {
assert!(!msg.is_null());
}
}
}
#[test]
fn set_attribute_value() {
let plugin = Basic::new().unwrap();
let new_val = Val::Double(3.14);
plugin.attribute_set_value(0, &new_val, RUN_PHASE).unwrap();
let attributes = plugin.attributes.borrow();
let actual = &attributes.get(&0).unwrap().value.as_val();
assert_eq!(
new_val, *actual,
"Expected attribute value to be {:?} but it was {:?}",
new_val, *actual
)
}
#[test]
fn set_attribute_wrong_variant() {
let plugin = Basic::new().unwrap();
let new_val = Val::Double(42.0);
let result = plugin.attribute_set_value(1, &new_val, RUN_PHASE);
match result {
Ok(_) => panic!("Expected different value variants."),
Err(_) => (),
}
}
}