use std::ops::{Deref, DerefMut};
use std::fmt::{self, Debug};
use std::borrow::Cow;
use std::{mem, vec};
use std::sync::Arc;
use crate::data::{Concat, Data, DataLayout, DataLayoutView, DataView};
use crate::dev::{AsNames, DevProxy, ProxyTarget};
use crate::error::{Error, ErrorKind, Result, reason};
use crate::prop::{FromProp, IntoProp};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct DatabaseProxy {
dev: DevProxy,
}
impl DatabaseProxy {
pub fn new_from_env() -> Self {
Self {
dev: DevProxy::new_with_target(ProxyTarget::DatabaseFromEnv),
}
}
pub fn new(host: &str, port: u16) -> Self {
Self {
dev: DevProxy::new_with_target(ProxyTarget::DatabaseAt {
host: Arc::from(host),
port,
}),
}
}
pub(crate) fn from_dev(dev: &DevProxy) -> &DatabaseProxy {
unsafe { &*(dev as *const _ as *const DatabaseProxy) }
}
pub(crate) fn with_dev(dev: DevProxy) -> Self {
Self {
dev,
}
}
#[doc(alias = "DbInfo")]
pub async fn info(&self) -> Result<String> {
self.command::<_, Concat<String>>("DbInfo", ()).await.map(|Concat(s)| s)
}
#[doc(alias = "DbAddDevice")]
pub async fn add_dev(&self, name: &str, class: &str, server: &str) -> Result<()> {
self.command("DbAddDevice", [server, name, class]).await
}
#[doc(alias = "DbDeleteDevice")]
pub async fn delete_dev(&self, name: &str) -> Result<()> {
self.command("DbDeleteDevice", name).await
}
#[doc(alias = "DbRenameServer")]
pub async fn rename_server(&self, name: &str, new_name: &str) -> Result<()> {
self.command("DbRenameServer", [name, new_name]).await
}
#[doc(alias = "DbGetDeviceWideList")]
pub async fn list_dev(&self, filter: &str) -> Result<Vec<String>> {
self.command("DbGetDeviceWideList", filter).await
}
#[doc(alias = "DbGetDeviceMemberList")]
pub async fn list_dev_member(&self, filter: &str) -> Result<Vec<String>> {
self.command("DbGetDeviceMemberList", filter).await
}
#[doc(alias = "DbGetDeviceFamilyList")]
pub async fn list_dev_family(&self, filter: &str) -> Result<Vec<String>> {
self.command("DbGetDeviceFamilyList", filter).await
}
#[doc(alias = "DbGetDeviceDomainList")]
pub async fn list_dev_domain(&self, filter: &str) -> Result<Vec<String>> {
self.command("DbGetDeviceDomainList", filter).await
}
#[doc(alias = "DbExportDevice")]
pub async fn export_dev(&self, info: ExportDevInfo<'_>) -> Result<()> {
self.command("DbExportDevice", data_from_export_dev_info(&[info][..])).await
}
#[doc(alias = "DbUnExportDevice")]
pub async fn unexport_dev(&self, dev_name: &str) -> Result<()> {
self.command("DbUnExportDevice", dev_name).await
}
#[doc(alias = "DbExportDevice")]
pub async fn export_server(&self, infos: &[ExportDevInfo<'_>]) -> Result<()> {
self.command("DbExportDevice", data_from_export_dev_info(infos)).await
}
#[doc(alias = "DbUnExportServer")]
pub async fn unexport_server(&self, server: &str) -> Result<()> {
self.command("DbUnExportServer", server).await
}
#[doc(alias = "DbGetDeviceExportedList")]
pub async fn list_exported_dev(&self, filter: &str) -> Result<Vec<String>> {
self.command("DbGetDeviceExportedList", filter).await
}
#[doc(alias = "DbImportDevice")]
pub async fn import_dev(&self, dev_name: &str) -> Result<ImportDevInfo> {
let output = self.command_raw("DbImportDevice", dev_name).await?;
let (
[exported, pid],
mut svalue,
) = match output.get::<([i32; 2], Vec<String>)>() {
Ok(ret) => ret,
Err(err) => {
if let ErrorKind::Reason(reason::DB_DeviceNotDefined) = err.kind() {
return Err(Error::new(ErrorKind::DevNotFound).with_parent(err));
} else {
return Err(err);
}
}
};
if svalue.len() != 5 && svalue.len() != 6 {
return Err(Error::new(ErrorKind::InvalidData)
.with_append_description("Data.1 should be a string spectrum of len 5 or 6")
.with_append_description(format_args!("{svalue:?}")));
}
Ok(ImportDevInfo {
name: mem::take(&mut svalue[0]),
itr: mem::take(&mut svalue[1]),
host: mem::take(&mut svalue[4]),
version: mem::take(&mut svalue[2]),
server: mem::take(&mut svalue[3]),
class: svalue.get_mut(5).map(mem::take),
exported: exported == 1,
pid,
})
}
#[doc(alias = "DbGetDeviceInfo")]
pub async fn get_dev_info(&self, dev_name: &str) -> Result<DevInfo> {
let output = self.command_raw("DbGetDeviceInfo", dev_name).await?;
let (
[exported, pid],
mut svalue,
) = match output.get::<([i32; 2], Vec<String>)>() {
Ok(ret) => ret,
Err(err) => {
if let ErrorKind::Reason(reason::DB_DeviceNotDefined) = err.kind() {
return Err(Error::new(ErrorKind::DevNotFound).with_parent(err));
} else {
return Err(err);
}
}
};
if svalue.len() != 7 && svalue.len() != 8 {
return Err(Error::new(ErrorKind::InvalidData)
.with_append_description("Data.1 should be a string spectrum of len 7 or 8")
.with_append_description(format_args!("{svalue:?}")));
}
Ok(DevInfo {
name: mem::take(&mut svalue[0]),
itr: mem::take(&mut svalue[1]),
host: Some(mem::take(&mut svalue[4])).filter(|s| s != "nada"),
version: mem::take(&mut svalue[2]),
server: mem::take(&mut svalue[3]),
class: svalue.get_mut(7).map(mem::take),
last_exported: Some(mem::take(&mut svalue[5])).filter(|s| s != "?"),
last_unexported: Some(mem::take(&mut svalue[6])).filter(|s| s != "?"),
exported: exported == 1,
pid: Some(pid).filter(|&pid| pid != 0),
})
}
#[doc(alias = "DbGetDeviceAlias")]
pub async fn get_dev_alias(&self, dev_name: &str) -> Result<Option<String>> {
match self.command::<_, String>("DbGetDeviceAlias", dev_name).await {
Ok(ret) => Ok(Some(ret)),
Err(err) => {
if let ErrorKind::Reason(reason::DB_AliasNotDefined) = err.kind() {
Ok(None)
} else {
Err(err)
}
}
}
}
#[doc(alias = "DbGetAliasDevice")]
pub async fn resolve_dev_alias(&self, alias: &str) -> Result<String> {
match self.command("DbGetAliasDevice", alias).await {
Ok(ret) => Ok(ret),
Err(err) => {
if let ErrorKind::Reason(reason::DB_AliasNotDefined) = err.kind() {
Err(Error::new(ErrorKind::AliasNotFound).with_parent(err))
} else {
Err(err)
}
}
}
}
#[doc(alias = "DbGetAliasDevice")]
pub async fn list_dev_aliases(&self, filter: &str) -> Result<Vec<String>> {
self.command("DbGetAliasDevice", filter).await
}
#[doc(alias = "DbGetAttributeAlias")]
pub async fn get_attr_alias(&self, attr_name: &str) -> Result<Option<String>> {
match self.command::<_, String>("DbGetAttributeAlias", attr_name).await {
Ok(ret) => Ok(Some(ret)),
Err(err) => {
if let ErrorKind::Reason(reason::DB_AliasNotDefined) = err.kind() {
Ok(None)
} else {
Err(err)
}
}
}
}
#[doc(alias = "DbGetAliasAttribute")]
pub async fn resolve_attr_alias(&self, alias: &str) -> Result<String> {
match self.command("DbGetAliasAttribute", alias).await {
Ok(ret) => Ok(ret),
Err(err) => {
if let ErrorKind::Reason(reason::DB_AliasNotDefined) = err.kind() {
Err(Error::new(ErrorKind::AliasNotFound).with_parent(err))
} else {
Err(err)
}
}
}
}
#[doc(alias = "DbGetAttributeAliasList")]
pub async fn list_attr_aliases(&self, filter: &str) -> Result<Vec<String>> {
self.command("DbGetAttributeAliasList", filter).await
}
async fn get_many_prop_inner(&self,
cmd_name: &str,
obj_name: &str,
names: &[&str],
) -> Result<(Box<[Cow<'static, str>]>, Box<[(usize, usize)]>)> {
let params = Some(obj_name).into_iter()
.chain(names.as_ref().into_iter().copied())
.collect::<Vec<_>>();
let output = self.command_raw(cmd_name, params).await?;
let data = output.get::<Data>()?;
let values = match data {
Data::String(DataLayout::Spectrum(strings)) =>
strings.map(|s| Cow::<str>::Owned(s.into_owned())),
Data::String(DataLayout::SpectrumView(strings)) =>
strings.map_to_owned(|s| Cow::Owned(s.to_string())),
_ => return Err(Error::new(ErrorKind::InvalidData)
.with_append_description("Data should be a spectrum of strings")
.with_append_description(format_args!("{data:?}"))),
}.into_boxed_slice();
let Some(output_dev_name) = values.get(0) else {
return Err(Error::new(ErrorKind::InvalidData)
.with_append_description("Missing device name")
.with_append_description(format_args!("{values:?}")));
};
if !output_dev_name.eq_ignore_ascii_case(obj_name) {
return Err(Error::new(ErrorKind::InvalidData)
.with_append_description(format_args!("Incoherent name get ({:?}) from what was requested ({:?})",
output_dev_name, obj_name)));
}
let Some(count_raw) = values.get(1) else {
return Err(Error::new(ErrorKind::InvalidData)
.with_append_description("Missing property count")
.with_append_description(format_args!("{values:?}")));
};
let Ok(count) = count_raw.parse::<usize>() else {
return Err(Error::new(ErrorKind::InvalidData)
.with_append_description(format_args!("Invalid property count: {count_raw:?}")));
};
if count != names.as_ref().len() {
return Err(Error::new(ErrorKind::InvalidData)
.with_append_description(format_args!("Incoherent property count ({count}) from what was requested ({})",
names.as_ref().len())));
}
let mut indices = Vec::with_capacity(count);
let mut values_idx = 2usize;
for prop_idx in 0..count {
if values_idx + 2 > values.len() {
return Err(Error::new(ErrorKind::InvalidData)
.with_append_description("Missing property name and its count")
.with_append_description(format_args!("{values:?}")));
}
let prop_name = &*values[values_idx + 0];
if !prop_name.eq_ignore_ascii_case(names[prop_idx]) {
return Err(Error::new(ErrorKind::InvalidData)
.with_append_description(format_args!("Incoherent property name ({:?}) from what was requested ({:?})",
prop_name, names[indices.len()])));
}
let Ok(count) = values[values_idx + 1].parse::<usize>() else {
return Err(Error::new(ErrorKind::InvalidData)
.with_append_description(format_args!("Invalid property values count ({count_raw:?})")));
};
values_idx += 2;
let begin = values_idx;
values_idx += count;
if values_idx > values.len() {
return Err(Error::new(ErrorKind::InvalidData)
.with_append_description("Missing property values")
.with_append_description(format_args!("{values:?}")));
}
indices.push((begin, values_idx));
}
Ok((values, indices.into_boxed_slice()))
}
#[doc(alias = "DbGetPropertyList")]
pub async fn list_free_prop(&self, obj_name: &str, filter: &str) -> Result<Vec<String>> {
self.command("DbGetPropertyList", [obj_name, filter]).await
}
#[doc(alias = "DbGetClassPropertyList")]
#[doc(alias = "DbGetClassPropertyListWildcard")]
pub async fn list_class_prop(&self, obj_name: &str, filter: &str) -> Result<Vec<String>> {
if filter == "*" {
self.command("DbGetClassPropertyList", [obj_name]).await
} else {
self.command("DbGetClassPropertyListWildcard", [obj_name, filter]).await
}
}
#[doc(alias = "DbGetDevicePropertyList")]
pub async fn list_dev_prop(&self, obj_name: &str, filter: &str) -> Result<Vec<String>> {
self.command("DbGetDevicePropertyList", [obj_name, filter]).await
}
async fn get_all_prop(&self, list_cmd_name: &str, with_wildcard: bool, get_cmd_name: &str, obj_name: &str) -> Result<GetManyProp> {
let output = if with_wildcard {
self.command_raw(list_cmd_name, [obj_name, "*"]).await?
} else {
self.command_raw(list_cmd_name, [obj_name]).await?
};
let data = output.get::<Data>()?;
let DataView::String(DataLayoutView::Spectrum(names)) = data.as_view() else {
return Err(Error::new(ErrorKind::InvalidData)
.with_append_description("Names should be a string spectrum")
.with_append_description(format_args!("{data:?}")));
};
let names = names.as_raw_slice()
.into_iter()
.map(|s| &**s)
.collect::<Vec<_>>();
if names.is_empty() {
return Ok(GetManyProp {
values: Box::new([]),
indices: Box::new([]),
});
}
let (
values,
indices
) = self.get_many_prop_inner(get_cmd_name, obj_name, &names).await?;
Ok(GetManyProp {
values,
indices,
})
}
pub async fn get_all_free_prop(&self, obj_name: &str) -> Result<GetManyProp> {
self.get_all_prop("DbGetPropertyList", true, "DbGetProperty", obj_name).await
}
pub async fn get_all_class_prop(&self, class_name: &str) -> Result<GetManyProp> {
self.get_all_prop("DbGetClassPropertyList", false, "DbGetClassProperty", class_name).await
}
pub async fn get_all_dev_prop(&self, dev_name: &str) -> Result<GetManyProp> {
self.get_all_prop("DbGetDevicePropertyList", true, "DbGetDeviceProperty", dev_name).await
}
async fn get_many_prop<Names: AsNames>(&self, cmd_name: &str, obj_name: &str, names: Names) -> Result<GetManyProp> {
let names = names.as_names();
let (
values,
indices
) = self.get_many_prop_inner(cmd_name, obj_name, names.as_ref()).await?;
Ok(GetManyProp {
values,
indices,
})
}
#[doc(alias = "DbGetProperty")]
pub async fn get_many_free_prop<Names: AsNames>(&self, obj_name: &str, names: Names) -> Result<GetManyProp> {
self.get_many_prop::<Names>("DbGetProperty", obj_name, names).await
}
#[doc(alias = "DbGetClassProperty")]
pub async fn get_many_class_prop<Names: AsNames>(&self, class_name: &str, names: Names) -> Result<GetManyProp> {
self.get_many_prop::<Names>("DbGetClassProperty", class_name, names).await
}
#[doc(alias = "DbGetDeviceProperty")]
pub async fn get_many_dev_prop<Names: AsNames>(&self, dev_name: &str, names: Names) -> Result<GetManyProp> {
self.get_many_prop::<Names>("DbGetDeviceProperty", dev_name, names).await
}
pub async fn get_free_prop<Spec, Names: AsNames>(&self, obj_name: &str, names: Names) -> Result<Spec::Output>
where
Spec: GetPropSpec<Names>,
{
self.get_many_free_prop(obj_name, names).await.and_then(|many| Spec::output(many, get_prop_spec::Token))
}
pub async fn get_class_prop<Spec, Names: AsNames>(&self, class_name: &str, names: Names) -> Result<Spec::Output>
where
Spec: GetPropSpec<Names>,
{
self.get_many_class_prop(class_name, names).await.and_then(|many| Spec::output(many, get_prop_spec::Token))
}
pub async fn get_dev_prop<Spec, Names: AsNames>(&self, dev_name: &str, names: Names) -> Result<Spec::Output>
where
Spec: GetPropSpec<Names>,
{
self.get_many_dev_prop(dev_name, names).await.and_then(|many| Spec::output(many, get_prop_spec::Token))
}
pub fn set_free_prop_builder<'data>(&self, obj_name: &'data str) -> SetPropBuilder<'_, 'data> {
assert!(!obj_name.is_empty(), "object name should not be empty");
SetPropBuilder::new(self, "DbPutProperty", Cow::Borrowed(obj_name))
}
pub fn set_class_prop_builder<'data>(&self, class_name: &'data str) -> SetPropBuilder<'_, 'data> {
assert!(!class_name.is_empty(), "class name should not be empty");
SetPropBuilder::new(self, "DbPutClassProperty", Cow::Borrowed(class_name))
}
pub fn set_dev_prop_builder<'data>(&self, dev_name: &'data str) -> SetPropBuilder<'_, 'data> {
assert!(!dev_name.is_empty(), "device name should not be empty");
SetPropBuilder::new(self, "DbPutDeviceProperty", Cow::Borrowed(dev_name))
}
#[doc(alias = "DbPutProperty")]
pub async fn set_free_prop<'data, Spec>(&self, obj_name: &'data str, props: Spec) -> Result<()>
where
Spec: SetPropSpec<'data>,
{
let mut builder = self.set_free_prop_builder(obj_name);
props.push_to_builder(&mut builder);
builder.set().await
}
#[doc(alias = "DbPutClassProperty")]
pub async fn set_class_prop<'data, Spec>(&self, class_name: &'data str, props: Spec) -> Result<()>
where
Spec: SetPropSpec<'data>,
{
let mut builder = self.set_class_prop_builder(class_name);
props.push_to_builder(&mut builder);
builder.set().await
}
#[doc(alias = "DbPutDeviceProperty")]
pub async fn set_dev_prop<'data, Spec>(&self, dev_name: &'data str, props: Spec) -> Result<()>
where
Spec: SetPropSpec<'data>,
{
let mut builder = self.set_dev_prop_builder(dev_name);
props.push_to_builder(&mut builder);
builder.set().await
}
async fn del_prop<Names: AsNames>(&self, cmd_name: &str, obj_name: &str, names: Names) -> Result<()> {
let names = names.as_names();
let params = Some(obj_name).iter()
.chain(names.as_ref())
.map(|&s| Cow::Borrowed(s))
.collect::<Vec<_>>();
self.command::<_, ()>(cmd_name, params).await
}
#[doc(alias = "DbDeleteProperty")]
pub async fn del_obj_prop<Names: AsNames>(&self, obj_name: &str, names: Names) -> Result<()> {
self.del_prop("DbDeleteProperty", obj_name, names).await
}
#[doc(alias = "DbDeleteClassProperty")]
pub async fn del_class_prop<Names: AsNames>(&self, class_name: &str, names: Names) -> Result<()> {
self.del_prop("DbDeleteClassProperty", class_name, names).await
}
#[doc(alias = "DbDeleteDeviceProperty")]
pub async fn del_dev_prop<Names: AsNames>(&self, dev_name: &str, names: Names) -> Result<()> {
self.del_prop("DbDeleteDeviceProperty", dev_name, names).await
}
}
impl Deref for DatabaseProxy {
type Target = DevProxy;
fn deref(&self) -> &Self::Target {
&self.dev
}
}
impl DerefMut for DatabaseProxy {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.dev
}
}
#[derive(Debug, Clone)]
pub struct ExportDevInfo<'a> {
pub name: Cow<'a, str>,
pub itr: Cow<'a, str>,
pub host: Cow<'a, str>,
pub version: Cow<'a, str>,
pub pid: i32,
}
fn data_from_export_dev_info<'a>(infos: &'a [ExportDevInfo<'a>]) -> Data<'a> {
let mut vec = Vec::with_capacity(infos.len() * 5);
for info in infos {
vec.push(Cow::Borrowed(&*info.name));
vec.push(Cow::Borrowed(&*info.itr));
vec.push(Cow::Borrowed(&*info.host));
vec.push(Cow::Owned(format!("{}", info.pid)));
vec.push(Cow::Borrowed(&*info.version));
}
Data::String(DataLayout::Spectrum(vec.into()))
}
#[derive(Debug, Clone)]
pub struct ImportDevInfo {
pub name: String,
pub itr: String,
pub host: String,
pub version: String,
pub server: String,
pub class: Option<String>,
pub exported: bool,
pub pid: i32,
}
#[derive(Debug, Clone)]
pub struct DevInfo {
pub name: String,
pub itr: String,
pub host: Option<String>,
pub version: String,
pub server: String,
pub class: Option<String>,
pub last_exported: Option<String>, pub last_unexported: Option<String>, pub exported: bool,
pub pid: Option<i32>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PropValue<'name, T> {
pub name: Cow<'name, str>,
pub value: T,
}
impl<'name, T> PropValue<'name, T> {
#[inline]
pub fn into_owned(self) -> PropValue<'static, T> {
PropValue {
name: Cow::Owned(self.name.into_owned()),
value: self.value,
}
}
}
pub struct GetManyProp {
values: Box<[Cow<'static, str>]>,
indices: Box<[(usize, usize)]>,
}
impl GetManyProp {
pub fn len(&self) -> usize {
self.indices.len()
}
pub fn get_raw(&self, index: usize) -> Option<PropValue<'_, &'_ [Cow<'_, str>]>> {
let (begin, end) = *self.indices.get(index)?;
let values = &self.values[begin..end];
let name = &*self.values[begin - 2];
Some(PropValue {
name: Cow::Borrowed(name),
value: values,
})
}
pub fn get<'d, T>(&'d self, index: usize) -> Result<PropValue<'d, T>>
where
T: FromProp<'d>,
{
let (begin, end) = *self.indices.get(index)
.ok_or_else(|| Error::new(ErrorKind::InvalidIndex))?;
let values = &self.values[begin..end];
let name = &*self.values[begin - 2];
T::from_prop(values)
.map(|value| PropValue { name: Cow::Borrowed(name), value })
.map_err(|err| Error::new(ErrorKind::InvalidData)
.with_append_description("Failed to parse property")
.with_append_description(err))
}
}
impl Debug for GetManyProp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "GetManyProp(")?;
f.debug_map().entries(self.indices.iter().map(|&(begin, end)| (&self.values[begin - 2], &self.values[begin..end]))).finish()?;
write!(f, ")")
}
}
pub(crate) mod get_prop_spec {
use crate::dev::AsNames;
use super::{Result, GetManyProp};
pub struct Token;
pub trait Internal<Names: AsNames + ?Sized> {
type Output;
fn output(array: GetManyProp, _: Token) -> Result<Self::Output>;
}
}
pub trait GetPropSpec<Names: AsNames>: get_prop_spec::Internal<Names> { }
impl<Names: AsNames, T: get_prop_spec::Internal<Names>> GetPropSpec<Names> for T { }
impl<T> get_prop_spec::Internal<&'_ str> for T
where
T: for<'d> FromProp<'d>,
{
type Output = PropValue<'static, T>;
#[inline]
fn output(array: GetManyProp, _: get_prop_spec::Token) -> Result<Self::Output> {
array.get(0).map(PropValue::into_owned)
}
}
impl<T> get_prop_spec::Internal<&'_ [&'_ str]> for T
where
T: for<'d> FromProp<'d>,
{
type Output = Vec<Result<PropValue<'static, T>>>;
#[inline]
fn output(array: GetManyProp, _: get_prop_spec::Token) -> Result<Self::Output> {
Ok((0..array.len()).map(|i| array.get::<T>(i).map(PropValue::into_owned)).collect())
}
}
impl<T, const N: usize> get_prop_spec::Internal<[&'_ str; N]> for T
where
T: for<'d> FromProp<'d>,
{
type Output = [Result<PropValue<'static, T>>; N];
#[inline]
fn output(array: GetManyProp, _: get_prop_spec::Token) -> Result<Self::Output> {
Ok(std::array::from_fn::<_, N, _>(|i| {
array.get(i).map(PropValue::into_owned)
}))
}
}
macro_rules! impl_get_prop_spec_tuple {
( @name: $comp:ident ) => { &'_ str };
( $( $comp:ident )* ) => {
#[allow(non_snake_case, unused_assignments)]
impl< $($comp,)* > get_prop_spec::Internal<( $( impl_get_prop_spec_tuple!(@name: $comp), )* )> for ( $($comp,)* )
where
$(
$comp: for<'d> FromProp<'d>,
)*
{
type Output = ( $( Result<PropValue<'static, $comp>>,)* );
fn output(array: GetManyProp, _: get_prop_spec::Token) -> Result<Self::Output> {
let mut i = 0;
$(
let $comp = array.get(i).map(PropValue::into_owned);
i += 1;
)*
Ok( ( $($comp,)* ) )
}
}
};
}
impl_get_prop_spec_tuple!(A);
impl_get_prop_spec_tuple!(A B);
impl_get_prop_spec_tuple!(A B C);
impl_get_prop_spec_tuple!(A B C D);
impl_get_prop_spec_tuple!(A B C D E);
impl_get_prop_spec_tuple!(A B C D E F);
pub struct SetPropBuilder<'dev, 'data> {
dev: &'dev DevProxy,
cmd: &'static str,
array: Vec<Cow<'data, str>>,
len: usize,
}
impl<'dev, 'data> SetPropBuilder<'dev, 'data> {
pub(crate) fn new(dev: &'dev DevProxy, cmd: &'static str, obj_name: Cow<'data, str>) -> Self {
Self {
dev,
cmd,
array: vec![obj_name, Cow::Owned(String::new())],
len: 0,
}
}
#[inline]
pub fn len(&self) -> usize {
self.len
}
pub fn push<T>(&mut self, prop: PropValue<'data, T>)
where
T: IntoProp<'data>,
{
self.array.push(prop.name);
self.array.push(Cow::Owned(String::new())); let len = self.array.len();
prop.value.into_prop(&mut self.array);
let count = self.array.len() - len;
self.array[len - 1] = Cow::Owned(format!("{count}")); }
pub async fn set(mut self) -> Result<()> {
if self.array[0].is_empty() {
let dev_name = self.dev.name().await?;
self.array[0] = Cow::Owned(dev_name);
}
self.array[1] = Cow::Owned(format!("{}", self.len));
self.dev.command::<_, ()>(self.cmd, self.array).await?;
Ok(())
}
}
mod prop_set_spec {
use super::SetPropBuilder;
pub trait Internal<'s>: Sized {
fn push_to_builder(self, builder: &mut SetPropBuilder<'_, 's>);
}
pub trait IntoIteratorFilter {}
impl<T, const N: usize> IntoIteratorFilter for [T; N] {}
impl<T> IntoIteratorFilter for Vec<T> {}
}
pub trait SetPropSpec<'data>: prop_set_spec::Internal<'data> { }
impl<'data, T: prop_set_spec::Internal<'data>> SetPropSpec<'data> for T { }
impl<'data, 'data_: 'data, T> prop_set_spec::Internal<'data> for PropValue<'data_, T>
where
T: IntoProp<'data>,
{
#[inline]
fn push_to_builder(self, builder: &mut SetPropBuilder<'_, 'data>) {
builder.push(self);
}
}
impl<'data, 'data_: 'data, T> prop_set_spec::Internal<'data> for (&'data_ str, T)
where
T: IntoProp<'data>,
{
#[inline]
fn push_to_builder(self, builder: &mut SetPropBuilder<'_, 'data>) {
builder.push(PropValue {
name: Cow::Borrowed(self.0),
value: self.1,
});
}
}
impl<'data, T, I> prop_set_spec::Internal<'data> for I
where
T: SetPropSpec<'data>,
I: IntoIterator<Item = T>,
I: prop_set_spec::IntoIteratorFilter,
{
#[inline]
fn push_to_builder(self, builder: &mut SetPropBuilder<'_, 'data>) {
for attr in self {
attr.push_to_builder(&mut *builder);
}
}
}
macro_rules! impl_set_prop_spec_tuple {
( @name: $comp:ident ) => { &'name str };
( $( $comp:ident )* ) => {
#[allow(non_snake_case, unused_assignments)]
impl<'data, $($comp,)*> prop_set_spec::Internal<'data> for ( $( $comp, )* )
where
$(
$comp: SetPropSpec<'data>,
)*
{
#[inline]
fn push_to_builder(self, builder: &mut SetPropBuilder<'_, 'data>) {
let ( $($comp,)* ) = self;
$(
$comp.push_to_builder(&mut *builder);
)*
}
}
};
}
impl_set_prop_spec_tuple!(A);
impl_set_prop_spec_tuple!(A B);
impl_set_prop_spec_tuple!(A B C);
impl_set_prop_spec_tuple!(A B C D);
impl_set_prop_spec_tuple!(A B C D E);
impl_set_prop_spec_tuple!(A B C D E F);