]> xenbits.xensource.com Git - xcp/xen-api-libs.git/commitdiff
[rpc-light] Initial commit for rpc-light.
authorThomas Gazagnaire <thomas.gazagnaire@citrix.com>
Wed, 16 Sep 2009 14:45:24 +0000 (15:45 +0100)
committerThomas Gazagnaire <thomas.gazagnaire@citrix.com>
Wed, 16 Sep 2009 14:45:24 +0000 (15:45 +0100)
Rpc-light is a camlp4 library to easily marshall and unmarshall ML types to and from different RPC values (at the moment, only XML-RPC conversions are implemented).

Makefile
rpc-light/META-rpc-light [new file with mode: 0644]
rpc-light/META-xmlrpc [new file with mode: 0644]
rpc-light/Makefile
rpc-light/examples/Makefile
rpc-light/examples/all_types.ml
rpc-light/pa_rpc.ml
rpc-light/rpc.ml
rpc-light/xmlrpc.ml

index 268be672f60b015b417eea0d536d4ff6f81a743b..47863d50e5359b549e0e36901a524dfccc0ad479 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -18,6 +18,7 @@ all:
        $(MAKE) -C log
        $(MAKE) -C sha1
        $(MAKE) -C xml-light2
+       $(MAKE) -C rpc-light
  
 allxen:
        $(MAKE) -C mmap
@@ -35,6 +36,7 @@ install:
        $(MAKE) -C log install
        $(MAKE) -C sha1 install
        $(MAKE) -C xml-light2 install
+       $(MAKE) -C rpc-light install
 
 installxen:
        $(MAKE) -C mmap install
@@ -52,6 +54,7 @@ uninstall:
        $(MAKE) -C log uninstall
        $(MAKE) -C sha1 uninstall
        $(MAKE) -C xml-light2 uninstall
+       $(MAKE) -C rpc-light uninstall
 
 uninstallxen:
        $(MAKE) -C eventchn uninstall
@@ -102,6 +105,7 @@ clean:
        make -C log clean
        make -C sha1 clean
        make -C xml-light2 clean
+       make -C rpc-light clean
        rm -f $(OUTPUT_API_PKG)
 
 cleanxen:
diff --git a/rpc-light/META-rpc-light b/rpc-light/META-rpc-light
new file mode 100644 (file)
index 0000000..81be631
--- /dev/null
@@ -0,0 +1,11 @@
+version = "0.1"
+description = "RPC light: lightweight library to convert plain ML types to and from RPC values"
+
+package "syntax"
+  (
+  version = "0.1"
+  description = "pa-rpc: library to marshalling/unmarshalling ML types to/from Rpc.t"
+  requires = "type-conv.syntax"
+  archive(syntax,preprocessor) = "pa_rpc.cma"
+  archive(syntax,toploop) = "pa_rpc.cma"
+  )
\ No newline at end of file
diff --git a/rpc-light/META-xmlrpc b/rpc-light/META-xmlrpc
new file mode 100644 (file)
index 0000000..49088ea
--- /dev/null
@@ -0,0 +1,5 @@
+version = "0.1"
+description = "XML-RPC marshalling/unmarshalling"
+requires = "xml-light2"
+archive(byte) = "xmlrpc.cma"
+archive(native) = "xmlrpc.cmxa"
index 8f12e48674cc58e45b46f85a63bda12d6ef7f28d..1fb2d83eee6a2727615ed6e5c98eee720a49c8ff 100644 (file)
@@ -1,49 +1,52 @@
 OCAMLC = ocamlfind ocamlc
 OCAMLOPT = ocamlfind ocamlopt
-OCAMLFLAGS = -annot -g
-PACKS = xmlm
+OCAMLFLAGS = -annot
 
-ICAMLP4 = -I $(shell ocamlfind query camlp4) -I $(shell ocamlfind query type-conv)
-DOCDIR  = /myrepos/xen-api-libs.hg/doc
-TARGETS = \
-       rpc.cmi rpc.cmo rpc.o rpc.cmx \
-       pa_rpc.cma \
-       xmlrpc.cmi xmlrpc.cmo xmlrpc.o xmlrpc.cmx \
-       jsonrpc.cmi jsonrpc.cmo jsonrpc.o jsonrpc.cmx
+ICAMLP4=-I $(shell ocamlfind query camlp4) -I $(shell ocamlfind query type-conv)
 
 .PHONY: all clean
-all: $(TARGETS)
+all: pa_rpc.cma xmlrpc.cma xmlrpc.cmxa
 
-pa_rpc.cma: rpc.cmo p4_rpc.cmo pa_rpc.cmo
+pa_rpc.cma: rpc.cmo pa_rpc.cmo
        $(OCAMLC) -a $(ICAMLP4) -o $@ $^
 
-pa_rpc.cmo: pa_rpc.ml p4_rpc.cmo
-       $(OCAMLC) $(OCAMLFLAGS) -c -package camlp4,type-conv -pp "camlp4orf" $(ICAMLP4) $@ $<
+xmlrpc.cmxa: rpc.cmx xmlrpc.cmx
+       $(OCAMLOPT) -a -o $@ $^
 
-p4_rpc.cmo: p4_rpc.ml rpc.cmo
-       $(OCAMLC) $(OCAMLFLAGS) -c -package camlp4,type-conv -pp "camlp4orf" $(ICAMLP4) $@ $<
+xmlrpc.cma: rpc.cmo xmlrpc.cmo
+       $(OCAMLC) -a -o $@ $^
+
+
+xmlrpc.cmx: xmlrpc.ml
+       $(OCAMLOPT) $(OCAMLFLAGS) -c -I ../xml-light2 -o $@ $<
+
+xmlrpc.cmo: xmlrpc.ml
+       $(OCAMLC) $(OCAMLFLAGES) -c -I ../xml-light2 -o $@ $<
 
-%.o %.cmx: %.ml
-       $(OCAMLOPT) $(OCAMLFLAGS) -package $(PACKS) -c -o $@ $<
 
-%.cmo: %.ml
-       $(OCAMLC) $(OCAMLFLAGS) -package $(PACKS) -c -o $@ $<
+rpc.cmx: rpc.ml
+       $(OCAMLOPT) $(OCAMLFLAGS) -c -o $@ $<
+
+rpc.cmo: rpc.ml
+       $(OCAMLC) $(OCAMLFLAGS) -c -o $@ $<
+
+
+pa_rpc.cmo: pa_rpc.ml
+       $(OCAMLC) $(OCAMLFLAGS) -c -package camlp4,type-conv -pp "camlp4orf" $(ICAMLP4) $@ $<
 
