@@ -31,6 +31,13 @@ pub enum CommandResponse {
3131 StackGet ( StackGetResponse ) ,
3232 Source ( String ) ,
3333 ContextGet ( ContextGetResponse ) ,
34+ Eval ( EvalResponse ) ,
35+ }
36+
37+ #[ derive( Debug , Clone , PartialEq ) ]
38+ pub struct DbgpError {
39+ pub message : String ,
40+ pub code : String ,
3441}
3542
3643#[ derive( Debug , Clone , PartialEq ) ]
@@ -135,6 +142,13 @@ pub struct ContinuationResponse {
135142 pub reason : String ,
136143}
137144
145+ #[ derive( Debug , Clone , PartialEq ) ]
146+ pub struct EvalResponse {
147+ pub success : bool ,
148+ pub error : Option < DbgpError > ,
149+ pub properties : Vec < Property > ,
150+ }
151+
138152#[ derive( Debug , Clone ) ]
139153pub struct StackGetResponse {
140154 pub entries : Vec < StackEntry > ,
@@ -244,6 +258,17 @@ impl DbgpClient {
244258 }
245259 }
246260
261+ pub ( crate ) async fn eval ( & mut self , expression : String , depth : u16 ) -> Result < EvalResponse > {
262+ let base64 = general_purpose:: STANDARD . encode ( expression. as_bytes ( ) ) ;
263+ match self . command ( "eval" , & mut [ "-d" , format ! ( "{}" , depth) . as_str ( ) , "--" , & base64] ) . await ? {
264+ Message :: Response ( r) => match r. command {
265+ CommandResponse :: Eval ( s) => Ok ( s) ,
266+ _ => anyhow:: bail!( "Unexpected response" ) ,
267+ } ,
268+ _ => anyhow:: bail!( "Unexpected response" ) ,
269+ }
270+ }
271+
247272 pub ( crate ) async fn step_into ( & mut self ) -> Result < ContinuationResponse > {
248273 match self . command ( "step_into" , & mut [ ] ) . await ? {
249274 Message :: Response ( r) => match r. command {
@@ -368,6 +393,7 @@ fn parse_xml(xml: &str) -> Result<Message, anyhow::Error> {
368393 "stack_get" => CommandResponse :: StackGet ( parse_stack_get ( & root) ) ,
369394 "source" => CommandResponse :: Source ( parse_source ( & root) ?) ,
370395 "context_get" => CommandResponse :: ContextGet ( parse_context_get ( & mut root) ?) ,
396+ "eval" => CommandResponse :: Eval ( parse_eval ( & mut root) ?) ,
371397 _ => CommandResponse :: Unknown ,
372398 } ,
373399 } ) ) ,
@@ -377,13 +403,40 @@ fn parse_xml(xml: &str) -> Result<Message, anyhow::Error> {
377403fn parse_source ( element : & Element ) -> Result < String , anyhow:: Error > {
378404 match element. children . first ( ) {
379405 Some ( XMLNode :: CData ( e) ) => {
380- Ok ( String :: from_utf8 ( general_purpose:: STANDARD . decode ( e) . unwrap ( ) ) . unwrap ( ) )
406+ Ok ( String :: from_utf8 ( general_purpose:: STANDARD . decode ( e) ? ) ? )
381407 }
382408 _ => anyhow:: bail!( "Expected CDATA" ) ,
383409 }
384410}
385411
412+
386413fn parse_context_get ( element : & mut Element ) -> Result < ContextGetResponse , anyhow:: Error > {
414+ Ok ( ContextGetResponse { properties : parse_properties ( element) ?} )
415+ }
416+
417+ fn parse_eval ( element : & mut Element ) -> Result < EvalResponse , anyhow:: Error > {
418+
419+ let error = if let Some ( mut error_el) = element. take_child ( "error" ) {
420+ let code = error_el. attributes . get ( "code" ) . map_or ( "" . to_string ( ) , |v|v. to_string ( ) ) ;
421+ let message = match error_el. take_child ( "message" ) {
422+ Some ( m) => match m. children . first ( ) {
423+ Some ( XMLNode :: CData ( e) ) => {
424+ e. to_string ( )
425+ } ,
426+ _ => String :: new ( ) ,
427+ } ,
428+ _ => "" . to_string ( )
429+ } ;
430+
431+ Some ( DbgpError { message, code : code. to_string ( ) } )
432+ } else {
433+ None
434+ } ;
435+
436+ Ok ( EvalResponse { success : true , properties : parse_properties ( element) ?, error } )
437+ }
438+
439+ fn parse_properties ( element : & mut Element ) -> Result < Vec < Property > , anyhow:: Error > {
387440 let mut properties: Vec < Property > = vec ! [ ] ;
388441 while let Some ( mut child) = element. take_child ( "property" ) {
389442 let encoding = child. attributes . get ( "encoding" ) . map ( |s| s. to_string ( ) ) ;
@@ -428,12 +481,12 @@ fn parse_context_get(element: &mut Element) -> Result<ContextGetResponse, anyhow
428481 key : child. attributes . get ( "key" ) . map ( |name| name. to_string ( ) ) ,
429482 address : child. attributes . get ( "address" ) . map ( |name| name. to_string ( ) ) ,
430483 encoding : encoding. clone ( ) ,
431- children : parse_context_get ( & mut child) . unwrap ( ) . properties ,
484+ children : parse_properties ( & mut child) ? ,
432485 value : decode_element ( Some ( & child) ) ,
433486 } ;
434487 properties. push ( p) ;
435488 }
436- Ok ( ContextGetResponse { properties } )
489+ Ok ( properties)
437490}
438491
439492fn decode_element ( element : Option < & Element > ) -> Option < String > {
@@ -601,6 +654,79 @@ function call_function(string $hello) {
601654 Ok ( ( ) )
602655 }
603656
657+ #[ test]
658+ fn test_parse_eval ( ) -> Result < ( ) , anyhow:: Error > {
659+ let result = parse_xml (
660+ r#"
661+ <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="https://xdebug.org/dbgp/xdebug" command="eval" transaction_id="28"><property type="int"><![CDATA[2]]></property></response>
662+ "# ,
663+ ) ?;
664+
665+ match result {
666+ Message :: Response ( r) => {
667+ match r. command {
668+ CommandResponse :: Eval ( response) => {
669+ let expected = EvalResponse {
670+ success : true ,
671+ error : None ,
672+ properties : vec ! [
673+ Property {
674+ name: "" . to_string( ) ,
675+ fullname: "" . to_string( ) ,
676+ classname: None ,
677+ page: None ,
678+ pagesize: None ,
679+ property_type: PropertyType :: Int ,
680+ facet: None ,
681+ size: None ,
682+ children: vec![ ] ,
683+ key: None ,
684+ address: None ,
685+ encoding: None ,
686+ value: Some ( 2 . to_string( ) ) ,
687+ } ,
688+ ] ,
689+ } ;
690+ assert_eq ! ( expected, response)
691+ }
692+ _ => panic ! ( "Could not parse context_get" ) ,
693+ } ;
694+ }
695+ _ => panic ! ( "Did not parse" ) ,
696+ } ;
697+ Ok ( ( ) )
698+ }
699+
700+ #[ test]
701+ fn test_parse_eval_error ( ) -> Result < ( ) , anyhow:: Error > {
702+ let result = parse_xml (
703+ r#"
704+ <response xmlns="urn:debugger_protocol_v1" xmlns:xdebug="https://xdebug.org/dbgp/xdebug" command="eval" transaction_id="6" status="break" reason="ok"><error code="206"><message><![CDATA[error evaluating code: Undefined constant "asda"]]></message></error></response>
705+ "# ,
706+ ) ?;
707+
708+ match result {
709+ Message :: Response ( r) => {
710+ match r. command {
711+ CommandResponse :: Eval ( response) => {
712+ let expected = EvalResponse {
713+ success : true ,
714+ error : Some ( DbgpError {
715+ message : "error evaluating code: Undefined constant \" asda\" " . to_string ( ) ,
716+ code : "206" . to_string ( )
717+ } ) ,
718+ properties : vec ! [ ] ,
719+ } ;
720+ assert_eq ! ( expected, response)
721+ }
722+ _ => panic ! ( "Could not parse context_get" ) ,
723+ } ;
724+ }
725+ _ => panic ! ( "Did not parse" ) ,
726+ } ;
727+ Ok ( ( ) )
728+ }
729+
604730 #[ test]
605731 fn test_parse_context_get ( ) -> Result < ( ) , anyhow:: Error > {
606732 let result = parse_xml (
0 commit comments