Skip to content

Commit 640983b

Browse files
committed
make dbus call take arguments
1 parent c4e5c9c commit 640983b

4 files changed

Lines changed: 558 additions & 16 deletions

File tree

src/client.rs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use dbus::{channel::{Channel, BusType}, Message};
22
use nu_plugin::LabeledError;
33
use nu_protocol::{Spanned, Value};
44

5-
use crate::config::{DbusClientConfig, DbusBusChoice};
5+
use crate::{config::{DbusClientConfig, DbusBusChoice}, dbus_type::DbusType, convert::to_message_item};
66

77
/// Executes D-Bus actions on a connection, handling nushell types
88
pub struct DbusClient {
@@ -42,8 +42,9 @@ impl DbusClient {
4242
object: &Spanned<String>,
4343
interface: &Spanned<String>,
4444
method: &Spanned<String>,
45-
) -> Result<Value, LabeledError> {
46-
// TODO accept arguments
45+
signature: Option<&Spanned<String>>,
46+
args: &[Value],
47+
) -> Result<Vec<Value>, LabeledError> {
4748
macro_rules! error {
4849
($label:expr) => (LabeledError {
4950
label: $label,
@@ -67,14 +68,35 @@ impl DbusClient {
6768
let valid_interface = validate_with!(dbus::strings::Interface, interface)?;
6869
let valid_method = validate_with!(dbus::strings::Member, method)?;
6970

71+
// Parse the signature
72+
let valid_signature = signature.map(|s| DbusType::parse_all(&s.item).map_err(|err| {
73+
LabeledError {
74+
label: err,
75+
msg: "in signature specified here".into(),
76+
span: Some(s.span),
77+
}
78+
})).transpose()?;
79+
80+
if let Some(sig) = &valid_signature {
81+
if sig.len() != args.len() {
82+
error!(format!("expected {} arguments, got {}", sig.len(), args.len()));
83+
}
84+
}
85+
7086
// Construct the method call message
71-
let message = Message::new_method_call(
87+
let mut message = Message::new_method_call(
7288
valid_dest,
7389
valid_object,
7490
valid_interface,
7591
valid_method,
7692
).map_err(|err| error!(err))?;
7793

94+
// Convert the args to message items
95+
let sigs_iter = valid_signature.iter().flatten().map(Some).chain(std::iter::repeat(None));
96+
for (val, sig) in args.iter().zip(sigs_iter) {
97+
message = message.append1(to_message_item(val, sig)?);
98+
}
99+
78100
// Send it on the channel and get the response
79101
let resp = self.conn.send_with_reply_and_block(message, self.config.timeout.item)
80102
.map_err(|err| error!(err.to_string()))?;

src/convert.rs

Lines changed: 167 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
1-
use dbus::{Message, arg::{ArgType, RefArg}};
1+
use dbus::{Message, arg::{ArgType, RefArg, messageitem::{MessageItemArray, MessageItem, MessageItemDict}}, Signature};
2+
use nu_plugin::LabeledError;
23
use nu_protocol::{Value, Span, Record};
4+
use std::str::FromStr;
35

4-
pub fn from_message(message: &Message) -> Result<Value, String> {
6+
use crate::dbus_type::DbusType;
7+
8+
/// Get the arguments of a message as nushell Values
9+
pub fn from_message(message: &Message) -> Result<Vec<Value>, String> {
510
let mut out = vec![];
611
for refarg in message.iter_init() {
712
out.push(from_refarg(&refarg)?);
813
}
9-
Ok(Value::list(out, Span::unknown()))
14+
Ok(out)
1015
}
1116

1217
pub fn from_refarg(refarg: &dyn RefArg) -> Result<Value, String> {
@@ -15,12 +20,12 @@ pub fn from_refarg(refarg: &dyn RefArg) -> Result<Value, String> {
1520
if refarg.signature().starts_with("a{") {
1621
// This is a dictionary
1722
let mut record = Record::new();
18-
for entry in refarg.as_iter().unwrap() {
19-
let mut entry_iter = entry.as_iter().unwrap();
20-
let key = entry_iter.next().unwrap();
21-
let val = entry_iter.next().unwrap();
22-
if let Some(key) = key.as_str() {
23-
record.insert(key, from_refarg(val)?);
23+
let mut iter = refarg.as_iter().unwrap();
24+
while let Some(key) = iter.next() {
25+
if let Some(val) = iter.next() {
26+
if let Some(key_str) = key.as_str() {
27+
record.insert(key_str, from_refarg(val)?);
28+
}
2429
}
2530
}
2631
Value::record(record, Span::unknown())
@@ -68,3 +73,156 @@ pub fn from_refarg(refarg: &dyn RefArg) -> Result<Value, String> {
6873
return Err("Encountered invalid D-Bus value".into()),
6974
})
7075
}
76+
77+
pub fn to_message_item(value: &Value, expected_type: Option<&DbusType>)
78+
-> Result<MessageItem, LabeledError>
79+
{
80+
// Report errors from conversion. Error must support Display
81+
macro_rules! try_convert {
82+
($result_expr:expr) => ($result_expr.map_err(|err| LabeledError {
83+
label: format!("Failed to convert value to the D-Bus `{:?}` type",
84+
expected_type.unwrap()),
85+
msg: err.to_string(),
86+
span: Some(value.span()),
87+
})?)
88+
}
89+
90+
// Try to match values to expected types
91+
match (value, expected_type) {
92+
// Boolean
93+
(Value::Bool { val, .. }, Some(DbusType::Boolean)) =>
94+
Ok(MessageItem::Bool(*val)),
95+
96+
// Strings and specialized strings
97+
(Value::String { val, .. }, Some(DbusType::String)) =>
98+
Ok(MessageItem::Str(val.to_owned())),
99+
(Value::String { val, .. }, Some(DbusType::ObjectPath)) =>
100+
Ok(MessageItem::ObjectPath(try_convert!(dbus::strings::Path::new(val)))),
101+
(Value::String { val, .. }, Some(DbusType::Signature)) =>
102+
Ok(MessageItem::Signature(try_convert!(dbus::strings::Signature::new(val)))),
103+
104+
// Signed ints
105+
(Value::Int { val, .. }, Some(DbusType::Int64)) =>
106+
Ok(MessageItem::Int64(*val)),
107+
(Value::Int { val, .. }, Some(DbusType::Int32)) =>
108+
Ok(MessageItem::Int32(try_convert!(i32::try_from(*val)))),
109+
(Value::Int { val, .. }, Some(DbusType::Int16)) =>
110+
Ok(MessageItem::Int16(try_convert!(i16::try_from(*val)))),
111+
112+
// Unsigned ints
113+
(Value::Int { val, .. }, Some(DbusType::UInt64)) =>
114+
Ok(MessageItem::UInt64(try_convert!(u64::try_from(*val)))),
115+
(Value::Int { val, .. }, Some(DbusType::UInt32)) =>
116+
Ok(MessageItem::UInt32(try_convert!(u32::try_from(*val)))),
117+
(Value::Int { val, .. }, Some(DbusType::UInt16)) =>
118+
Ok(MessageItem::UInt16(try_convert!(u16::try_from(*val)))),
119+
(Value::Int { val, .. }, Some(DbusType::Byte)) =>
120+
Ok(MessageItem::Byte(try_convert!(u8::try_from(*val)))),
121+
122+
// Ints from string
123+
(Value::String { val, .. }, Some(DbusType::Int64)) =>
124+
Ok(MessageItem::Int64(try_convert!(i64::from_str(&val[..])))),
125+
(Value::String { val, .. }, Some(DbusType::Int32)) =>
126+
Ok(MessageItem::Int32(try_convert!(i32::from_str(&val[..])))),
127+
(Value::String { val, .. }, Some(DbusType::Int16)) =>
128+
Ok(MessageItem::Int16(try_convert!(i16::from_str(&val[..])))),
129+
(Value::String { val, .. }, Some(DbusType::UInt64)) =>
130+
Ok(MessageItem::UInt64(try_convert!(u64::from_str(&val[..])))),
131+
(Value::String { val, .. }, Some(DbusType::UInt32)) =>
132+
Ok(MessageItem::UInt32(try_convert!(u32::from_str(&val[..])))),
133+
(Value::String { val, .. }, Some(DbusType::UInt16)) =>
134+
Ok(MessageItem::UInt16(try_convert!(u16::from_str(&val[..])))),
135+
(Value::String { val, .. }, Some(DbusType::Byte)) =>
136+
Ok(MessageItem::Byte(try_convert!(u8::from_str(&val[..])))),
137+
138+
// Float
139+
(Value::Float { val, .. }, Some(DbusType::Double)) =>
140+
Ok(MessageItem::Double(*val)),
141+
(Value::String { val, .. }, Some(DbusType::Double)) =>
142+
Ok(MessageItem::Double(try_convert!(f64::from_str(&val[..])))),
143+
144+
// List/array
145+
(Value::List { vals, .. }, Some(DbusType::Array(content_type))) => {
146+
let content_sig = Signature::from(content_type.stringify());
147+
let items = vals.iter()
148+
.map(|content| to_message_item(content, Some(content_type)))
149+
.collect::<Result<Vec<MessageItem>, _>>()?;
150+
Ok(MessageItem::Array(MessageItemArray::new(items, content_sig).unwrap()))
151+
},
152+
153+
// Struct
154+
(Value::List { vals, .. }, Some(DbusType::Struct(types))) => {
155+
if vals.len() != types.len() {
156+
return Err(LabeledError {
157+
label: format!("expected struct with {} element(s) ({:?})", types.len(), types),
158+
msg: format!("this list has {} element(s) instead", vals.len()),
159+
span: Some(value.span())
160+
});
161+
}
162+
let items = vals.iter().zip(types)
163+
.map(|(content, r#type)| to_message_item(content, Some(r#type)))
164+
.collect::<Result<Vec<MessageItem>, _>>()?;
165+
Ok(MessageItem::Struct(items))
166+
},
167+
168+
// Record/dict
169+
(Value::Record { val, .. }, Some(DbusType::Array(content_type)))
170+
if matches!(**content_type, DbusType::DictEntry(_, _)) =>
171+
{
172+
if let DbusType::DictEntry(ref key_type, ref val_type) = **content_type {
173+
let key_sig = Signature::from(key_type.stringify());
174+
let val_sig = Signature::from(val_type.stringify());
175+
let pairs = val.iter()
176+
.map(|(key, val)| {
177+
let key_as_value = Value::string(key, value.span());
178+
let key_message_item = to_message_item(&key_as_value, Some(key_type))?;
179+
let val_message_item = to_message_item(val, Some(val_type))?;
180+
Ok((key_message_item, val_message_item))
181+
})
182+
.collect::<Result<Vec<_>, LabeledError>>()?;
183+
Ok(MessageItem::Dict(MessageItemDict::new(pairs, key_sig, val_sig).unwrap()))
184+
} else {
185+
unreachable!()
186+
}
187+
},
188+
189+
// Variant - use automatic type
190+
(other_value, Some(DbusType::Variant)) =>
191+
Ok(MessageItem::Variant(Box::new(to_message_item(other_value, None)?))),
192+
193+
// Value not compatible with expected type
194+
(other_value, Some(expectation)) =>
195+
Err(LabeledError {
196+
label: format!("`{}` can not be converted to the D-Bus `{:?}` type",
197+
other_value.get_type(), expectation),
198+
msg: format!("expected a `{:?}` here", expectation),
199+
span: Some(other_value.span()),
200+
}),
201+
202+
// Automatic types (with no type expectation)
203+
(Value::String { .. }, None) =>
204+
to_message_item(value, Some(&DbusType::String)),
205+
(Value::Int { .. }, None) =>
206+
to_message_item(value, Some(&DbusType::Int64)),
207+
(Value::Float { .. }, None) =>
208+
to_message_item(value, Some(&DbusType::Double)),
209+
(Value::Bool { .. }, None) =>
210+
to_message_item(value, Some(&DbusType::Boolean)),
211+
(Value::List { .. }, None) =>
212+
to_message_item(value, Some(&DbusType::Array(DbusType::Variant.into()))),
213+
(Value::Record { .. }, None) =>
214+
to_message_item(value, Some(&DbusType::Array(
215+
DbusType::DictEntry(
216+
DbusType::String.into(),
217+
DbusType::Variant.into()
218+
).into()))),
219+
220+
// No expected type, but can't handle this type
221+
_ =>
222+
Err(LabeledError {
223+
label: format!("can not use values of type `{}` in D-Bus calls", value.get_type()),
224+
msg: "use a supported type here instead".into(),
225+
span: Some(value.span()),
226+
})
227+
}
228+
}

0 commit comments

Comments
 (0)