-%.cmi: %.mli %.ml
-       $(OCAMLOPT) $(OCAMLFLAGS) -package $(PACKS) -c -o $@ $<
 
 .PHONY: install
-install: INSTALL_PATH = $(DESTDIR)$(shell ocamlfind printconf destdir)
-install: all
-       ocamlfind install -destdir $(INSTALL_PATH) rpc-light META $(TARGETS)
+install: rpc.cmi pa_rpc.cma xmlrpc.cma xmlrpc.cmxa
+       cp META-xmlrpc META
+       ocamlfind install -destdir $(DESTDIR)$(shell ocamlfind printconf destdir) xmlrpc META xmlrpc.cma xmlrpc.cmxa xmlrpc.cmi rpc.cmi xmlrpc.cmx rpc.cmx xmlrpc.a xmlrpc.o
+       cp META-rpc-light META
+       ocamlfind install -destdir $(DESTDIR)$(shell ocamlfind printconf destdir) rpc-light META pa_rpc.cma pa_rpc.cmi
+       rm META
 
 .PHONY: uninstall
 uninstall:
+       ocamlfind remove xmlrpc
        ocamlfind remove rpc-light
 
-.PHONY: doc
-doc: $(INTF)
-       python ../doc/doc.py $(DOCDIR) "rpc-light" "package" "jsonrpc pa_rpc rpc xmlrpc" "." "xmlm" ""
-
 clean:
-       rm -f *.cmo *.cmx *.cma *.cmxa *.annot *.o *.cmi *.a
+       rm -f *.cmo *.cmx *.cma *.cmxa *.annot *.o *.cmi *.a
\ No newline at end of file
index 178cd3e896800cc4f62255b7381e2b94f8cde156..cd38fd305dfa3fddd3091933120d33069993947f 100644 (file)
@@ -1,15 +1,9 @@
 OCAMLC = ocamlfind ocamlc
 OCAMLOPT = ocamlfind ocamlopt
-OCAMLFLAGS = -annot -g
+OCAMLFLAGS = -annot
 
-PACKS = rpc-light
-EXAMPLES = \
-       all_types \
-       phantom \
-       xapi \
-       encoding \
-       dict \
-       variants
+PACKS = xmlrpc
+EXAMPLES = all_types
 
 EXECS=$(foreach example, $(EXAMPLES), $(example).opt)
 
@@ -20,11 +14,7 @@ all: $(EXECS)
        $(OCAMLOPT) -linkpkg -package $(PACKS) -o $@ $<
 
 %.cmx: %.ml
-       $(OCAMLOPT) $(OCAMLFLAGS) -package $(PACKS),rpc-light.syntax -syntax camlp4o -c -o $@ $<
-
-%_gen: %.ml
-       camlp4o $(shell ocamlfind query rpc-light.syntax -r -format "-I %d %a" -predicates syntax,preprocessor) $< -printer o > $@.ml
-       $(OCAMLOPT) $(OCAMLFLAGS) -package $(PACKS) -c -o $@ $@.ml
+       $(OCAMLOPT) -package $(PACKS),rpc-light.syntax -syntax camlp4o -c -o $@ $<
 
 clean:
-       rm -f *.cmx *.cmi *.cmo *.cmxa *.o $(EXECS)
+       rm -f *.cmx *.cmi *.cmo *.cmxa *.o $(EXECS)
\ No newline at end of file
index 6d8f714e4581bde3e4a35693194f541a05cdf7cc..804837a72ed3d9f797631f0305b525d01b45d60f 100644 (file)
-(*
- * Copyright (C) 2006-2009 Citrix Systems Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Lesser General Public License for more details.
- *)
-
 type t = Foo of int | Bar of (int * float) with rpc
 
-module M = struct
-       type m = t with rpc
-end
-
-type 'a x = {
-       foo: M.m;
+type x = {
+       foo: t;
        bar: string;
        gna: float list;
        f1: (int option * bool list * float list list) option;
        f2: (string * string list) array;
        f3: int32;
        f4: int64;
-       f5: int;
-       f6: (unit * char) list;
-       f7: 'a list;
-       f8: (string, t) Hashtbl.t;
-       progress: int array;
  } with rpc
 
 let _ =
