use std::ops::{Deref, DerefMut};
use std::future::Future;
use std::borrow::Cow;
use std::fmt::Debug;
use std::sync::Arc;
use crate::dev::{All, DevProxy, Named, NamedResult, ProxyTarget, ToNames, ToNamesNotAll, unexpected_len_err};
use crate::error::{Error, ErrorKind, Result, reason};
use crate::backend::{default_backend, Backend};
use crate::prop::{FromProp, IntoProp};
use crate::value::{Scalar, Spectrum};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[repr(transparent)]
pub struct DatabaseProxy {
dev: DevProxy,
}
impl DatabaseProxy {
pub fn new_from_env() -> Self {
Self::new_from_env_with_backend(default_backend())
}
pub fn new(host: &str, port: u16) -> Self {
Self::new_with_backend(default_backend(), host, port)
}
pub fn new_from_env_with_backend(backend: Arc<dyn Backend>) -> Self {
Self {
dev: DevProxy::new_from_target_with_backend(backend, ProxyTarget::DatabaseFromEnv),
}
}
pub fn new_with_backend(backend: Arc<dyn Backend>, host: &str, port: u16) -> Self {
Self {
dev: DevProxy::new_from_target_with_backend(backend, 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,
}
}
pub(crate) async fn host_and_port_async(&self) -> Result<(Arc<str>, u16)> {
let host_and_port = self.dev.database_host_and_port_async().await?;
Ok(host_and_port.expect("This should be defined if the target is a database"))
}
#[doc(alias = "DbInfo")]
pub async fn database_info_async(&self) -> Result<Spectrum<str>> {
self.command_async("DbInfo", ()).await
}
#[doc(alias = "DbInfo")]
pub fn database_info(&self) -> Result<Spectrum<str>> {
self.block_on(self.database_info_async())?
}
#[doc(alias = "DbAddDevice")]
pub async fn add_dev_async(&self, name: &str, class: &str, server: &str) -> Result<()> {
self.command_async("DbAddDevice", [server, name, class]).await
}
#[doc(alias = "DbAddDevice")]
pub fn add_dev(&self, name: &str, class: &str, server: &str) -> Result<()> {
self.block_on(self.add_dev_async(name, class, server))?
}
#[doc(alias = "DbDeleteDevice")]
pub async fn delete_dev_async(&self, name: &str) -> Result<()> {
self.command_async("DbDeleteDevice", name).await
}
#[doc(alias = "DbDeleteDevice")]
pub fn delete_dev(&self, name: &str) -> Result<()> {
self.block_on(self.delete_dev_async(name))?
}
#[doc(alias = "DbAddServer")]
pub async fn add_server_async(&self, server: &str, devs: &[(&str, &str)]) -> Result<()> {
let mut args = Vec::with_capacity(1 + devs.len() * 2);
args.push(server);
args.extend(devs.iter().flat_map(|&(name, class)| [name, class]));
self.command_async("DbAddServer", &args[..]).await
}
#[doc(alias = "DbAddServer")]
pub fn add_server(&self, server: &str, devs: &[(&str, &str)]) -> Result<()> {
self.block_on(self.add_server_async(server, devs))?
}
#[doc(alias = "DbDeleteServer")]
pub async fn delete_server_async(&self, server: &str) -> Result<()> {
self.command_async("DbDeleteServer", server).await
}
#[doc(alias = "DbDeleteServer")]
pub fn delete_server(&self, server: &str) -> Result<()> {
self.block_on(self.delete_server_async(server))?
}
#[doc(alias = "DbRenameServer")]
pub async fn rename_server_async(&self, name: &str, new_name: &str) -> Result<()> {
self.command_async("DbRenameServer", [name, new_name]).await
}
#[doc(alias = "DbRenameServer")]
pub fn rename_server(&self, name: &str, new_name: &str) -> Result<()> {
self.block_on(self.rename_server_async(name, new_name))?
}
#[doc(alias = "DbGetDeviceClassList")]
pub async fn list_server_dev_async(&self, server: &str) -> Result<Vec<Named<Scalar<str>>>> {
let flat: Spectrum<str> = self.command_async("DbGetDeviceClassList", server).await?;
let mut pairs = Vec::with_capacity(flat.len() / 2);
let mut index = 0;
while let Some(dev_name) = flat.get_scalar([index]) {
let Some(class_name) = flat.get_scalar([index + 1]) else {
return Err(Error::new(ErrorKind::InvalidData)
.with_description(format!("Database answered device {:?} of server {server:?} without its class", dev_name.value()))
.with_append_description("Expected: a string spectrum of device/class pairs"));
};
pairs.push(Named::new(dev_name, class_name));
index += 2;
}
Ok(pairs)
}
#[doc(alias = "DbGetDeviceClassList")]
pub fn list_server_dev(&self, server: &str) -> Result<Vec<Named<Scalar<str>>>> {
self.block_on(self.list_server_dev_async(server))?
}
#[doc(alias = "DbGetDeviceWideList")]
pub async fn list_dev_async(&self, filter: &str) -> Result<Spectrum<str>> {
self.command_async("DbGetDeviceWideList", filter).await
}
#[doc(alias = "DbGetDeviceWideList")]
pub fn list_dev(&self, filter: &str) -> Result<Spectrum<str>> {
self.block_on(self.list_dev_async(filter))?
}
#[doc(alias = "DbGetDeviceMemberList")]
pub async fn list_dev_member_async(&self, filter: &str) -> Result<Spectrum<str>> {
self.command_async("DbGetDeviceMemberList", filter).await
}
#[doc(alias = "DbGetDeviceMemberList")]
pub fn list_dev_member(&self, filter: &str) -> Result<Spectrum<str>> {
self.block_on(self.list_dev_member_async(filter))?
}
#[doc(alias = "DbGetDeviceFamilyList")]
pub async fn list_dev_family_async(&self, filter: &str) -> Result<Spectrum<str>> {
self.command_async("DbGetDeviceFamilyList", filter).await
}
#[doc(alias = "DbGetDeviceFamilyList")]
pub fn list_dev_family(&self, filter: &str) -> Result<Spectrum<str>> {
self.block_on(self.list_dev_family_async(filter))?
}
#[doc(alias = "DbGetDeviceDomainList")]
pub async fn list_dev_domain_async(&self, filter: &str) -> Result<Spectrum<str>> {
self.command_async("DbGetDeviceDomainList", filter).await
}
#[doc(alias = "DbGetDeviceDomainList")]
pub fn list_dev_domain(&self, filter: &str) -> Result<Spectrum<str>> {
self.block_on(self.list_dev_domain_async(filter))?
}
#[doc(alias = "DbExportDevice")]
pub async fn export_dev_async(&self, info: ExportDevInfo<'_>) -> Result<()> {
self.command_async("DbExportDevice", data_from_export_dev_info(&[info][..])).await
}
#[doc(alias = "DbExportDevice")]
pub fn export_dev(&self, info: ExportDevInfo<'_>) -> Result<()> {
self.block_on(self.export_dev_async(info))?
}
#[doc(alias = "DbUnExportDevice")]
pub async fn unexport_dev_async(&self, dev_name: &str) -> Result<()> {
self.command_async("DbUnExportDevice", dev_name).await
}
#[doc(alias = "DbUnExportDevice")]
pub fn unexport_dev(&self, dev_name: &str) -> Result<()> {
self.block_on(self.unexport_dev_async(dev_name))?
}
#[doc(alias = "DbExportServer")]
pub async fn export_server_async(&self, infos: &[ExportDevInfo<'_>]) -> Result<()> {
self.command_async("DbExportServer", data_from_export_dev_info(infos)).await
}
#[doc(alias = "DbExportServer")]
pub fn export_server(&self, infos: &[ExportDevInfo<'_>]) -> Result<()> {
self.block_on(self.export_server_async(infos))?
}
#[doc(alias = "DbUnExportServer")]
pub async fn unexport_server_async(&self, server: &str) -> Result<()> {
self.command_async("DbUnExportServer", server).await
}
#[doc(alias = "DbUnExportServer")]
pub fn unexport_server(&self, server: &str) -> Result<()> {
self.block_on(self.unexport_server_async(server))?
}
#[doc(alias = "DbGetDeviceExportedList")]
pub async fn list_exported_dev_async(&self, filter: &str) -> Result<Spectrum<str>> {
self.command_async("DbGetDeviceExportedList", filter).await
}
#[doc(alias = "DbGetDeviceExportedList")]
pub fn list_exported_dev(&self, filter: &str) -> Result<Spectrum<str>> {
self.block_on(self.list_exported_dev_async(filter))?
}
#[doc(alias = "DbImportDevice")]
pub async fn import_dev_async(&self, dev_name: &str) -> Result<ImportDevInfo> {
let (
[exported, pid],
svalue,
) = match self.command_async("DbImportDevice", dev_name).await.and_then(long2_string) {
Ok(ret) => ret,
Err(err) => {
if let ErrorKind::Reason(reason::DB_DeviceNotDefined) = err.kind() {
return Err(Error::new(ErrorKind::DevNotFound)
.with_description(format!("Device {dev_name:?} is not defined in the database"))
.with_parent(err));
} else {
return Err(err);
}
}
};
if svalue.len() != 5 && svalue.len() != 6 {
return Err(Error::new(ErrorKind::InvalidData)
.with_description(format!("Database answered {} strings for device {dev_name:?} where 5 or 6 were expected", svalue.len()))
.with_append_description(format_args!("Got: {svalue:?}")));
}
let field = |index| svalue.get_scalar([index]).unwrap();
Ok(ImportDevInfo {
name: field(0),
itr: field(1),
host: field(4),
version: field(2),
server: field(3),
class: svalue.get_scalar([5]),
exported: exported == 1,
pid,
})
}
#[doc(alias = "DbImportDevice")]
pub fn import_dev(&self, dev_name: &str) -> Result<ImportDevInfo> {
self.block_on(self.import_dev_async(dev_name))?
}
#[doc(alias = "DbGetDeviceInfo")]
pub async fn get_dev_info_async(&self, dev_name: &str) -> Result<DbDevInfo> {
let (
[exported, pid],
svalue,
) = match self.command_async("DbGetDeviceInfo", dev_name).await.and_then(long2_string) {
Ok(ret) => ret,
Err(err) => {
if let ErrorKind::Reason(reason::DB_DeviceNotDefined) = err.kind() {
return Err(Error::new(ErrorKind::DevNotFound)
.with_description(format!("Device {dev_name:?} is not defined in the database"))
.with_parent(err));
} else {
return Err(err);
}
}
};
if svalue.len() != 7 && svalue.len() != 8 {
return Err(Error::new(ErrorKind::InvalidData)
.with_description(format!("Database answered {} strings for device {dev_name:?} where 7 or 8 were expected", svalue.len()))
.with_append_description(format_args!("Got: {svalue:?}")));
}
let field = |index| svalue.get_scalar([index]).unwrap();
Ok(DbDevInfo {
name: field(0),
itr: field(1),
host: Some(field(4)).filter(|host| host.value() != "nada"),
version: field(2),
server: field(3),
class: svalue.get_scalar([7]),
last_exported: Some(field(5)).filter(|time| time.value() != "?"),
last_unexported: Some(field(6)).filter(|time| time.value() != "?"),
exported: exported == 1,
pid: Some(pid).filter(|&pid| pid != 0),
})
}
#[doc(alias = "DbGetDeviceInfo")]
pub fn get_dev_info(&self, dev_name: &str) -> Result<DbDevInfo> {
self.block_on(self.get_dev_info_async(dev_name))?
}
#[doc(alias = "DbGetDeviceAlias")]
pub async fn get_dev_alias_async(&self, dev_name: &str) -> Result<Option<Scalar<str>>> {
match self.command_async::<_, Scalar<str>>("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 = "DbGetDeviceAlias")]
pub fn get_dev_alias(&self, dev_name: &str) -> Result<Option<Scalar<str>>> {
self.block_on(self.get_dev_alias_async(dev_name))?
}
#[doc(alias = "DbGetAliasDevice")]
pub async fn resolve_dev_alias_async(&self, alias: &str) -> Result<Scalar<str>> {
match self.command_async("DbGetAliasDevice", alias).await {
Ok(ret) => Ok(ret),
Err(err) => {
if let ErrorKind::Reason(reason::DB_AliasNotDefined) = err.kind() {
Err(Error::new(ErrorKind::AliasNotFound)
.with_description(format!("Device alias {alias:?} is not defined in the database"))
.with_parent(err))
} else {
Err(err)
}
}
}
}
#[doc(alias = "DbGetAliasDevice")]
pub fn resolve_dev_alias(&self, alias: &str) -> Result<Scalar<str>> {
self.block_on(self.resolve_dev_alias_async(alias))?
}
#[doc(alias = "DbGetDeviceAliasList")]
pub async fn list_dev_aliases_async(&self, filter: &str) -> Result<Spectrum<str>> {
self.command_async("DbGetDeviceAliasList", filter).await
}
#[doc(alias = "DbGetDeviceAliasList")]
pub fn list_dev_aliases(&self, filter: &str) -> Result<Spectrum<str>> {
self.block_on(self.list_dev_aliases_async(filter))?
}
#[doc(alias = "DbGetAttributeAlias")]
pub async fn get_attr_alias_async(&self, attr_name: &str) -> Result<Option<Scalar<str>>> {
match self.command_async::<_, Scalar<str>>("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 = "DbGetAttributeAlias")]
pub fn get_attr_alias(&self, attr_name: &str) -> Result<Option<Scalar<str>>> {
self.block_on(self.get_attr_alias_async(attr_name))?
}
#[doc(alias = "DbGetAliasAttribute")]
pub async fn resolve_attr_alias_async(&self, alias: &str) -> Result<Scalar<str>> {
match self.command_async("DbGetAliasAttribute", alias).await {
Ok(ret) => Ok(ret),
Err(err) => {
if let ErrorKind::Reason(reason::DB_AliasNotDefined) = err.kind() {
Err(Error::new(ErrorKind::AliasNotFound)
.with_description(format!("Attribute alias {alias:?} is not defined in the database"))
.with_parent(err))
} else {
Err(err)
}
}
}
}
#[doc(alias = "DbGetAliasAttribute")]
pub fn resolve_attr_alias(&self, alias: &str) -> Result<Scalar<str>> {
self.block_on(self.resolve_attr_alias_async(alias))?
}
#[doc(alias = "DbGetAttributeAliasList")]
pub async fn list_attr_aliases_async(&self, filter: &str) -> Result<Spectrum<str>> {
self.command_async("DbGetAttributeAliasList", filter).await
}
#[doc(alias = "DbGetAttributeAliasList")]
pub fn list_attr_aliases(&self, filter: &str) -> Result<Spectrum<str>> {
self.block_on(self.list_attr_aliases_async(filter))?
}
#[doc(alias = "DbGetPropertyList")]
pub async fn list_free_prop_async(&self, obj_name: &str, filter: &str) -> Result<Spectrum<str>> {
self.command_async("DbGetPropertyList", [obj_name, filter]).await
}
#[doc(alias = "DbGetPropertyList")]
pub fn list_free_prop(&self, obj_name: &str, filter: &str) -> Result<Spectrum<str>> {
self.block_on(self.list_free_prop_async(obj_name, filter))?
}
#[doc(alias = "DbGetClassPropertyList")]
#[doc(alias = "DbGetClassPropertyListWildcard")]
pub async fn list_class_prop_async(&self, obj_name: &str, filter: &str) -> Result<Spectrum<str>> {
if filter == "*" {
self.command_async("DbGetClassPropertyList", [obj_name]).await
} else {
self.command_async("DbGetClassPropertyListWildcard", [obj_name, filter]).await
}
}
#[doc(alias = "DbGetClassPropertyList")]
#[doc(alias = "DbGetClassPropertyListWildcard")]
pub fn list_class_prop(&self, obj_name: &str, filter: &str) -> Result<Spectrum<str>> {
self.block_on(self.list_class_prop_async(obj_name, filter))?
}
#[doc(alias = "DbGetDevicePropertyList")]
pub async fn list_dev_prop_async(&self, obj_name: &str, filter: &str) -> Result<Spectrum<str>> {
self.command_async("DbGetDevicePropertyList", [obj_name, filter]).await
}
#[doc(alias = "DbGetDevicePropertyList")]
pub fn list_dev_prop(&self, obj_name: &str, filter: &str) -> Result<Spectrum<str>> {
self.block_on(self.list_dev_prop_async(obj_name, filter))?
}
pub(crate) async fn get_raw_prop_async(&self,
commands: PropCommands,
obj_name: &str,
params: Option<Spectrum<str>>,
) -> Result<get_prop::PropReply> {
let params = match params {
Some(params) => params,
None => {
let list_params = [obj_name, "*"];
let list_params = if commands.list_wildcard { &list_params[..] } else { &list_params[0..1] };
let names: Spectrum<str> = self.command_async(commands.list, list_params).await?;
prop_params(obj_name, names.iter())
}
};
let names = params.slice(1..);
if names.is_empty() {
return Ok(get_prop::PropReply::empty());
}
let listing: Spectrum<str> = self.command_async(commands.get, params).await?;
get_prop::PropReply::new(obj_name, listing, names)
}
fn get_any_prop_async<Spec, Names: ToNames>(&self,
commands: PropCommands,
obj_name: &str,
names: Names,
) -> impl Future<Output = Result<Spec::Output>> + Send
where
Spec: GetPropSpec<Names>,
{
let params = names.iter_names().map(|names| prop_params(obj_name, names));
async move {
let mut reply = self.get_raw_prop_async(commands, obj_name, params).await?;
Spec::collect(&mut reply)
}
}
#[doc(alias = "DbGetProperty")]
pub fn get_free_prop_async<Spec, Names: ToNames>(&self, obj_name: &str, names: Names) -> impl Future<Output = Result<Spec::Output>> + Send
where
Spec: GetPropSpec<Names>,
{
self.get_any_prop_async::<Spec, Names>(PropCommands::FREE, obj_name, names)
}
#[doc(alias = "DbGetProperty")]
pub fn get_free_prop<Spec, Names: ToNames>(&self, obj_name: &str, names: Names) -> Result<Spec::Output>
where
Spec: GetPropSpec<Names>,
{
self.block_on(self.get_free_prop_async::<Spec, Names>(obj_name, names))?
}
#[doc(alias = "DbGetClassProperty")]
pub fn get_class_prop_async<Spec, Names: ToNames>(&self, class_name: &str, names: Names) -> impl Future<Output = Result<Spec::Output>> + Send
where
Spec: GetPropSpec<Names>,
{
self.get_any_prop_async::<Spec, Names>(PropCommands::CLASS, class_name, names)
}
#[doc(alias = "DbGetClassProperty")]
pub fn get_class_prop<Spec, Names: ToNames>(&self, class_name: &str, names: Names) -> Result<Spec::Output>
where
Spec: GetPropSpec<Names>,
{
self.block_on(self.get_class_prop_async::<Spec, Names>(class_name, names))?
}
#[doc(alias = "DbGetDeviceProperty")]
pub fn get_dev_prop_async<Spec, Names: ToNames>(&self, dev_name: &str, names: Names) -> impl Future<Output = Result<Spec::Output>> + Send
where
Spec: GetPropSpec<Names>,
{
self.get_any_prop_async::<Spec, Names>(PropCommands::DEV, dev_name, names)
}
#[doc(alias = "DbGetDeviceProperty")]
pub fn get_dev_prop<Spec, Names: ToNames>(&self, dev_name: &str, names: Names) -> Result<Spec::Output>
where
Spec: GetPropSpec<Names>,
{
self.block_on(self.get_dev_prop_async::<Spec, Names>(dev_name, names))?
}
pub(crate) async fn prop_command_async(&self, cmd_name: &'static str, params: Spectrum<str>) -> Result<()> {
self.command_async(cmd_name, params).await
}
#[doc(alias = "DbPutProperty")]
pub fn set_free_prop_async<'a, Spec>(&self, obj_name: &'a str, spec: Spec) -> impl Future<Output = Result<()>> + Send
where
Spec: SetPropSpec<'a>,
{
self.prop_command_async("DbPutProperty", prop_params(obj_name, set_prop_params(spec).iter().map(|param| &**param)))
}
#[doc(alias = "DbPutProperty")]
pub fn set_free_prop<'a, Spec>(&self, obj_name: &'a str, spec: Spec) -> Result<()>
where
Spec: SetPropSpec<'a>,
{
self.block_on(self.set_free_prop_async(obj_name, spec))?
}
#[doc(alias = "DbPutClassProperty")]
pub fn set_class_prop_async<'a, Spec>(&self, class_name: &'a str, spec: Spec) -> impl Future<Output = Result<()>> + Send
where
Spec: SetPropSpec<'a>,
{
self.prop_command_async("DbPutClassProperty", prop_params(class_name, set_prop_params(spec).iter().map(|param| &**param)))
}
#[doc(alias = "DbPutClassProperty")]
pub fn set_class_prop<'a, Spec>(&self, class_name: &'a str, spec: Spec) -> Result<()>
where
Spec: SetPropSpec<'a>,
{
self.block_on(self.set_class_prop_async(class_name, spec))?
}
#[doc(alias = "DbPutDeviceProperty")]
pub fn set_dev_prop_async<'a, Spec>(&self, dev_name: &'a str, spec: Spec) -> impl Future<Output = Result<()>> + Send
where
Spec: SetPropSpec<'a>,
{
self.prop_command_async("DbPutDeviceProperty", prop_params(dev_name, set_prop_params(spec).iter().map(|param| &**param)))
}
#[doc(alias = "DbPutDeviceProperty")]
pub fn set_dev_prop<'a, Spec>(&self, dev_name: &'a str, spec: Spec) -> Result<()>
where
Spec: SetPropSpec<'a>,
{
self.block_on(self.set_dev_prop_async(dev_name, spec))?
}
#[doc(alias = "DbDeleteProperty")]
pub fn delete_free_prop_async<Names: ToNamesNotAll>(&self, obj_name: &str, names: Names) -> impl Future<Output = Result<()>> + Send {
self.prop_command_async("DbDeleteProperty", prop_params(obj_name, names.iter_names_not_all()))
}
#[doc(alias = "DbDeleteProperty")]
pub fn delete_free_prop<Names: ToNamesNotAll>(&self, obj_name: &str, names: Names) -> Result<()> {
self.block_on(self.delete_free_prop_async(obj_name, names))?
}
#[doc(alias = "DbDeleteClassProperty")]
pub fn delete_class_prop_async<Names: ToNamesNotAll>(&self, class_name: &str, names: Names) -> impl Future<Output = Result<()>> + Send {
self.prop_command_async("DbDeleteClassProperty", prop_params(class_name, names.iter_names_not_all()))
}
#[doc(alias = "DbDeleteClassProperty")]
pub fn delete_class_prop<Names: ToNamesNotAll>(&self, class_name: &str, names: Names) -> Result<()> {
self.block_on(self.delete_class_prop_async(class_name, names))?
}
#[doc(alias = "DbDeleteDeviceProperty")]
pub fn delete_dev_prop_async<Names: ToNamesNotAll>(&self, dev_name: &str, names: Names) -> impl Future<Output = Result<()>> + Send {
self.prop_command_async("DbDeleteDeviceProperty", prop_params(dev_name, names.iter_names_not_all()))
}
#[doc(alias = "DbDeleteDeviceProperty")]
pub fn delete_dev_prop<Names: ToNamesNotAll>(&self, dev_name: &str, names: Names) -> Result<()> {
self.block_on(self.delete_dev_prop_async(dev_name, names))?
}
pub(crate) async fn get_raw_attr_prop_async(&self,
commands: AttrPropCommands,
obj_name: &str,
params: Option<Spectrum<str>>,
prop_names: Option<Spectrum<str>>,
) -> Result<get_prop::AttrPropReply> {
let params = match params {
Some(params) => params,
None => {
let attr_names: Spectrum<str> = self.command_async(commands.list, [obj_name, "*"]).await?;
prop_params(obj_name, attr_names.iter())
}
};
let attr_names = params.slice(1..);
if attr_names.is_empty() {
return Ok(get_prop::AttrPropReply::empty());
}
let listing: Spectrum<str> = self.command_async(commands.get, params).await?;
get_prop::AttrPropReply::new(obj_name, listing, attr_names, prop_names)
}
fn get_any_attr_prop_async<Spec, AttrNames, Names>(&self,
commands: AttrPropCommands,
obj_name: &str,
attr_names: AttrNames,
prop_names: Names,
) -> impl Future<Output = Result<Spec::Output>> + Send
where
Spec: GetAttrPropSpec<AttrNames, Names>,
AttrNames: ToNames,
Names: ToNames,
{
let params = attr_names.iter_names().map(|attr_names| prop_params(obj_name, attr_names));
let prop_names = prop_names.to_names();
async move {
let mut reply = self.get_raw_attr_prop_async(commands, obj_name, params, prop_names).await?;
Spec::collect(&mut reply)
}
}
#[doc(alias = "DbGetClassAttributeProperty2")]
pub fn get_class_attr_prop_async<Spec, AttrNames, Names>(&self, class_name: &str, attr_names: AttrNames, prop_names: Names) -> impl Future<Output = Result<Spec::Output>> + Send
where
Spec: GetAttrPropSpec<AttrNames, Names>,
AttrNames: ToNames,
Names: ToNames,
{
self.get_any_attr_prop_async::<Spec, AttrNames, Names>(AttrPropCommands::CLASS, class_name, attr_names, prop_names)
}
#[doc(alias = "DbGetClassAttributeProperty2")]
pub fn get_class_attr_prop<Spec, AttrNames, Names>(&self, class_name: &str, attr_names: AttrNames, prop_names: Names) -> Result<Spec::Output>
where
Spec: GetAttrPropSpec<AttrNames, Names>,
AttrNames: ToNames,
Names: ToNames,
{
self.block_on(self.get_class_attr_prop_async::<Spec, AttrNames, Names>(class_name, attr_names, prop_names))?
}
#[doc(alias = "DbGetDeviceAttributeProperty2")]
pub fn get_dev_attr_prop_async<Spec, AttrNames, Names>(&self, dev_name: &str, attr_names: AttrNames, prop_names: Names) -> impl Future<Output = Result<Spec::Output>> + Send
where
Spec: GetAttrPropSpec<AttrNames, Names>,
AttrNames: ToNames,
Names: ToNames,
{
self.get_any_attr_prop_async::<Spec, AttrNames, Names>(AttrPropCommands::DEV, dev_name, attr_names, prop_names)
}
#[doc(alias = "DbGetDeviceAttributeProperty2")]
pub fn get_dev_attr_prop<Spec, AttrNames, Names>(&self, dev_name: &str, attr_names: AttrNames, prop_names: Names) -> Result<Spec::Output>
where
Spec: GetAttrPropSpec<AttrNames, Names>,
AttrNames: ToNames,
Names: ToNames,
{
self.block_on(self.get_dev_attr_prop_async::<Spec, AttrNames, Names>(dev_name, attr_names, prop_names))?
}
#[doc(alias = "DbPutClassAttributeProperty2")]
pub fn set_class_attr_prop_async<'a, Spec>(&self, class_name: &'a str, spec: Spec) -> impl Future<Output = Result<()>> + Send
where
Spec: SetAttrPropSpec<'a>,
{
self.prop_command_async("DbPutClassAttributeProperty2", attr_prop_params(class_name, spec))
}
#[doc(alias = "DbPutClassAttributeProperty2")]
pub fn set_class_attr_prop<'a, Spec>(&self, class_name: &'a str, spec: Spec) -> Result<()>
where
Spec: SetAttrPropSpec<'a>,
{
self.block_on(self.set_class_attr_prop_async(class_name, spec))?
}
#[doc(alias = "DbPutDeviceAttributeProperty2")]
pub fn set_dev_attr_prop_async<'a, Spec>(&self, dev_name: &'a str, spec: Spec) -> impl Future<Output = Result<()>> + Send
where
Spec: SetAttrPropSpec<'a>,
{
self.prop_command_async("DbPutDeviceAttributeProperty2", attr_prop_params(dev_name, spec))
}
#[doc(alias = "DbPutDeviceAttributeProperty2")]
pub fn set_dev_attr_prop<'a, Spec>(&self, dev_name: &'a str, spec: Spec) -> Result<()>
where
Spec: SetAttrPropSpec<'a>,
{
self.block_on(self.set_dev_attr_prop_async(dev_name, spec))?
}
#[doc(alias = "DbDeleteClassAttributeProperty")]
pub fn delete_class_attr_prop_async<Names: ToNamesNotAll>(&self, class_name: &str, attr_name: &str, names: Names) -> impl Future<Output = Result<()>> + Send {
let params = prop_params(class_name, Some(attr_name).into_iter().chain(names.iter_names_not_all()));
self.prop_command_async("DbDeleteClassAttributeProperty", params)
}
#[doc(alias = "DbDeleteClassAttributeProperty")]
pub fn delete_class_attr_prop<Names: ToNamesNotAll>(&self, class_name: &str, attr_name: &str, names: Names) -> Result<()> {
self.block_on(self.delete_class_attr_prop_async(class_name, attr_name, names))?
}
#[doc(alias = "DbDeleteDeviceAttributeProperty")]
pub fn delete_dev_attr_prop_async<Names: ToNamesNotAll>(&self, dev_name: &str, attr_name: &str, names: Names) -> impl Future<Output = Result<()>> + Send {
let params = prop_params(dev_name, Some(attr_name).into_iter().chain(names.iter_names_not_all()));
self.prop_command_async("DbDeleteDeviceAttributeProperty", params)
}
#[doc(alias = "DbDeleteDeviceAttributeProperty")]
pub fn delete_dev_attr_prop<Names: ToNamesNotAll>(&self, dev_name: &str, attr_name: &str, names: Names) -> Result<()> {
self.block_on(self.delete_dev_attr_prop_async(dev_name, attr_name, names))?
}
}
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(infos: &[ExportDevInfo<'_>]) -> Spectrum<str> {
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));
}
Spectrum::from(&vec[..])
}
fn long2_string((longs, strings): (Spectrum<i32>, Spectrum<str>)) -> Result<([i32; 2], Spectrum<str>)> {
let longs = <[i32; 2]>::try_from(longs.as_slice())
.map_err(|_| Error::new(ErrorKind::InvalidData)
.with_description(format!("Database answered {} longs where 2 were expected", longs.len()))
.with_append_description(format_args!("Got: {longs:?}")))?;
Ok((longs, strings))
}
#[derive(Debug, Clone)]
pub struct ImportDevInfo {
pub name: Scalar<str>,
pub itr: Scalar<str>,
pub host: Scalar<str>,
pub version: Scalar<str>,
pub server: Scalar<str>,
pub class: Option<Scalar<str>>,
pub exported: bool,
pub pid: i32,
}
#[derive(Debug, Clone)]
pub struct DbDevInfo {
pub name: Scalar<str>,
pub itr: Scalar<str>,
pub host: Option<Scalar<str>>,
pub version: Scalar<str>,
pub server: Scalar<str>,
pub class: Option<Scalar<str>>,
pub last_exported: Option<Scalar<str>>, pub last_unexported: Option<Scalar<str>>, pub exported: bool,
pub pid: Option<i32>,
}
pub(crate) fn set_prop_params<'a, Spec: SetPropSpec<'a>>(spec: Spec) -> Vec<Cow<'a, str>> {
let mut builder = set_prop::Builder::new();
spec.push_to_builder(&mut builder);
builder.finalize()
}
pub(crate) fn prop_params<'a>(obj_name: &'a str, rest: impl Iterator<Item = &'a str>) -> Spectrum<str> {
Spectrum::from_strs(Some(obj_name).into_iter().chain(rest)).expect("property parameters of more than 4 GiB")
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct PropCommands {
get: &'static str,
list: &'static str,
list_wildcard: bool,
}
impl PropCommands {
const FREE: Self = Self { get: "DbGetProperty", list: "DbGetPropertyList", list_wildcard: true };
const CLASS: Self = Self { get: "DbGetClassProperty", list: "DbGetClassPropertyList", list_wildcard: false };
pub(crate) const DEV: Self = Self { get: "DbGetDeviceProperty", list: "DbGetDevicePropertyList", list_wildcard: true };
}
mod get_prop {
use super::{Error, ErrorKind, Named, Result, Scalar, Spectrum};
#[derive(Debug)]
pub enum PropReply {
Listing {
listing: Spectrum<str>,
names: Spectrum<str>,
index: usize,
cursor: usize,
},
Attr {
listing: Spectrum<str>,
begin: usize,
count: usize,
names: Option<Spectrum<str>>,
index: usize,
cursor: usize,
},
}
impl PropReply {
pub(super) fn empty() -> Self {
Self::Listing { listing: Spectrum::new(), names: Spectrum::new(), index: 0, cursor: 0 }
}
pub(super) fn new(obj_name: &str, listing: Spectrum<str>, names: Spectrum<str>) -> Result<Self> {
let cursor = read_head(&listing, obj_name, names.len(), "property count", "properties")?;
Ok(Self::Listing { listing, names, index: 0, cursor })
}
pub fn len(&self) -> usize {
match self {
Self::Listing { names, index, .. } => names.len() - index,
Self::Attr { names: Some(names), index, .. } => names.len() - index,
Self::Attr { names: None, count, index, .. } => count - index,
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn next(&mut self) -> Option<Result<Named<Spectrum<str>>>> {
let (listing, names, index, cursor) = match self {
Self::Listing { listing, names, index, cursor } => (listing, names, index, cursor),
Self::Attr { listing, begin, count, names, index, cursor } => {
return next_attr_prop(listing, *begin, *count, names.as_ref(), index, cursor);
}
};
let expected = names.get([*index])?;
*index += 1;
match next_prop(listing, expected, cursor) {
Ok(prop) => Some(Ok(prop)),
Err(err) => {
*index = names.len();
Some(Err(err))
}
}
}
}
fn next_prop(listing: &Spectrum<str>, expected: &str, cursor: &mut usize) -> Result<Named<Spectrum<str>>> {
let (Some(name), Some(count_raw)) = (listing.get([*cursor]), listing.get([*cursor + 1])) else {
return Err(Error::new(ErrorKind::InvalidData)
.with_description(format!("Database answer is missing the name and values count of property {expected:?}"))
.with_append_description(format_args!("Got: {listing:?}")));
};
if !name.eq_ignore_ascii_case(expected) {
return Err(Error::new(ErrorKind::InvalidData)
.with_description(format!("Database answered property {name:?} where {expected:?} was requested")));
}
let Ok(count) = count_raw.parse::<usize>() else {
return Err(Error::new(ErrorKind::InvalidData)
.with_description(format!("Database answered values count {count_raw:?} of property {name:?}, which is not a number")));
};
let name_index = *cursor;
let begin = *cursor + 2;
*cursor = begin + count;
if *cursor > listing.len() {
return Err(Error::new(ErrorKind::InvalidData)
.with_description(format!("Database answer is missing values of property {name:?}"))
.with_append_description(format_args!("Got: {listing:?}")));
}
let name = listing.slice(name_index..name_index + 1).into_scalar().expect("one name");
Ok(Named::new(name, listing.slice(begin..*cursor)))
}
fn next_attr_prop(
listing: &Spectrum<str>,
begin: usize,
count: usize,
names: Option<&Spectrum<str>>,
index: &mut usize,
cursor: &mut usize,
) -> Option<Result<Named<Spectrum<str>>>> {
let Some(names) = names else {
if *index >= count {
return None;
}
*index += 1;
return Some(next_value_prop(listing, cursor));
};
let name = names.get_scalar([*index])?;
*index += 1;
let mut lookup = begin;
for _ in 0..count {
match next_value_prop(listing, &mut lookup) {
Ok(prop) if prop.name.value().eq_ignore_ascii_case(name.value()) => {
return Some(Ok(prop));
}
Ok(_) => {}
Err(err) => {
*index = names.len();
return Some(Err(err));
}
}
}
Some(Ok(Named::new(name, Spectrum::new())))
}
fn next_value_prop(listing: &Spectrum<str>, cursor: &mut usize) -> Result<Named<Spectrum<str>>> {
let name = next_str(listing, cursor, "property name")?;
let count = next_count(listing, cursor, "property values count")?;
let begin = *cursor;
*cursor += count.max(1);
if *cursor > listing.len() {
return Err(Error::new(ErrorKind::InvalidData)
.with_description(format!("Database answer is missing values of property {:?}", name.value()))
.with_append_description(format_args!("Got: {listing:?}")));
}
Ok(Named::new(name, listing.slice(begin..begin + count)))
}
#[derive(Debug)]
pub struct AttrPropReply {
listing: Spectrum<str>,
attr_names: Spectrum<str>,
prop_names: Option<Spectrum<str>>,
index: usize,
cursor: usize,
}
impl AttrPropReply {
pub(super) fn empty() -> Self {
Self { listing: Spectrum::new(), attr_names: Spectrum::new(), prop_names: None, index: 0, cursor: 0 }
}
pub(super) fn new(
obj_name: &str,
listing: Spectrum<str>,
attr_names: Spectrum<str>,
prop_names: Option<Spectrum<str>>,
) -> Result<Self> {
let cursor = read_head(&listing, obj_name, attr_names.len(), "attribute count", "attributes")?;
Ok(Self { listing, attr_names, prop_names, index: 0, cursor })
}
pub fn len(&self) -> usize {
self.attr_names.len() - self.index
}
pub fn next(&mut self) -> Option<Result<Named<PropReply>>> {
let Self { listing, attr_names, prop_names, index, cursor } = self;
let expected = attr_names.get([*index])?;
*index += 1;
let res = (|| {
let name = next_str(listing, cursor, "attribute name")?;
if !name.value().eq_ignore_ascii_case(expected) {
return Err(Error::new(ErrorKind::InvalidData)
.with_description(format!("Database answered attribute {:?} where {expected:?} was requested",
name.value())));
}
let count = next_count(listing, cursor, "property count")?;
let begin = *cursor;
for _ in 0..count {
next_value_prop(listing, cursor)?;
}
Ok(Named::new(name, PropReply::Attr {
listing: listing.clone(),
begin,
count,
names: prop_names.clone(),
index: 0,
cursor: begin,
}))
})();
if res.is_err() {
*index = attr_names.len();
}
Some(res)
}
}
fn read_head(listing: &Spectrum<str>, obj_name: &str, expected: usize, what: &str, entries: &str) -> Result<usize> {
let mut cursor = 0;
let output_obj_name = next_str(listing, &mut cursor, "object name")?;
if !output_obj_name.value().eq_ignore_ascii_case(obj_name) {
return Err(Error::new(ErrorKind::InvalidData)
.with_description(format!("Database answered for object {:?} where {obj_name:?} was requested",
output_obj_name.value())));
}
let count = next_count(listing, &mut cursor, what)?;
if count != expected {
return Err(Error::new(ErrorKind::InvalidData)
.with_description(format!("Database answered {count} {entries} of object {obj_name:?} where {expected} were requested")));
}
Ok(cursor)
}
fn next_str(listing: &Spectrum<str>, cursor: &mut usize, what: &str) -> Result<Scalar<str>> {
let Some(value) = listing.get_scalar([*cursor]) else {
return Err(Error::new(ErrorKind::InvalidData)
.with_description(format!("Database answer is missing the {what}"))
.with_append_description(format_args!("Got: {listing:?}")));
};
*cursor += 1;
Ok(value)
}
fn next_count(listing: &Spectrum<str>, cursor: &mut usize, what: &str) -> Result<usize> {
let raw = next_str(listing, cursor, what)?;
raw.value().trim().parse::<usize>().map_err(|_| Error::new(ErrorKind::InvalidData)
.with_description(format!("Database answered {what} {:?}, which is not a number", raw.value())))
}
}
pub trait GetPropSpec<Names> {
type Output;
fn collect(props: &mut get_prop::PropReply) -> Result<Self::Output>;
}
impl<T> GetPropSpec<&'_ str> for T
where
T: FromProp,
{
type Output = T;
fn collect(props: &mut get_prop::PropReply) -> Result<Self::Output> {
match props.next() {
Some(entry) => T::from_prop(&entry?.value),
None => Err(unexpected_len_err(1)),
}
}
}
impl<T> GetPropSpec<&'_ [&'_ str]> for T
where
T: FromProp,
{
type Output = Vec<Result<T>>;
fn collect(props: &mut get_prop::PropReply) -> Result<Self::Output> {
let mut values = Vec::with_capacity(props.len());
while let Some(entry) = props.next() {
values.push(T::from_prop(&entry?.value));
}
Ok(values)
}
}
impl<T> GetPropSpec<Spectrum<str>> for T
where
T: FromProp,
{
type Output = Vec<Result<T>>;
fn collect(props: &mut get_prop::PropReply) -> Result<Self::Output> {
<T as GetPropSpec<&[&str]>>::collect(props)
}
}
impl<T> GetPropSpec<&Spectrum<str>> for T
where
T: FromProp,
{
type Output = Vec<Result<T>>;
fn collect(props: &mut get_prop::PropReply) -> Result<Self::Output> {
<T as GetPropSpec<&[&str]>>::collect(props)
}
}
impl<T, const N: usize> GetPropSpec<[&'_ str; N]> for T
where
T: FromProp,
{
type Output = [Result<T>; N];
fn collect(props: &mut get_prop::PropReply) -> Result<Self::Output> {
let mut slots = [const { None }; N];
for slot in &mut slots {
let entry = props.next().ok_or_else(|| unexpected_len_err(N))?;
*slot = Some(T::from_prop(&entry?.value));
}
if props.next().is_some() {
return Err(unexpected_len_err(N));
}
Ok(slots.map(|slot| slot.expect("every slot is filled")))
}
}
impl GetPropSpec<All> for Spectrum<str> {
type Output = Vec<Named<Spectrum<str>>>;
fn collect(props: &mut get_prop::PropReply) -> Result<Self::Output> {
let mut values = Vec::with_capacity(props.len());
while let Some(entry) = props.next() {
values.push(entry?);
}
Ok(values)
}
}
macro_rules! impl_get_prop_spec_tuple {
( @one: $comp:ident ) => { 1 };
( @name: $comp:ident ) => { &'name str };
( $( $comp:ident )* ) => {
#[allow(non_snake_case)]
impl<'name, $($comp,)* > GetPropSpec<( $( impl_get_prop_spec_tuple!(@name: $comp), )* )> for ( $($comp,)* )
where
$(
$comp: FromProp,
)*
{
type Output = ( $( Result<$comp>,)* );
fn collect(props: &mut get_prop::PropReply) -> Result<Self::Output> {
const LEN: usize = 0 $( + impl_get_prop_spec_tuple!(@one: $comp) )*;
let output = ( $( {
let entry = props.next().ok_or_else(|| unexpected_len_err(LEN))?;
$comp::from_prop(&entry?.value)
}, )* );
if props.next().is_some() {
return Err(unexpected_len_err(LEN));
}
Ok(output)
}
}
};
}
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);
mod set_prop {
use super::{Cow, IntoProp, SetPropSpec};
pub struct Builder<'a> {
array: Vec<Cow<'a, str>>,
count: usize,
}
impl<'a> Builder<'a> {
pub fn new() -> Self {
Self {
array: vec![Cow::Borrowed("")], count: 0,
}
}
pub fn push<T>(&mut self, name: &'a str, value: T)
where
T: IntoProp<'a>,
{
self.array.push(Cow::Borrowed(name));
self.array.push(Cow::Borrowed("")); let len = self.array.len();
self.array.extend(value.into_prop());
let count = self.array.len() - len;
self.array[len - 1] = Cow::Owned(format!("{count}")); self.count += 1;
}
pub fn finalize(mut self) -> Vec<Cow<'a, str>> {
self.array[0] = Cow::Owned(format!("{}", self.count));
self.array
}
}
pub struct AttrBuilder<'a> {
array: Vec<Cow<'a, str>>,
count: usize,
}
impl<'a> AttrBuilder<'a> {
pub fn new() -> Self {
Self {
array: vec![Cow::Borrowed("")], count: 0,
}
}
pub fn push<S>(&mut self, attr_name: &'a str, spec: S)
where
S: SetPropSpec<'a>,
{
let mut builder = Builder::new();
spec.push_to_builder(&mut builder);
self.array.push(Cow::Borrowed(attr_name));
self.array.extend(builder.finalize());
self.count += 1;
}
pub fn finalize(mut self) -> Vec<Cow<'a, str>> {
self.array[0] = Cow::Owned(format!("{}", self.count));
self.array
}
}
}
pub trait SetPropSpec<'a>: Sized {
fn push_to_builder(self, builder: &mut set_prop::Builder<'a>);
}
impl<'a, T> SetPropSpec<'a> for (&'a str, T)
where
T: IntoProp<'a>,
{
#[inline]
fn push_to_builder(self, builder: &mut set_prop::Builder<'a>) {
builder.push(self.0, self.1);
}
}
impl<'a, T, const N: usize> SetPropSpec<'a> for [(&'a str, T); N]
where
T: IntoProp<'a>,
{
#[inline]
fn push_to_builder(self, builder: &mut set_prop::Builder<'a>) {
for (name, value) in self {
builder.push(name, value);
}
}
}
impl<'a, 'aa: 'a, T> SetPropSpec<'a> for Vec<(&'aa str, T)>
where
T: IntoProp<'a>,
{
#[inline]
fn push_to_builder(self, builder: &mut set_prop::Builder<'a>) {
for (name, value) in self {
builder.push(name, value);
}
}
}
macro_rules! impl_set_prop_spec_tuple {
( @name: $comp:ident ) => { &'a str };
( $( $comp:ident )* ) => {
#[allow(non_snake_case, unused_assignments)]
impl<'a, $($comp,)*> SetPropSpec<'a> for ( $( (impl_set_prop_spec_tuple!(@name: $comp), $comp), )* )
where
$(
$comp: IntoProp<'a>,
)*
{
#[inline]
fn push_to_builder(self, builder: &mut set_prop::Builder<'a>) {
let ( $($comp,)* ) = self;
$(
builder.push($comp.0, $comp.1);
)*
}
}
};
}
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);
#[derive(Debug, Clone, Copy)]
pub(crate) struct AttrPropCommands {
get: &'static str,
list: &'static str,
}
impl AttrPropCommands {
pub(crate) const CLASS: Self = Self { get: "DbGetClassAttributeProperty2", list: "DbGetClassAttributeList" };
pub(crate) const DEV: Self = Self { get: "DbGetDeviceAttributeProperty2", list: "DbGetDeviceAttributeList" };
}
pub trait GetAttrPropSpec<AttrNames, Names> {
type Output;
fn collect(attrs: &mut get_prop::AttrPropReply) -> Result<Self::Output>;
}
fn next_attr<T: GetPropSpec<Names>, Names>(attrs: &mut get_prop::AttrPropReply) -> Option<Result<NamedResult<T::Output>>> {
Some(attrs.next()?.map(|attr| attr.map(|mut props| T::collect(&mut props))))
}
impl<T: GetPropSpec<Names>, Names> GetAttrPropSpec<&'_ str, Names> for T {
type Output = T::Output;
fn collect(attrs: &mut get_prop::AttrPropReply) -> Result<Self::Output> {
match next_attr::<T, Names>(attrs) {
Some(attr) => attr?.value,
None => Err(unexpected_len_err(1)),
}
}
}
fn collect_attr_list<T: GetPropSpec<Names>, Names>(attrs: &mut get_prop::AttrPropReply) -> Result<Vec<Result<T::Output>>> {
let mut outputs = Vec::with_capacity(attrs.len());
while let Some(attr) = next_attr::<T, Names>(attrs) {
outputs.push(attr?.value);
}
Ok(outputs)
}
impl<T: GetPropSpec<Names>, Names> GetAttrPropSpec<&'_ [&'_ str], Names> for T {
type Output = Vec<Result<T::Output>>;
fn collect(attrs: &mut get_prop::AttrPropReply) -> Result<Self::Output> {
collect_attr_list::<T, Names>(attrs)
}
}
impl<T: GetPropSpec<Names>, Names> GetAttrPropSpec<Spectrum<str>, Names> for T {
type Output = Vec<Result<T::Output>>;
fn collect(attrs: &mut get_prop::AttrPropReply) -> Result<Self::Output> {
collect_attr_list::<T, Names>(attrs)
}
}
impl<T: GetPropSpec<Names>, Names> GetAttrPropSpec<&'_ Spectrum<str>, Names> for T {
type Output = Vec<Result<T::Output>>;
fn collect(attrs: &mut get_prop::AttrPropReply) -> Result<Self::Output> {
collect_attr_list::<T, Names>(attrs)
}
}
impl<T: GetPropSpec<Names>, Names, const N: usize> GetAttrPropSpec<[&'_ str; N], Names> for T {
type Output = [Result<T::Output>; N];
fn collect(attrs: &mut get_prop::AttrPropReply) -> Result<Self::Output> {
let mut slots = [const { None }; N];
for slot in &mut slots {
*slot = Some(next_attr::<T, Names>(attrs).ok_or_else(|| unexpected_len_err(N))??.value);
}
if attrs.next().is_some() {
return Err(unexpected_len_err(N));
}
Ok(slots.map(|slot| slot.expect("every slot is filled")))
}
}
impl<T: GetPropSpec<Names>, Names> GetAttrPropSpec<All, Names> for T {
type Output = Vec<NamedResult<T::Output>>;
fn collect(attrs: &mut get_prop::AttrPropReply) -> Result<Self::Output> {
let mut outputs = Vec::with_capacity(attrs.len());
while let Some(attr) = next_attr::<T, Names>(attrs) {
outputs.push(attr?);
}
Ok(outputs)
}
}
macro_rules! impl_get_attr_prop_spec_tuple {
( @one: $index:tt ) => { 1 };
( @name: $index:tt ) => { &'name str };
( @output: $index:tt ) => { Result<T::Output> };
( @next: $index:tt, $attrs:ident ) => { next_attr::<T, Names>($attrs).ok_or_else(|| unexpected_len_err(LEN))??.value };
( $( $index:tt )* ) => {
impl<'name, T: GetPropSpec<Names>, Names> GetAttrPropSpec<( $( impl_get_attr_prop_spec_tuple!(@name: $index), )* ), Names> for T {
type Output = ( $( impl_get_attr_prop_spec_tuple!(@output: $index), )* );
fn collect(attrs: &mut get_prop::AttrPropReply) -> Result<Self::Output> {
const LEN: usize = 0 $( + impl_get_attr_prop_spec_tuple!(@one: $index) )*;
let output = ( $( impl_get_attr_prop_spec_tuple!(@next: $index, attrs), )* );
if attrs.next().is_some() {
return Err(unexpected_len_err(LEN));
}
Ok(output)
}
}
};
}
impl_get_attr_prop_spec_tuple!(0);
impl_get_attr_prop_spec_tuple!(0 1);
impl_get_attr_prop_spec_tuple!(0 1 2);
impl_get_attr_prop_spec_tuple!(0 1 2 3);
impl_get_attr_prop_spec_tuple!(0 1 2 3 4);
impl_get_attr_prop_spec_tuple!(0 1 2 3 4 5);
pub trait SetAttrPropSpec<'a>: Sized {
fn push_to_builder(self, builder: &mut set_prop::AttrBuilder<'a>);
}
impl<'a, S> SetAttrPropSpec<'a> for (&'a str, S)
where
S: SetPropSpec<'a>,
{
#[inline]
fn push_to_builder(self, builder: &mut set_prop::AttrBuilder<'a>) {
builder.push(self.0, self.1);
}
}
impl<'a, S, const N: usize> SetAttrPropSpec<'a> for [(&'a str, S); N]
where
S: SetPropSpec<'a>,
{
#[inline]
fn push_to_builder(self, builder: &mut set_prop::AttrBuilder<'a>) {
for (attr_name, spec) in self {
builder.push(attr_name, spec);
}
}
}
impl<'a, S> SetAttrPropSpec<'a> for Vec<(&'a str, S)>
where
S: SetPropSpec<'a>,
{
#[inline]
fn push_to_builder(self, builder: &mut set_prop::AttrBuilder<'a>) {
for (attr_name, spec) in self {
builder.push(attr_name, spec);
}
}
}
fn attr_prop_params<'a, Spec: SetAttrPropSpec<'a>>(obj_name: &str, spec: Spec) -> Spectrum<str> {
let mut builder = set_prop::AttrBuilder::new();
spec.push_to_builder(&mut builder);
prop_params(obj_name, builder.finalize().iter().map(|param| &**param))
}
#[cfg(test)]
mod tests {
use futures::future::BoxFuture;
use super::*;
#[allow(dead_code)]
fn attr_props_in_send_future(db: &DatabaseProxy) -> BoxFuture<'_, Result<()>> {
Box::pin(async move {
let _ = db.get_dev_attr_prop_async::<f64, _, _>("motor/dev/1", "Position", "max_value").await?;
let _ = db.get_class_attr_prop_async::<(String, f64), _, _>("Motor", ["Position"], ("label", "max_value")).await?;
let _ = db.get_dev_attr_prop_async::<Spectrum<str>, _, _>("motor/dev/1", All, All).await?;
Ok(())
})
}
fn reply(names: &[&str], listing: &[&str]) -> get_prop::PropReply {
get_prop::PropReply::new(listing[0], Spectrum::from(listing), Spectrum::from(names)).unwrap()
}
fn attr_reply(attr_names: &[&str], prop_names: Option<&[&str]>, listing: &[&str]) -> get_prop::AttrPropReply {
get_prop::AttrPropReply::new(listing[0], Spectrum::from(listing), Spectrum::from(attr_names), prop_names.map(Spectrum::from)).unwrap()
}
#[test]
fn listing_head() {
let names = || Spectrum::from(&["prop"][..]);
let err = get_prop::PropReply::new("obj", Spectrum::from(&["another", "1", "prop", "0"][..]), names()).unwrap_err();
assert_eq!(err.kind(), ErrorKind::InvalidData);
let err = get_prop::PropReply::new("obj", Spectrum::from(&["obj", "2", "prop", "0"][..]), names()).unwrap_err();
assert_eq!(err.kind(), ErrorKind::InvalidData);
let err = get_prop::AttrPropReply::new("obj", Spectrum::from(&["obj", "2", "prop", "0"][..]), names(), None).unwrap_err();
assert_eq!(err.kind(), ErrorKind::InvalidData);
let mut props = get_prop::PropReply::empty();
assert!(props.is_empty());
assert!(props.next().is_none());
let mut attrs = get_prop::AttrPropReply::empty();
assert_eq!(attrs.len(), 0);
assert!(attrs.next().is_none());
}
fn attr_props(props: &mut get_prop::PropReply) -> Vec<(String, Vec<String>)> {
let mut taken = Vec::new();
while let Some(prop) = props.next() {
let prop = prop.unwrap();
taken.push((prop.name.value().to_string(), prop.value.iter().map(str::to_string).collect()));
}
taken
}
const ATTR_LISTING: &[&str] = &[
"sys/tg_test/1", "3",
"first", "2", "label", "1", "First", "rel_change", "2", "-1", "2",
"empty", "1", "unit", "0", " ",
"none", "0",
];
#[test]
fn attr_props_in_order() {
let mut reply = attr_reply(&["first", "empty", "none"], None, ATTR_LISTING);
assert_eq!(reply.len(), 3);
let Named { name, value: mut props } = reply.next().unwrap().unwrap();
assert_eq!(name.value(), "first");
assert_eq!(props.len(), 2);
assert_eq!(attr_props(&mut props), [
("label".to_string(), vec!["First".to_string()]),
("rel_change".to_string(), vec!["-1".to_string(), "2".to_string()]),
]);
let mut props = reply.next().unwrap().unwrap().value;
assert_eq!(attr_props(&mut props), [("unit".to_string(), vec![])]);
let mut props = reply.next().unwrap().unwrap().value;
assert!(attr_props(&mut props).is_empty());
assert!(reply.next().is_none());
}
#[test]
fn attr_prop_lookup() {
let mut reply = attr_reply(&["first", "empty", "none"], Some(&["REL_CHANGE", "unit"]), ATTR_LISTING);
let mut props = reply.next().unwrap().unwrap().value;
assert_eq!(props.len(), 2);
assert_eq!(attr_props(&mut props), [
("rel_change".to_string(), vec!["-1".to_string(), "2".to_string()]),
("unit".to_string(), vec![]),
]);
let mut props = reply.next().unwrap().unwrap().value;
assert_eq!(attr_props(&mut props), [
("REL_CHANGE".to_string(), vec![]),
("unit".to_string(), vec![]),
]);
}
#[test]
fn incoherent_attr_props() {
let mut reply = attr_reply(&["asked", "behind"], None, &["obj", "2", "another", "0", "behind", "0"]);
assert_eq!(reply.next().unwrap().unwrap_err().kind(), ErrorKind::InvalidData);
assert!(reply.next().is_none());
let mut reply = attr_reply(&["asked"], None, &["obj", "1", "asked", "1", "label", "2", "one"]);
assert_eq!(reply.next().unwrap().unwrap_err().kind(), ErrorKind::InvalidData);
}
#[test]
fn attr_prop_write() {
let params = attr_prop_params("obj", [
("first", vec![("label", "First"), ("unit", "mm")]),
("second", vec![("label", "Second")]),
]);
assert_eq!(params.iter().collect::<Vec<_>>(), [
"obj", "2",
"first", "2", "label", "1", "First", "unit", "1", "mm",
"second", "1", "label", "1", "Second",
]);
}
#[test]
fn props_in_order() {
let mut props = reply(&["first", "second"],
&["sys/tg_test/1", "2", "first", "1", "one", "second", "2", "two", "three"]);
assert_eq!(props.len(), 2);
let Named { name, value: values } = props.next().unwrap().unwrap();
assert_eq!(name.value(), "first");
assert_eq!(values.iter().collect::<Vec<_>>(), ["one"]);
let Named { name, value: values } = props.next().unwrap().unwrap();
assert_eq!(name.value(), "second");
assert_eq!(values.iter().collect::<Vec<_>>(), ["two", "three"]);
assert!(props.next().is_none());
}
#[test]
fn prop_without_value() {
let mut props = reply(&["empty"], &["obj", "1", "empty", "0"]);
let Named { name, value: values } = props.next().unwrap().unwrap();
assert_eq!(name.value(), "empty");
assert!(values.is_empty());
}
#[test]
fn incoherent_prop_name() {
let mut props = reply(&["asked"], &["obj", "1", "another", "1", "one"]);
let err = props.next().unwrap().unwrap_err();
assert_eq!(err.kind(), ErrorKind::InvalidData);
}
#[test]
fn missing_prop_values() {
let mut props = reply(&["prop", "behind"], &["obj", "2", "prop", "2", "one"]);
let err = props.next().unwrap().unwrap_err();
assert_eq!(err.kind(), ErrorKind::InvalidData);
assert!(props.next().is_none());
}
#[test]
fn single_prop_parsed() {
let mut props = reply(&["prop"], &["obj", "1", "prop", "1", "3.5"]);
let value = <f64 as GetPropSpec<&str>>::collect(&mut props).unwrap();
assert_eq!(value, 3.5);
}
}