@@ -2,14 +2,25 @@ use dbus::{channel::{Channel, BusType}, Message};
22use nu_plugin:: LabeledError ;
33use nu_protocol:: { Spanned , Value } ;
44
5- use crate :: { config:: { DbusClientConfig , DbusBusChoice } , dbus_type:: DbusType , convert:: to_message_item} ;
5+ use crate :: { config:: { DbusClientConfig , DbusBusChoice } , dbus_type:: DbusType , convert:: to_message_item, introspection :: Node } ;
66
77/// Executes D-Bus actions on a connection, handling nushell types
88pub struct DbusClient {
99 config : DbusClientConfig ,
1010 conn : Channel ,
1111}
1212
13+ // Convenience macros for error handling
14+ macro_rules! validate_with {
15+ ( $type: ty, $spanned: expr) => ( <$type>:: new( & $spanned. item) . map_err( |msg| {
16+ LabeledError {
17+ label: msg,
18+ msg: "this argument is incorrect" . into( ) ,
19+ span: Some ( $spanned. span) ,
20+ }
21+ } ) )
22+ }
23+
1324impl DbusClient {
1425 pub fn new ( config : DbusClientConfig ) -> Result < DbusClient , LabeledError > {
1526 // Try to connect to the correct D-Bus destination, as specified in the config
@@ -35,6 +46,71 @@ impl DbusClient {
3546 } )
3647 }
3748
49+ fn error ( & self , err : impl std:: fmt:: Display , msg : impl std:: fmt:: Display ) -> LabeledError {
50+ LabeledError {
51+ label : err. to_string ( ) ,
52+ msg : msg. to_string ( ) ,
53+ span : Some ( self . config . span )
54+ }
55+ }
56+
57+ /// Introspect a D-Bus object
58+ pub fn introspect (
59+ & self ,
60+ dest : & Spanned < String > ,
61+ object : & Spanned < String > ,
62+ ) -> Result < Node , LabeledError > {
63+ let context = "while introspecting a D-Bus method" ;
64+ let valid_dest = validate_with ! ( dbus:: strings:: BusName , dest) ?;
65+ let valid_object = validate_with ! ( dbus:: strings:: Path , object) ?;
66+
67+ // Create the introspection method call
68+ let message = Message :: new_method_call (
69+ valid_dest,
70+ valid_object,
71+ "org.freedesktop.DBus.Introspectable" ,
72+ "Introspect"
73+ ) . map_err ( |err| self . error ( err, context) ) ?;
74+
75+ // Send and get the response
76+ let resp = self . conn . send_with_reply_and_block ( message, self . config . timeout . item )
77+ . map_err ( |err| self . error ( err, context) ) ?;
78+
79+ // Parse it to a Node
80+ let xml: & str = resp. get1 ( )
81+ . ok_or_else ( || self . error ( "Introspect method returned the wrong type" , context) ) ?;
82+
83+ Node :: from_xml ( xml) . map_err ( |err| self . error ( err, context) )
84+ }
85+
86+ /// Try to use introspection to get the signature of a method
87+ fn get_method_signature_by_introspection (
88+ & self ,
89+ dest : & Spanned < String > ,
90+ object : & Spanned < String > ,
91+ interface : & Spanned < String > ,
92+ method : & Spanned < String > ,
93+ ) -> Result < Vec < DbusType > , LabeledError > {
94+ let node = self . introspect ( dest, object) ?;
95+
96+ if let Some ( sig) = node. get_method_args_signature ( & interface. item , & method. item ) {
97+ DbusType :: parse_all ( & sig) . map_err ( |err| LabeledError {
98+ label : format ! ( "while getting interface {:?} method {:?} signature: {}" ,
99+ interface. item,
100+ method. item,
101+ err) ,
102+ msg : "try running with --no-introspect or --signature" . into ( ) ,
103+ span : Some ( self . config . span ) ,
104+ } )
105+ } else {
106+ Err ( LabeledError {
107+ label : format ! ( "Method {:?} not found on {:?}" , method. item, interface. item) ,
108+ msg : "check that this method/interface is correct" . into ( ) ,
109+ span : Some ( method. span ) ,
110+ } )
111+ }
112+ }
113+
38114 /// Call a D-Bus method and wait for the response
39115 pub fn call (
40116 & self ,
@@ -45,41 +121,42 @@ impl DbusClient {
45121 signature : Option < & Spanned < String > > ,
46122 args : & [ Value ] ,
47123 ) -> Result < Vec < Value > , LabeledError > {
48- macro_rules! error {
49- ( $label: expr) => ( LabeledError {
50- label: $label,
51- msg: "while calling a D-Bus method" . into( ) ,
52- span: Some ( self . config. span)
53- } )
54- }
124+ let context = "while calling a D-Bus method" ;
55125
56126 // Validate inputs before sending to the dbus lib so we don't panic
57- macro_rules! validate_with {
58- ( $type: ty, $spanned: expr) => ( <$type>:: new( & $spanned. item) . map_err( |msg| {
59- LabeledError {
60- label: msg,
61- msg: "this argument is incorrect" . into( ) ,
62- span: Some ( $spanned. span) ,
63- }
64- } ) )
65- }
66127 let valid_dest = validate_with ! ( dbus:: strings:: BusName , dest) ?;
67128 let valid_object = validate_with ! ( dbus:: strings:: Path , object) ?;
68129 let valid_interface = validate_with ! ( dbus:: strings:: Interface , interface) ?;
69130 let valid_method = validate_with ! ( dbus:: strings:: Member , method) ?;
70131
71132 // Parse the signature
72- let valid_signature = signature. map ( |s| DbusType :: parse_all ( & s. item ) . map_err ( |err| {
133+ let mut valid_signature = signature. map ( |s| DbusType :: parse_all ( & s. item ) . map_err ( |err| {
73134 LabeledError {
74135 label : err,
75136 msg : "in signature specified here" . into ( ) ,
76137 span : Some ( s. span ) ,
77138 }
78139 } ) ) . transpose ( ) ?;
79140
141+ // If not provided, try introspection (unless disabled)
142+ if valid_signature. is_none ( ) && self . config . introspect {
143+ match self . get_method_signature_by_introspection ( dest, object, interface, method) {
144+ Ok ( sig) => {
145+ valid_signature = Some ( sig) ;
146+ } ,
147+ Err ( err) => {
148+ eprintln ! ( "Warning: D-Bus introspection failed on {:?}. \
149+ Use `--no-introspect` or pass `--signature` to silence this warning. \
150+ Cause: {}",
151+ object. item,
152+ err. label) ;
153+ }
154+ }
155+ }
156+
80157 if let Some ( sig) = & valid_signature {
81158 if sig. len ( ) != args. len ( ) {
82- error ! ( format!( "expected {} arguments, got {}" , sig. len( ) , args. len( ) ) ) ;
159+ self . error ( format ! ( "expected {} arguments, got {}" , sig. len( ) , args. len( ) ) , context ) ;
83160 }
84161 }
85162
@@ -89,7 +166,7 @@ impl DbusClient {
89166 valid_object,
90167 valid_interface,
91168 valid_method,
92- ) . map_err ( |err| error ! ( err) ) ?;
169+ ) . map_err ( |err| self . error ( err, context ) ) ?;
93170
94171 // Convert the args to message items
95172 let sigs_iter = valid_signature. iter ( ) . flatten ( ) . map ( Some ) . chain ( std:: iter:: repeat ( None ) ) ;
@@ -99,8 +176,8 @@ impl DbusClient {
99176
100177 // Send it on the channel and get the response
101178 let resp = self . conn . send_with_reply_and_block ( message, self . config . timeout . item )
102- . map_err ( |err| error ! ( err. to_string ( ) ) ) ?;
179+ . map_err ( |err| self . error ( err, context ) ) ?;
103180
104- crate :: convert:: from_message ( & resp) . map_err ( |err| error ! ( err) )
181+ crate :: convert:: from_message ( & resp) . map_err ( |err| self . error ( err, context ) )
105182 }
106183}
0 commit comments