chiark / gitweb /
protogen: The bulk of the eclient code generation.
[disorder] / scripts / protocol
index f2a0c80e3a49736671a75703496558e1e1e57149..2362f633831650a0ff1ce7369b4b24f1c9078017 100755 (executable)
@@ -45,6 +45,55 @@ use strict;
 
 our @h = ();
 our @c = ();
+our @ah = ();
+our @ac = ();
+our @missing = ();
+
+# Mapping of return type sequences to eclient callbacks
+our @eclient_return = (
+    ["no_response" => []],
+    ["string_response" => ["string"]],
+    ["string_response" => ["string-raw"]],
+    ["integer_response" => ["integer"]],
+    ["integer_response" => ["boolean"]],
+    ["time_response" => ["time"]],
+    ["pair_integer_response" => ["integer", "integer"]],
+    ["queue_response" => ["queue"]],
+    ["playing_response" => ["queue-one"]],
+    ["list_response" => ["body"]],
+    );
+
+# eclient_response_matces(RETURNS, VARIANT)
+#
+# Return true if VARIANT matches RETURNS
+sub eclient_response_matches {
+    my $returns = shift;
+    my $variant = shift;
+    my $types = $variant->[1];
+    if(scalar @$returns != scalar @$types) { return 0; }
+    for my $n (0 .. $#$returns) {
+       my $return = $returns->[$n];
+       my $type = $return->[0];
+       if($type ne $types->[$n]) { return 0; }
+    }
+    return 1;
+}
+
+# find_eclient_type(RETURNS)
+#
+# Find the result type for an eclient call
+sub find_eclient_response {
+    my $returns = shift;
+    if(!defined $returns) {
+       $returns = [];
+    }
+    for my $variant (@eclient_return) {
+       if(eclient_response_matches($returns, $variant)) {
+           return $variant->[0];
+       }
+    }
+    return undef;
+}
 
 # Write(PATH, LINES)
 #
@@ -193,6 +242,30 @@ sub simple {
         $cmdc =~ s/-/_/g;
     }
     print STDERR "Processing $cmd... ";