-       let x = {
+       let x1 = {
                foo= Foo 3;
-               bar= "ha          ha";
+               bar= "foo";
                gna=[1.; 2.; 3.; 4. ];
                f2 = [| "hi",["hi"]; "hou",["hou";"hou"]; "foo", ["b";"a";"r"] |];
-               f1 = Some (None, [true], [[1.]; [2.;3.]]);
+               f1 = None;
                f3 = Int32.max_int;
-               f4 = Int64.max_int;
-               f5 = max_int;
-               f6 = [ (),'a' ; (),'b' ; (),'c'; (),'d' ; (),'e' ];
-               f7 = [ Foo 1; Foo 2; Foo 3 ];
-               f8 = Hashtbl.create 0;
-               progress = [| 0; 1; 2; 3; 4; 5 |];
+               f4 = Int64.max_int 
        } in
-
-       Hashtbl.add x.f8 "hello" (Foo 5);
-       Hashtbl.add x.f8 "there" (Bar (5,0.5));
-
-       (* Testing basic marshalling/unmarshalling *)
-       
-       let rpc = rpc_of_x M.rpc_of_m x in
-
-       let rpc_xml = Xmlrpc.to_string rpc in
-       let rpc_json = Jsonrpc.to_string rpc in
-
-       Printf.printf "\n==rpc_xml==\n%s\n" rpc_xml;
-       Printf.printf "\n==json==\n%s\n" rpc_json;
-
-       let callback fields value = match (fields, value) with
-               | ["progress"], Rpc.Int i -> Printf.printf "Progress: %Ld\n" i
-               | _                       -> ()
-       in
-       let x_xml = x_of_rpc M.m_of_rpc (Xmlrpc.of_string ~callback rpc_xml) in
-       let x_json = x_of_rpc M.m_of_rpc (Jsonrpc.of_string rpc_json) in
-
-       Printf.printf "\n==Sanity check 1==\nx=x_xml: %b\nx=x_json: %b\n" (x = x_xml) (x = x_json);
-       (*assert (x = x_xml && x = x_json);*)
-       
-       (* Testing calls and responses *)
-       
-       let call = Rpc.call "foo" [ rpc; Rpc.String "Mouhahahaaaaa" ] in
-       let success = Rpc.success rpc in
-       let failure = Rpc.failure rpc in
-
-       let c_xml_str = Xmlrpc.string_of_call call in
-       let s_xml_str = Xmlrpc.string_of_response success in
-       let f_xml_str = Xmlrpc.string_of_response failure in
-
-       let c_json_str = Jsonrpc.string_of_call call in
-       let s_json_str = Jsonrpc.string_of_response success in
-       let f_json_str = Jsonrpc.string_of_response failure in
-
-       Printf.printf "\n==call==\n %s\n%s\n" c_xml_str c_json_str;
-       Printf.printf "\n==success==\n %s\n%s\n" s_xml_str s_json_str;
-       Printf.printf "\n==failure==\n %s\n%s\n" f_xml_str f_json_str;
-
-       let c_xml = Xmlrpc.call_of_string c_xml_str in
-       let s_xml = Xmlrpc.response_of_string s_xml_str in
-       let f_xml = Xmlrpc.response_of_string f_xml_str in
-
-       (* Printf.printf "\n==Sanity check 2==\ncall=c_xml: %b\nsuccess=s_xml: %b\nfailure=f_xml: %b\n"
-               (call = c_xml) (success = s_xml) (failure = f_xml);
-       assert (call = c_xml && success = s_xml && failure = f_xml); *)
-
-       let c_json = Jsonrpc.call_of_string c_json_str in
-       let s_json = Jsonrpc.response_of_string s_json_str in
-       let f_json = Jsonrpc.response_of_string f_json_str in
-
-       Printf.printf "\n==Sanity check 3==\ncall=c_json': %b\nsuccess=s_json': %b\nfailure=f_json': %b\n"
-               (call = c_json) (success = s_json) (failure = f_json);
-       assert (call = c_json && success = s_json && failure = f_json);
-
-       let print_hashtbl h =
-         Hashtbl.iter (fun k v -> Printf.printf "key=%s v=%s\n" k (match v with | Foo x -> Printf.sprintf "Foo (%d)" x | Bar (x,y) -> Printf.sprintf "Bar (%d,%f)" x y)) h
-       in
-
-       Printf.printf "Original hashtbl:\n";
-       print_hashtbl x.f8;
-       Printf.printf "Testing xml Hashtbl representation:\n";
-       print_hashtbl x_xml.f8;
-       Printf.printf "Testing json Hashtbl representation:\n";
-       print_hashtbl x_json.f8
-
+       let str = Xmlrpc.to_string (rpc_of_x x1) in
+       Printf.printf "String: %s\n\n" str;
+       let x2 = x_of_rpc (Xmlrpc.of_string str) in
+       Printf.printf "Result: %s\nx1=x2: %b\n\n" str (x1 = x2)
index 88016aecff0c2bd6ad9746c43af0684d125151a5..0eebfcd761cf3f7a1d18c8b121ed6043cacddb84 100644 (file)
-(*
- * Copyright (C) 2006-2009 Citrix Systems Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Lesser General Public License for more details.
- *)
+(* -pp camlp4orf *)
 
 open Camlp4
 open PreCast
 open Ast
+open Syntax
 
-open Pa_type_conv
+(* utils *)
 
-let _ =
-       add_generator "rpc" (fun tds ->
-               let _loc = loc_of_ctyp tds in
-               <:str_item< $P4_rpc.gen tds$ >>)
+let biList_to_expr _loc bindings final =
+       List.fold_right 
+               (fun b a -> <:expr< let $b$ in $a$ >>)
+               bindings final
+
+let function_with_label_args _loc ~fun_name ~final_ident ~function_body ~return_type opt_args =
+   let opt_args = opt_args @ [ <:patt< $lid:final_ident$ >> ] in
+   <:binding< $lid:fun_name$ = 
+      $List.fold_right (fun b a ->
+        <:expr<fun $b$ -> $a$ >>
+       ) opt_args <:expr< ( $function_body$ : $return_type$ ) >>
+      $ >>
+
+let rec list_of_fields _loc fields =
+       match fields with
+       | <:ctyp< $t1$; $t2$ >> ->
+               list_of_fields _loc t1 @ list_of_fields _loc t2
+       | <:ctyp< $lid:field_name$: mutable $t$ >> | <:ctyp< $lid:field_name$: $t$ >> ->
+               [ field_name, t ]
+       | _ -> failwith "unexpected type while processing fields"
+
+let record_of_fields _loc fields =
+       let rec_bindings = List.map (fun (n,e) -> Ast.RbEq(_loc, <:ident< $lid:n$ >>, e)) fields in
+       <:expr< { $rbSem_of_list rec_bindings$ } >>
+
+let list_of_expr _loc exprs =
+       match List.rev exprs with
+       | []   -> <:expr< [ ] >>
+       | h::t -> List.fold_left (fun accu x -> <:expr< [ $x$ :: $accu$ ] >>) <:expr< [ $h$ ] >> t 
+
+let patt_list_of_expr _loc patts =
+       match List.rev patts with
+       | []   -> assert false
+       | h::t -> List.fold_left (fun accu x -> <:patt< [ $x$ :: $accu$ ] >>) <:patt< [ $h$ ] >> t
+
+let tuple_of_expr _loc exprs =
+       match List.rev exprs with
+       | []   -> assert false
+       | h::t -> Ast.ExTup ( _loc, List.fold_left (fun accu x -> <:expr< $x$,$accu$ >>) h t)
+(* BUG? <:expr< ( $exCom_of_list exprs$ ) doesn't work >> *)
+
+let patt_tuple_of_expr _loc patts = 
+       Ast.PaTup (_loc, paCom_of_list patts)
+(* BUG?        <:patt< ( $paCom_of_list patts$ ) doesn't work >> *)
+
+let decompose_variants _loc variant =
+       let rec fn accu = function
+       | <:ctyp< $t$ | $u$ >> -> fn (fn accu t) u
+       | <:ctyp< $uid:id$ of $t$ >> -> (id, Some t) :: accu
+       | <:ctyp< $uid:id$ >> -> (id, None) :: accu
+       | _ -> failwith "decompose_variant"
+       in fn [] variant
+
+let count = ref 0
+let new_id _loc =
+       incr count;
+       let new_id = Printf.sprintf "__x%i__" !count in
+       <:expr< $lid:new_id$ >>, <:patt< $lid:new_id$ >>
+
+(* conversion ML type -> Rpc.Value.t *)
+module Rpc_of_ML = struct
+       
+       let rec value_of_ctyp _loc id = function
+               | <:ctyp< unit >>    -> <:expr< Rpc.String "nil" >>
+               | <:ctyp< int >>     -> <:expr< Rpc.Int $id$ >>
+               | <:ctyp< int32 >>   -> <:expr< Rpc.String (Int32.to_string $id$) >>
+               | <:ctyp< int64 >>   -> <:expr< Rpc.String (Int64.to_string $id$) >>
+               | <:ctyp< float >>   -> <:expr< Rpc.Double $id$ >>
+               | <:ctyp< char >>    -> <:expr< Rpc.String (sprintf "%c" $id$) >>
+               | <:ctyp< string >>  -> <:expr< Rpc.String $id$ >>
+               | <:ctyp< bool >>    -> <:expr< Rpc.Bool $id$ >>
+
+               | <:ctyp< [< $t$ ] >> | <:ctyp< [> $t$ ] >> | <:ctyp< [= $t$ ] >> | <:ctyp< [ $t$ ] >> ->
+                       let decomp = decompose_variants _loc t in
+                       let patterns =
+                               List.map (fun (n, t) ->
+                                       let new_id, new_pid = new_id _loc in
+                                       match t with
+                                       | None -> 
+                                               <:match_case< $uid:n$ -> Rpc.Array [ Rpc.String $str:n$ ] >>
+                                       | Some t ->
+                                               <:match_case< $uid:n$ $new_pid$ -> Rpc.Array [ Rpc.String $str:n$; $value_of_ctyp _loc new_id t$ ] >>
+                                       ) decomp in
+                       let pattern = mcOr_of_list patterns in
+                       <:expr< match $id$ with [ $pattern$ ] >>
+
+               | <:ctyp< option $t$ >> ->
+                       let new_id, new_pid = new_id _loc in
+                       <:expr< match $id$ with [
+                                 Some $new_pid$ -> Rpc.Array [ $value_of_ctyp _loc new_id t$ ]
+                               | None -> Rpc.Array []
+                       ] >> 
+
+               | <:ctyp< $tup:tp$ >> ->
+                       let tys = list_of_ctyp tp [] in
+                       let new_ids = List.map (fun t -> let new_id, new_pid = new_id _loc in (t,new_id, new_pid)) tys in
+                       let exprs = List.map (fun (t,new_id,_) -> value_of_ctyp _loc new_id t) new_ids in
+                       let new_ids_patt = List.map (fun (_,_,new_pid) -> new_pid) new_ids in
+                       <:expr<
+                               let $patt_tuple_of_expr _loc new_ids_patt$ = $id$ in
+                               Rpc.Array $list_of_expr _loc exprs$
+                       >>
+
+               | <:ctyp< list $t$ >> ->
+                       let new_id, new_pid = new_id _loc in
+                       <:expr< Rpc.Array (List.map (fun $new_pid$ -> $value_of_ctyp _loc new_id t$) $id$) >>
+
+               | <:ctyp< array $t$ >> ->
+                       let new_id, new_pid = new_id _loc in
+                       <:expr<
+                               Rpc.Array (Array.to_list (Array.map (fun $new_pid$ -> $value_of_ctyp _loc new_id t$) $id$))
+                       >>
+
+               | <:ctyp< { $t$ } >> ->
+                       let get_name_value (n,ctyp) = <:expr< ($str:n$, $value_of_ctyp _loc <:expr< $lid:n$ >> ctyp$) >> in
+
+                       let fields = list_of_fields _loc t in
+                       let bindings = List.map (fun (f,_) -> <:binding< $lid:f$ = $id$ . $lid:f$ >>) fields in
+                       let final_expr = <:expr< Rpc.Struct $list_of_expr _loc (List.map get_name_value fields)$ >> in
+                       biList_to_expr _loc bindings final_expr
+
+               | <:ctyp< $lid:t$ >> -> <:expr< $lid:"rpc_of_"^t$ $id$ >>
+
+               | _ -> failwith "Rpc_of_ML.value_of_ctyp: type not supported"
+
+       let rpc_of _loc id ctyp =
+               let id = <:expr< $lid:id$ >> in
+               value_of_ctyp _loc id ctyp
+
+       let process _loc id ctyp =
+               function_with_label_args _loc
+                       ~fun_name:("rpc_of_"^id)
+                       ~final_ident:id
+                       ~function_body:(rpc_of _loc id ctyp)
+                       ~return_type:<:ctyp< Rpc.t >>
+                       []
+
+end
+
+(* conversion Rpc.Value.t -> ML type *)
+module ML_of_rpc = struct
+
+       let arg = let _loc = Loc.ghost in <:expr< $lid:"__x__"$ >>
+       let parg = let _loc = Loc.ghost in <:patt< $lid:"__x__"$ >>
+
+       let parse_error expected got =
+               let _loc = Loc.ghost in
+               <:expr< raise (Parse_error( $str:expected^" expected"$, $got$)) >>
+
+       let rec value_of_ctyp _loc id = function
+               | <:ctyp< unit >>   ->
+                       <:expr< match $id$ with [ Rpc.String "nil" -> unit | $parg$ -> $parse_error "String(nil)" arg$ ] >>
+
+               | <:ctyp< int >>    ->
+                       <:expr< match $id$ with [ Rpc.Int x -> x | $parg$ -> $parse_error "Int(int)" arg$ ] >>
+
+               | <:ctyp< int32 >>  ->
+                       <:expr< match $id$ with [ Rpc.String x -> Int32.of_string x | $parg$ -> $parse_error "String(int32)" arg$ ] >>
+
+               | <:ctyp< int64 >>  ->
+                       <:expr< match $id$ with [ Rpc.String x -> Int64.of_string x | $parg$ -> $parse_error "String(int64)" arg$ ] >>
+
+               | <:ctyp< float >>  ->
+                       <:expr< match $id$ with [ Rpc.Double x -> x | $parg$ -> $parse_error "Double(flaot)" arg$ ] >>
+
+               | <:ctyp< char >>   ->
+                       <:expr< match $id$ with [ Rpc.String x -> x.[0] | $parg$ -> $parse_error "Char(string)" arg$ ] >>
+
+               | <:ctyp< string >> ->
+                       <:expr< match $id$ with [ Rpc.String x -> x | $parg$ -> $parse_error "String(string)" arg$ ] >>
+
+               | <:ctyp< bool >>   ->
+                       <:expr< match $id$ with [ Rpc.Bool x -> x | $parg$ -> $parse_error "Bool(bool)" arg$ ] >>
+
+               | <:ctyp< [< $t$ ] >> | <:ctyp< [> $t$ ] >> | <:ctyp< [= $t$ ] >> | <:ctyp< [ $t$ ] >> ->
+                       let decomp = decompose_variants _loc t in
+                       let patterns =
+                               List.map (fun (n,t) ->
+                                       let new_id, new_pid = new_id _loc in
+                                       match t with
+                                       | None ->
+                                               <:match_case< Rpc.Array [ Rpc.String $str:n$ ] ->  $uid:n$ >>
+                                       | Some t ->
+                                               <:match_case< Rpc.Array [ Rpc.String $str:n$; $new_pid$ ] -> $uid:n$ $value_of_ctyp _loc new_id t$ >>
+                                       ) decomp 
+                               @ [ <:match_case< $parg$ -> $parse_error "Array[string;_]" arg$ >> ] in
+                       let pattern = mcOr_of_list patterns in
+                       <:expr< match $id$ with [ $pattern$ ] >>
+
+               | <:ctyp< option $t$ >> ->
+                       let new_id, new_pid = new_id _loc in
+                       <:expr< match $id$ with [
+                                 Rpc.Array [] -> None
+                               | Rpc.Array [$new_pid$] -> Some $value_of_ctyp _loc new_id t$
+                               | $parg$ -> $parse_error "Array[_]" arg$
+                       ] >>
+
+               | <:ctyp< $tup:tp$ >> ->
+                       let tys = list_of_ctyp tp [] in
+                       let new_ids = List.map (fun t -> let new_id, new_pid = new_id _loc in (t,new_id,new_pid)) tys in
+                       let exprs = List.map (fun (t,new_id,mew_pid) -> value_of_ctyp _loc new_id t) new_ids in
+                       let new_ids_patt = List.map (fun (_,_,new_pid) -> new_pid) new_ids in
+                       let new_id, new_pid = new_id _loc in
+                       <:expr< match $id$ with [
+                         Rpc.Array $new_pid$ ->
+                               match $new_id$ with [
+                                 $patt_list_of_expr _loc new_ids_patt$ -> $tuple_of_expr _loc exprs$
+                               | $parg$ -> $parse_error (Printf.sprintf "list of size %i" (List.length tys)) <:expr< Rpc.Array $arg$ >>$ ]
+                       | $parg$ -> $parse_error "Array[_]" arg$
+                       ] >>
+
+               | <:ctyp< list $t$ >> ->
+                       let new_id, new_pid = new_id _loc in
+                       <:expr< match $id$ with [
+                         Rpc.Array $new_pid$ -> 
+                               let __fn__ $parg$ = $value_of_ctyp _loc arg t$ in
+                               List.map __fn__ $new_id$
+                       | $parg$ -> $parse_error "Array[_]" arg$
+                       ] >>
+
+               | <:ctyp< array $t$ >> ->
+                       let new_id, new_pid = new_id _loc in
+                       <:expr< match $id$ with [
+                         Rpc.Array $new_pid$ ->
+                               let __fn__ $parg$ = $value_of_ctyp _loc arg t$ in
+                               Array.of_list (List.map __fn__ $new_id$)
+                       | $parg$ -> $parse_error "Array[_]" arg$
+                       ] >>
+
+               | <:ctyp< { $t$ } >> ->
+                       let new_id, new_pid = new_id _loc in
+                       let fields = list_of_fields _loc t in
+                       let bindings =
+                               List.map (fun (n,ctyp) ->
+                                       <:binding< $lid:n$ =
+                                               let __f__ $parg$ = $value_of_ctyp _loc arg ctyp$ in 
+                                               __f__ (try List.assoc $str:n$ $new_id$ with [ Not_found -> $parse_error ("key "^n) id$ ])
+                                       >>)
+                                       fields in
+                       let record_bindings = List.map (fun (n,_) -> (n,<:expr< $lid:n$ >>)) fields in
+                       let final_expr = record_of_fields _loc record_bindings in
+                       <:expr< match $id$ with [
+                         Rpc.Struct $new_pid$ -> $biList_to_expr _loc bindings final_expr$
+                       | $parg$ -> $parse_error "Struct(_)" arg$
+                       ] >>
+
+               | <:ctyp< $lid:t$ >> -> <:expr< $lid:t^"_of_rpc"$ $id$ >>
+
+               | _ -> failwith "ML_of_rpc.scalar_of_ctyp: unsuported type"
+
+       let of_rpc _loc id ctyp =
+               let id = <:expr< $lid:id$ >> in
+               value_of_ctyp _loc id ctyp
+
+       let process _loc id ctyp =
+               function_with_label_args _loc
+                       ~fun_name:(id^"_of_rpc")
+                       ~final_ident:id
+                       ~function_body:(of_rpc _loc id ctyp)
+                       ~return_type:<:ctyp< $lid:id$ >>
+                       []
+
+end
+
+let process_type_declaration _loc process ctyp =
+       let rec fn ty accu = match ty with
+       | Ast.TyAnd (_loc, tyl, tyr)      -> fn tyl (fn tyr accu)
+       | Ast.TyDcl (_loc, id, _, ty, []) -> process _loc id ty :: accu
+       | _                               -> accu in
+       biAnd_of_list (fn ctyp [])
+
+let () =
+       Pa_type_conv.add_generator "rpc"
+               (fun ctyp ->
+                       let _loc = loc_of_ctyp ctyp in
+                       <:str_item<
+                               exception Parse_error of (string * Rpc.t);
+                               value rec $process_type_declaration _loc Rpc_of_ML.process ctyp$;
+                               value rec $process_type_declaration _loc ML_of_rpc.process ctyp$
+                               >>)
index 11eae13e9718eedff8b78892deec538253f809c3..cb99ab181f6a16311bfef45bf82e59c10ec66d7a 100644 (file)
@@ -1,94 +1,8 @@
-(*
- * Copyright (C) 2006-2009 Citrix Systems Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Lesser General Public License for more details.
- *)
-
-let debug = ref false
-let set_debug x = debug := x
-let get_debug () = !debug
-
-type t =
-       | Int of int64
+(* From http://www.xmlrpc.com/spec *)
+type t = 
+       | Int of int
        | Bool of bool
-       | Float of float
        | String of string
-       | Enum of t list
-       | Dict of (string * t) list
-       | Null
-
-exception Runtime_error of string * t
-exception Runtime_exception of string * string
-
-open Printf
-let map_strings sep fn l = String.concat sep (List.map fn l)
-let rec to_string t = match t with
-       | Int i      -> sprintf "I(%Li)" i
-       | Bool b     -> sprintf "B(%b)" b
-       | Float f    -> sprintf "F(%g)" f
-       | String s   -> sprintf "S(%s)" s
-       | Enum ts    -> sprintf "[%s]" (map_strings ";" to_string ts)
-       | Dict ts    -> sprintf "{%s}" (map_strings ";" (fun (s,t) -> sprintf "%s:%s" s (to_string t)) ts)
-       | Null       -> "N"
-
-
-let rpc_of_t x = x
-let rpc_of_int64 i = Int i
-let rpc_of_int32 i = Int (Int64.of_int32 i)
-let rpc_of_int i = Int (Int64.of_int i)
-let rpc_of_bool b = Bool b
-let rpc_of_float f = Float f
-let rpc_of_string s = String s
-let rpc_of_unit () = Null
-
-let t_of_rpc x = x
-let int64_of_rpc = function
-       | Int i    -> i 
-       | String s -> Int64.of_string s
-       | _ -> failwith "int64_of_rpc"
-let int32_of_rpc = function
-       | Int i    -> Int64.to_int32 i
-       | String s -> Int32.of_string s
-       | _ -> failwith "int32_of_rpc"
-let int_of_rpc = function
-       | Int i    -> Int64.to_int i
-       | String s -> int_of_string s
-       | _ -> failwith "int_of_rpc"
-let bool_of_rpc = function Bool b -> b | _ -> failwith "bool_of_rpc"
-let float_of_rpc = function
-       | Float f  -> f 
-       | String s -> float_of_string s
-       | _ -> failwith "float_of_rpc"
-let string_of_rpc = function String s -> s | _ -> failwith "string_of_rpc"
-let unit_of_rpc = function Null -> () | _ -> failwith "unit_of_rpc"
-
-type callback = string list -> t -> unit
-
-type call = {
-       name: string;
-       params: t list;
-}
-
-let call name params = { name = name; params = params }
-
-let string_of_call call =
-       sprintf "-> %s(%s)" call.name (String.concat "," (List.map to_string call.params))
-
-type response = {
-       success: bool;
-       contents: t;
-}
-
-let string_of_response response =
-       sprintf "<- %s(%s)" (if response.success then "success" else "failure") (to_string response.contents)
-let success v = { success = true; contents = v }
-let failure v = { success = false; contents = v }
+       | Double of float
+       | Struct of (string * t) list
+       | Array of t list
index ceb8c3d12790d4565e6139d8d9bd9a905fdbc4dd..5575a467472f9a222c2b54176cdef0f68303558c 100644 (file)
-(*
- * Copyright (C) 2006-2009 Citrix Systems Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation; version 2.1 only. with the special
- * exception on linking described in file LICENSE.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Lesser General Public License for more details.
- *)
-
 open Printf
-open Rpc
 
 let debug = ref false
 let debug (fmt: ('a, unit, string, unit) format4) : 'a =
        kprintf (fun s -> if !debug then begin print_string s; print_newline (); flush stdout end) fmt
 
 (* marshalling/unmarshalling code *)
-
-(* The XML-RPC is not very clear about what characters can be in a string value ... *)
-let encode s =
-       let n = String.length s in
-       let aux = function
-               | '>'    -> Some "&gt;"
-               | '<'    -> Some "&lt;"
-               | '&'    -> Some "&amp;"
-               | '"'    -> Some "&quot;"
-               | c when (c >= '\x20' && c <= '\xff') || c = '\x09' || c = '\x0a' || c = '\x0d'
-                        -> None
-               | _      -> Some "" in
-       let need_encoding =
-               let b = ref false in
-               let i = ref 0 in
-               while not !b && !i < n-1 do
-                       b := aux s.[ !i ] <> None;
-                       incr i;
-               done;
-               !b in
-       if need_encoding then begin
-               let buf = Buffer.create 0 in
-               let m = ref 0 in
-               for i = 0 to n-1 do
-                       match aux s.[i] with
-                       | None   -> ()
-                       | Some n ->
-                                 Buffer.add_substring buf s !m (i - !m);
-                                 Buffer.add_string buf n;
-                                 m := i + 1
-               done;
-               Buffer.contents buf
-       end else
-               s
-
-let rec add_value f = function
-       | Null ->
-               f "<value><nil/></value>"
-
-       | Int i  ->
-               f "<value>";
-               f (Int64.to_string i);
-               f "</value>"
-
-       | Bool b ->
-               f "<value><boolean>";
-               f (if b then "1" else "0");
-               f "</boolean></value>"
-
-       | Float d ->
-               f "<value><double>";
-               f (Printf.sprintf "%g" d);
-               f "</double></value>"
-
-       | String s ->
-               f "<value>";
-               f (encode s);
-               f "</value>"
-
-       | Enum l ->
-               f "<value><array><data>";
-               List.iter (add_value f) l;
-               f "</data></array></value>"
-
-       | Dict d ->
-               let add_member (name, value) =
-                       f "<member><name>";
-                       f name;
-                       f "</name>";
-                       add_value f value;
-                       f "</member>"
-               in
-               f "<value><struct>";
-               List.iter add_member d;
-               f "</struct></value>"
-
-let to_string x =
-       let buf = Buffer.create 128 in
-       add_value (Buffer.add_string buf) x;
-       Buffer.contents buf
-
-let to_a ~empty ~append x =
-       let buf = empty () in
-       add_value (fun s -> append buf s) x;
-       buf
-
-let string_of_call call =
-       let module B = Buffer in
-       let buf = B.create 1024 in
-       let add = B.add_string buf in
-       add "<?xml version=\"1.0\"?>";
-       add "<methodCall><methodName>";
-       add (encode call.name);
-       add "</methodName><params>";
-       List.iter (fun p ->
-               add "<param>";
-               add (to_string p);
-               add "</param>"
-               ) call.params;
-       add "</params></methodCall>";
-       B.contents buf
-
-let add_response add response =
-       let v = if response.success then
-               Dict [ "Status", String "Success"; "Value", response.contents ]
-       else
-               Dict [ "Status", String "Failure"; "ErrorDescription", response.contents ] in
-       add "<?xml version=\"1.0\"?><methodResponse><params><param>";
-       add (to_string v);
-       add "</param></params></methodResponse>"
-
-let string_of_response response =
-       let module B = Buffer in
-       let buf = B.create 256 in
-       let add = B.add_string buf in
-       add_response add response;
-       B.contents buf
-
-let a_of_response ~empty ~append response =
-       let buf = empty () in
-       let add s = append buf s in
-       add_response add response;
-       buf
-
-exception Parse_error of string * string * Xmlm.input
-
-let debug_input input =
-       let buf = Buffer.create 1024 in
-       let rec aux tags =
-               if not (Xmlm.eoi input) then begin
-                       match Xmlm.input input with
-                       | `El_start ((_,tag),_) ->
-                               Buffer.add_string buf "<";
-                               Buffer.add_string buf tag;
-                               Buffer.add_string buf ">";
-                               aux (tag :: tags)
-                       | `El_end ->
-                               begin match tags with
-                               | []     ->
-                                       Buffer.add_string buf "<?/>";
-                                       aux tags
-                               | h :: t ->
-                                       Buffer.add_string buf "</";
-                                       Buffer.add_string buf h;
-                                       Buffer.add_string buf ">";
-                                       aux t
-                               end
-                       | `Data d ->
-                               Buffer.add_string buf d;
-                               aux tags
-                       | `Dtd _ ->
-                               aux tags end
-       in
-       aux [];
-       Buffer.contents buf
-
-let pretty_string_of_error got expected input =
-       sprintf "Error: got '%s' while '%s' was expected when processing '%s'\n" got expected (debug_input input)
-
-let parse_error got expected input =
-       raise (Parse_error (got, expected, input))
-
-module Parser = struct
-
-       let is_empty s =
-               let is_empty = ref true in
-               for i = 0 to (String.length s - 1)
-               do
-                       if s.[i] <> '\n' && s.[i] <> ' ' && s.[i] <> '\t' then is_empty := false
-               done;
-               !is_empty
-
-       let rec skip_empty input =
-               match Xmlm.peek input with
-               | `Data d when is_empty d -> let _ = Xmlm.input input in skip_empty input
-               | _                       -> ()
-
-       (* Helpers *)
-       let get_data input =
-               match Xmlm.input input with
-               | `Dtd _                -> parse_error "dtd" "data" input
-               | `Data d               -> d
-               | `El_start ((_,tag),_) -> parse_error (sprintf "open_tag(%s)" tag) "data" input
-               | `El_end               -> ""
-
-       let rec open_tag input =
-               match Xmlm.input input with
-               | `Dtd _                  -> open_tag input
-               | `El_start ((_,tag),_)   -> tag
-               | `Data d when is_empty d -> open_tag input
-               | `Data d                 -> parse_error (sprintf "data(%s)" (String.escaped d)) "open_tag" input
-               | `El_end                 -> parse_error "close_tag" "open_tag" input
-
-       let rec close_tag tag input =
-               match Xmlm.input input with
-               | `Dtd _                  -> parse_error "dtd" (sprintf "close_tag(%s)" tag) input
-               | `El_end                 -> ()
-               | `El_start ((_,t),_)     -> parse_error (sprintf "open_tag(%s)" t) (sprintf "close_tag(%s)" tag) input
-               | `Data d when is_empty d -> close_tag tag input
-               | `Data d                 -> parse_error (sprintf "data(%s)" (String.escaped d)) (sprintf "close_tag(%s)" tag) input
-
-       let empty_tag input = function
-               | "string" -> String ""
-               | "array"  -> Enum []
-               | "struct" -> Dict []
-               | "nil"    -> Null
-               | "value"  -> String ""
-               | tag      -> parse_error (sprintf "empty_%s" tag) tag input
-
-       let map_tags f input =
-               let tag = open_tag input in
-               let r = 
-                       if Xmlm.peek input = `El_end then
-                               empty_tag input tag
-                       else
-                               f input tag in
-               close_tag tag input;
-               r
-
-       let map_tag tag f input =
-               let t = open_tag input in
-               if t = tag then begin
-                       let r = f input in
-                       close_tag tag input;
-                       r
-               end else
-                       parse_error (sprintf "open_tag(%s)" t) (sprintf "open_tag(%s)" tag) input
-
-       let name   input   = map_tag "name" get_data input
-       let data   f input = map_tag "data" f input
-       let value  f input =
-               let t = open_tag input in
-               if t = "value" then begin
-                       let r =
-                               match Xmlm.peek input with
-                               | `El_end -> Rpc.String ""
-                               | `Data d ->
-                                       let _ = Xmlm.input input in
-                                       if is_empty d && match Xmlm.peek input with `El_start _ -> true | _ -> false then
-                                               f input
-                                       else
-                                               Rpc.String d
-                               | _       -> f input in
-                       close_tag "value" input;
-                       r
-               end else
-                       parse_error "open_tag(value)" (sprintf "open_tag(%s)" t) input
-
-       let members f input =
-               let g input =
-                       let name  = name input in
-                       let value = f name input in
-                       (name, value) in
-               let r = ref [] in
-               skip_empty input;
-               while Xmlm.peek input <> `El_end do
-                       r := map_tag "member" g input :: !r;
-                       skip_empty input;
-               done;
-               List.rev !r
-
-       (* Constructors *)
-       let make fn ?callback accu data =
-               let r = fn data in
-               match callback with
-               | Some f -> f (List.rev accu) r; r
-               | None   -> r
-
-       let make_null   = make (fun ()   -> Null)
-       let make_int    = make (fun data -> Int (Int64.of_string data))
-       let make_bool   = make (fun data -> Bool (if data = "1" then true else false))
-       let make_float  = make (fun data -> Float (float_of_string data))
-       let make_string = make (fun data -> String data)
-       let make_enum   = make (fun data -> Enum data)
-       let make_dict   = make (fun data -> Dict data)
-
-       (* General parser functions *)
-       let rec of_xml ?callback accu input =
-               try value (map_tags (basic_types ?callback accu)) input
-               with
-                       | Xmlm.Error ((a,b), e) as exn->
-                               eprintf "Characters %i--%i: %s\n%!" a b (Xmlm.error_message e);
-                               raise exn
-                       | e ->
-                               eprintf "%s\n%!" (Printexc.to_string e);
-                               raise e
-
-       and basic_types ?callback accu input = function
-               | "int"
-               | "i4"     -> make_int    ?callback accu (get_data input)
-               | "boolean"-> make_bool   ?callback accu (get_data input)
-               | "double" -> make_float  ?callback accu (get_data input)
-               | "string" -> make_string ?callback accu (get_data input)
-               | "array"  -> make_enum   ?callback accu (data (of_xmls ?callback accu) input)
-               | "struct" -> make_dict   ?callback accu (members (fun name -> of_xml ?callback (name::accu)) input)
-               | "nil"    -> make_null   ?callback accu ()
-               | tag      -> parse_error (sprintf "open_tag(%s)" tag) "open_tag(int/i4/boolean/double/string/array/struct/nil)" input
-
-       and of_xmls ?callback accu input =
-               let r = ref [] in
-               skip_empty input;
-               while Xmlm.peek input <> `El_end do
-                       r := of_xml ?callback accu input :: !r;
-                       skip_empty input;
-               done;
-               List.rev !r
-end
-
-let of_string ?callback str =
-       let input = Xmlm.make_input (`String (0, str)) in
-       begin match Xmlm.peek input with
-       | `Dtd _ -> ignore (Xmlm.input input)
-       | _      -> () end;
-       Parser.of_xml ?callback [] input
-
-let of_a ?callback ~next_char b =
-       let aux () =
-               try 
-                       let c = next_char b in
-                       int_of_char c
-               with _ -> raise End_of_file in
-       let input = Xmlm.make_input (`Fun aux) in
-       Parser.of_xml ?callback [] input
-
-let call_of_string ?callback str =
-       let input = Xmlm.make_input (`String (0, str)) in
-       begin match Xmlm.peek input with
-       | `Dtd _ -> ignore (Xmlm.input input)
-       | _      -> () end;
-       let name = ref "" in
-       let params = ref [] in
-       Parser.map_tag "methodCall" (fun input ->
-               name := Parser.map_tag "methodName" Parser.get_data input;
-               Parser.map_tag "params" (fun input ->
-                       Parser.skip_empty input;
-                       while Xmlm.peek input <> `El_end do
-                               Parser.map_tag "param" (fun input -> params := (Parser.of_xml ?callback [] input) :: !params) input;
-                               Parser.skip_empty input;
-                       done;
-                       ) input
-               ) input; 
-       call !name (List.rev !params)
-
-let response_of_fault ?callback input =
-       Parser.map_tag "fault" (fun input ->
-               match Parser.of_xml ?callback [] input with
-               | Dict d ->
-                       let fault_code = List.assoc "faultCode" d in 
-                       let fault_string = List.assoc "faultString" d in
-                       failure ( Rpc.Enum [ String "fault"; fault_code; fault_string ] )
-               | r      -> parse_error (to_string r) "fault" input
-               ) input
-
-let response_of_success ?callback input =
-       Parser.map_tag "params" (fun input ->
-               Parser.map_tag "param" (fun input ->
-                       match Parser.of_xml ?callback [] input with
-                       | Dict d ->
-                               if List.mem_assoc "Status" d && List.assoc "Status" d = String "Success" && List.mem_assoc "Value" d then
-                                       success (List.assoc "Value" d)
-                               else if List.mem_assoc "Status" d && List.assoc "Status" d = String "Failure" && List.mem_assoc "ErrorDescription" d then
-                                       failure (List.assoc "ErrorDescription" d)
-                               else
-                                       success (Dict d)
-                       | v  -> success v
-                       ) input
-               ) input
-
-let response_of_input ?callback input =
-       begin match Xmlm.peek input with
-       | `Dtd _ -> ignore (Xmlm.input input)
-       | _      -> () end;
-       Parser.map_tag "methodResponse" (fun input ->
-               Parser.skip_empty input;
-               match Xmlm.peek input with
-               | `El_start ((_,"params"),_) -> response_of_success ?callback input
-               | `El_start ((_,"fault"),_)  -> response_of_fault ?callback input
-               | `El_start ((_,tag),_)      -> parse_error (sprintf "open_tag(%s)" tag) "open_tag(fault/params)" input
-               | `Data d                    -> parse_error (String.escaped d) "open_tag(fault/params)" input
-               | `El_end                    -> parse_error "close_tag" "open_tag(fault/params)" input
-               | `Dtd _                     -> parse_error "dtd" "open_tag(fault/params)" input
-               ) input
-
-let response_of_string ?callback str =
-       let input = Xmlm.make_input (`String (0, str)) in
-       response_of_input ?callback input
-
-let response_of_in_channel ?callback chan =
-       let input = Xmlm.make_input (`Channel chan) in
-       response_of_input ?callback input
+let rec to_string = function
+       | Rpc.Int i  -> sprintf "<value><int>%i</int></value>" i
+       | Rpc.Bool b -> sprintf "<value><bool>%b</bool></value>" b
+       | Rpc.String s -> sprintf "<value><string>%s</string></value>" s
+       | Rpc.Double d -> sprintf "<value><double>%f</double></value>" d
+       | Rpc.Array a -> sprintf "<value><array><data>%s</data></array></value>" (String.concat "" (List.map to_string a))
+       | Rpc.Struct f ->
+               let members =
+                       List.map (fun (name, value) -> sprintf "<member><name>%s</name>%s</member>" name (to_string value)) f in
+               sprintf "<value><struct>%s</struct></value>" (String.concat "" members)
+
+exception Parse_error of string * string
+
+let get_child xml =
+       match Xml.children xml with
+       | [x] -> x
+       | _   -> raise (Parse_error ("get_child", Xml.to_string xml))
+
+let get_content xml =
+       debug "Value.get_content(%s)" (Xml.to_string xml);
+       Xml.pcdata (get_child xml)
+
+let rec of_xml xml =
+       match Xml.tag xml with
+       | "value" -> value_of_xml (get_child xml)
+       | x -> raise (Parse_error (x, Xml.to_string xml))
+
+and value_of_xml xml =
+       match Xml.tag xml with
+       | "int" -> Rpc.Int (int_of_string (get_content xml))
+       | "bool" -> Rpc.Bool (bool_of_string (get_content xml))
+       | "string" -> Rpc.String (get_content xml)
+       | "double" -> Rpc.Double (float_of_string (get_content xml))
+       | "struct" -> Rpc.Struct (List.map member_of_xml (Xml.children xml))
+       | "array"  -> data_of_xml (get_child xml)
+       | x -> raise (Parse_error (x, Xml.to_string xml))
+
+and data_of_xml xml =
+       match Xml.tag xml with
+       | "data" -> Rpc.Array  (List.map of_xml (Xml.children xml))
+       | x  -> raise (Parse_error (x, Xml.to_string xml))
+
+and member_of_xml xml =
+       match Xml.tag xml with
+       | "member" ->
+               begin match Xml.children xml with
+               | [name; value] -> name_of_xml name, of_xml value
+               | _   -> raise (Parse_error ("member_of_xml",Xml.to_string xml))
+               end
+       | x -> raise (Parse_error (x, Xml.to_string xml))
+
+and name_of_xml xml =
+       match Xml.tag xml with
+       | "name" ->
+               let data = get_child xml in
+               debug "Value.name_of_xml.data(%s)" (Xml.to_string data);
+               Xml.pcdata data
+       | x -> raise (Parse_error (x, Xml.to_string xml))
+
+let of_string str = of_xml (Xml.parse_string str)