pub mod macros;
use std::borrow::{Borrow, Cow};
use std::collections::HashSet;
use std::hash::{Hash, Hasher};
use std::fmt::{self, Debug};
use std::sync::{Arc, Weak};
use std::pin::Pin;
use tokio::sync::{RwLock, RwLockMappedWriteGuard, RwLockReadGuard, RwLockWriteGuard};
use async_trait::async_trait;
use crate::backend::{DevBackend, DevCommandBackend, DevGetAttrInfoBackend, DevGetCommandInfoBackend, DevReadAttrBackend, DevWriteAttrBackend};
use crate::error::{Error, ErrorKind, Result, reason};
use crate::dev::{AttrWrite, DevProxy, DevState};
use crate::data::{Data, DataKind};
#[derive(Debug)]
pub struct DevCtx<Model> {
this: Weak<DevCtx<Model>>,
name: Arc<str>,
admin_dev_name: Arc<str>,
model: RwLock<Option<Model>>,
iface: RwLock<DefInterface<Model>>,
}
#[derive(Debug)]
struct DefInterface<Model> {
commands: HashSet<CommandDefSlot<Model>>,
}
impl<Model> DevCtx<Model> {
#[inline]
fn this(&self) -> Arc<Self> {
self.this.upgrade().unwrap()
}
pub async fn read_model(&self) -> RwLockReadGuard<'_, Model> {
RwLockReadGuard::map(self.model.read().await,
|model| model.as_ref().expect("The model is not yet readable as you are in the initializer"))
}
pub async fn write_model(&self) -> RwLockMappedWriteGuard<'_, Model> {
RwLockWriteGuard::map(self.model.write().await,
|model| model.as_mut().expect("The model is not yet writable as you are in the initializer"))
}
pub fn name(&self) -> &str {
&self.name
}
pub fn proxy(&self) -> DevProxy {
todo!()
}
pub fn add_command(&self, def: CommandDef<Model>) {
let mut iface = self.iface.blocking_write();
iface.commands.insert(CommandDefSlot(def));
}
}
#[async_trait]
impl<Model: Debug + Send + Sync> DevBackend for DevCtx<Model> {
async fn name(&self) -> Result<Arc<str>> {
Ok(self.name.clone())
}
async fn admin_dev_name(&self) -> Result<Arc<str>> {
Ok(self.admin_dev_name.clone())
}
async fn description(&self) -> Result<Arc<str>> {
todo!()
}
async fn ping(&self) -> Result<()> {
Ok(())
}
async fn state(&self) -> Result<DevState> {
todo!()
}
async fn status(&self) -> Result<String> {
todo!()
}
async fn black_box(&self, max: Option<usize>) -> Result<Vec<String>> {
todo!()
}
async fn command(&self, name: &str, data: Data<'_>) -> Result<Box<dyn DevCommandBackend>> {
let iface = self.iface.read().await;
let command = iface.commands.get(name)
.ok_or_else(|| Error::new(ErrorKind::CommandNotFound)
.with_append_description(name))?;
let mut res = Data::Void;
(command.0.exec)(self, name, data, &mut res).await?;
#[derive(Debug)]
#[repr(transparent)]
struct DevCommandFromData(Data<'static>);
impl DevCommandBackend for DevCommandFromData {
fn get(&self) -> Result<Data<'_>> {
Ok(self.0.as_view().into_owned())
}
}
Ok(Box::new(DevCommandFromData(res)))
}
async fn read_attr(&self, names: Option<&[&str]>) -> Result<Box<dyn DevReadAttrBackend>> {
todo!()
}
async fn write_attr(&self, attrs: &[AttrWrite<'_, Data<'_>>]) -> Result<Box<dyn DevWriteAttrBackend>> {
todo!()
}
async fn get_command_info(&self, names: Option<&[&str]>) -> Result<Box<dyn DevGetCommandInfoBackend>> {
todo!()
}
async fn get_attr_info(&self, names: Option<&[&str]>) -> Result<Box<dyn DevGetAttrInfoBackend>> {
todo!()
}
}
#[derive(Debug)]
#[repr(transparent)]
struct CommandDefSlot<Model>(CommandDef<Model>);
impl<Model> Borrow<str> for CommandDefSlot<Model> {
fn borrow(&self) -> &str {
&self.0.name
}
}
impl<Model> PartialEq for CommandDefSlot<Model> {
fn eq(&self, other: &Self) -> bool {
self.0.name == other.0.name
}
}
impl<Model> Eq for CommandDefSlot<Model> { }
impl<Model> Hash for CommandDefSlot<Model> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.name.hash(state);
}
}
pub struct DevDef<Model> {
class_name: Box<str>,
init: for<'a> fn(&'a DevCtx<Model>) -> Pin<Box<dyn Future<Output = Result<Model>> + 'a>>,
delete: for<'a> fn(&'a DevCtx<Model>) -> Pin<Box<dyn Future<Output = ()> + 'a>>,
}
pub struct CommandDef<Model> {
pub name: Cow<'static, str>,
pub in_description: Cow<'static, str>,
pub out_description: Cow<'static, str>,
pub in_kind: DataKind,
pub out_kind: DataKind,
pub exec: for<'a> fn(ctx: &'a DevCtx<Model>, name: &'a str, input: Data<'a>, output: &'a mut Data<'static>) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>,
}
impl<Model> Debug for CommandDef<Model> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CommandDef")
.field("name", &self.name)
.field("in_description", &self.in_description)
.field("out_description", &self.out_description)
.field("in_kind", &self.in_kind)
.field("out_kind", &self.out_kind)
.finish()
}
}
#[cfg(test)]
mod test {
use crate::dev::AttrWrite;
use super::*;
pub struct Foo {
}
impl Foo {
async fn init(ctx: &DevCtx<Self>) -> Result<Self> {
ctx.add_command(crate::command_def! {
name: "ExclusiveCommand",
in_description: "This command takes nothing as input",
out_description: "This command outputs nothing",
exec(mut): Foo::exclusive_command,
});
Ok(Foo {
})
}
async fn exclusive_command(&mut self, name: &str, data: ()) -> Result<&str> {
todo!()
}
async fn shared_command(&self, data: ()) -> Result<()> {
todo!()
}
async fn read_my_attribute(&self, ctx: &DevCtx<Self>) -> Result<u16> {
Ok(65)
}
async fn write_my_attribute(&self, value: AttrWrite<'_, u16>) -> Result<()> {
Ok(())
}
}
pub fn main() {
}
}