+    # C argument types and conversions
+    my @cargs = ();
+    my @conversions = ();
+    for my $arg (@$args) {
+        if($arg->[0] eq 'body' or $arg->[0] eq 'list') {
+            push(@cargs, "disorder__$arg->[0]", $arg->[1], "n$arg->[1]");
+        } elsif($arg->[0] eq 'string') {
+            push(@cargs, $arg->[1]);
+        } elsif($arg->[0] eq 'integer') {
+            push(@cargs, "buf_$arg->[1]");
+            push(@conversions,
+                "  char buf_$arg->[1]\[16];\n",
+                 "  byte_snprintf(buf_$arg->[1], sizeof buf_$arg->[1], \"%ld\", $arg->[1]);\n");
+        } elsif($arg->[0] eq 'time') {
+            push(@cargs, "buf_$arg->[1]");
+            push(@conversions,
+                "  char buf_$arg->[1]\[16];\n",
+                 "  byte_snprintf(buf_$arg->[1], sizeof buf_$arg->[1], \"%lld\", (long long)$arg->[1]);\n");
+        } elsif($arg->[0] eq 'literal') {
+            push(@cargs, "\"$arg->[1]\"");
+        } else {
+            die "$0: unsupported arg type '$arg->[0]' for '$cmd'\n";
+        }
+    }
     # Synchronous C API
     print STDERR "H ";
     push(@h, "/** \@brief $summary\n",
@@ -214,27 +287,8 @@ sub simple {
         join(", ", "disorder_client *c",
                    map(c_in_decl($_), @$args),
                     map(c_out_decl($_), @$returns)),
-        ") {\n");
-    my @cargs = ();
-    for my $arg (@$args) {
-        if($arg->[0] eq 'body' or $arg->[0] eq 'list') {
-            push(@cargs, "disorder_$arg->[0]", $arg->[1], "n$arg->[1]");
-        } elsif($arg->[0] eq 'string') {
-            push(@cargs, $arg->[1]);
-        } elsif($arg->[0] eq 'integer') {
-            push(@cargs, "buf_$arg->[1]");
-            push(@c, "  char buf_$arg->[1]\[16];\n",
-                 "  byte_snprintf(buf_$arg->[1], sizeof buf_$arg->[1], \"%ld\", $arg->[1]);\n");
-        } elsif($arg->[0] eq 'time') {
-            push(@cargs, "buf_$arg->[1]");
-            push(@c, "  char buf_$arg->[1]\[16];\n",
-                 "  byte_snprintf(buf_$arg->[1], sizeof buf_$arg->[1], \"%lld\", (long long)$arg->[1]);\n");
-        } elsif($arg->[0] eq 'literal') {
-            push(@cargs, "\"$arg->[1]\"");
-        } else {
-            die "$0: unsupported arg type '$arg->[0]' for '$cmd'\n";
-        }
-    }
+        ") {\n",
+       @conversions);
     if(!defined $returns or scalar @$returns == 0) {
         # Simple case
        push(@c, "  return disorder_simple(",
@@ -345,7 +399,50 @@ sub simple {
     push(@c, "}\n\n");
 
     # Asynchronous C API
-    # TODO
+    my $variant = find_eclient_response($returns);
+    if(defined $variant) {
+       print STDERR "AH ";
+       push(@ah,
+            "/** \@brief $summary\n",
+            " *\n",
+            " * $detail\n",
+            " *\n",
+            " * \@param c Client\n",
+            " * \@param completed Called upon completion\n",
+            c_param_docs($args),
+            " * \@param v Passed to \@p completed\n",
+            " * \@return 0 if the command was queued successfuly, non-0 on error\n",
+            " */\n",
+            "int disorder_eclient_$cmdc(",
+            join(", ", "disorder_eclient *c",
+                 "disorder_eclient_$variant *completed",
+                 map(c_in_decl($_), @$args),
+                 "void *v"),
+            ");\n\n");
+
+       print STDERR "AC ";
+       push(@ac,
+            "int disorder_eclient_$cmdc(",
+            join(", ", "disorder_eclient *c",
+                 "disorder_eclient_$variant *completed",
+                 map(c_in_decl($_), @$args),
+                 "void *v"),
+            ") {\n",
+            @conversions);
+       push(@ac, "  return simple(",
+            join(", ", 
+                 "c",
+                 "${variant}_opcallback",
+                 "(void (*)())completed",
+                 "v",
+                 "\"$cmd\"",
+                 @cargs,
+                 "(char *)0"),
+            ");\n");
+       push(@ac, "}\n\n");
+    } else {
+       push(@missing, "disorder_eclient_$cmdc");
+    }
 
     # Python API
     # TODO
@@ -392,6 +489,14 @@ push(@h, @generated, @gpl,
 push(@c, @generated, @gpl,
      "\n");
 
+push(@ah, @generated, @gpl,
+     "#ifndef ECLIENT_STUBS_H\n",
+     "#define ECLIENT_STUBS_H\n",
+     "\n");
+
+push(@ac, @generated, @gpl,
+     "\n");
+
 # The protocol ----------------------------------------------------------------
 
 simple("adopt",
@@ -816,7 +921,17 @@ simple(["volume", "get_volume"],
 
 push(@h, "#endif\n");
 
+push(@ah, "#endif\n");
+
 # Write it all out ------------------------------------------------------------
 
 Write("lib/client-stubs.h", \@h);
 Write("lib/client-stubs.c", \@c);
+
+Write("lib/eclient-stubs.h", \@ah);
+Write("lib/eclient-stubs.c", \@ac);
+
+if(scalar @missing) {
+  print "Missing:\n";
+  print map("  $_\n", @missing);
+}