Index: pkg/json_rpc_2/lib/src/parameters.dart |
diff --git a/pkg/json_rpc_2/lib/src/parameters.dart b/pkg/json_rpc_2/lib/src/parameters.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e08ef39236bbdadcd66823d0ab79eae6cf638b79 |
--- /dev/null |
+++ b/pkg/json_rpc_2/lib/src/parameters.dart |
@@ -0,0 +1,189 @@ |
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+library json_rpc_2.parameters; |
+ |
+import 'dart:math' as math; |
+ |
+import 'exception.dart'; |
+ |
+/// A wrapper for the parameters to a server method. |
+/// |
+/// JSON-RPC 2.0 allows parameters that are either a list or a map. This class |
+/// provides functions that not only assert that the parameters object is the |
+/// correct type, but also that the expected arguments exist and are themselves |
+/// the correct type. |
+class Parameters { |
+ /// The name of the method that this request called. |
+ final String method; |
+ |
+ /// The underlying value of the parameters. |
Bob Nystrom
2014/03/20 18:25:58
"parameters" -> "parameters object"
nweiz
2014/03/20 22:55:41
Done.
|
+ final value; |
+ |
+ /// The path to this parameters map, or null if this is the top-level |
+ /// parameters map. |
Bob Nystrom
2014/03/20 18:25:58
How about a bit explaining how this is formatted?
nweiz
2014/03/20 22:55:41
Done.
|
+ final String _prefix; |
+ |
+ /// Returns the length of the positional parameters array. |
+ /// |
+ /// If the request passed named parameters rather than positional parameters, |
+ /// this will reject the request. |
+ int get length { |
+ _assertPositional(); |
+ return value.length; |
+ } |
+ |
+ Parameters(this.method, this.value, [this._prefix]); |
+ |
+ /// Returns the parameter named [name]. |
+ /// |
+ /// If there is no parameter named [name] and [orElse] was passed, this will |
+ /// call [orElse] and return its result. If [orElse] was not passed, this will |
+ /// reject the request. |
+ /// |
+ /// If the request passed positional parameters rather than named parameters, |
+ /// this will reject the request. |
+ getNamed(String name, {orElse()}) { |
+ _assertNamed(); |
+ if (value.containsKey(name)) return value[name]; |
+ if (orElse != null) return orElse(); |
+ throw new RpcException.invalidParams('Request for method "$method" is ' |
+ 'missing required parameter "${_prefixName(name)}".'); |
+ } |
+ |
+ /// Returns the numeric parameter named [name]. |
+ /// |
+ /// If there is no parameter named [name] and [orElse] was passed, this will |
+ /// call [orElse] and return its result. If [orElse] was not passed, this will |
+ /// reject the request. |
+ /// |
+ /// If the parameter named [name] is not a number or if the request passed |
+ /// positional parameters rather than named parameters, this will reject the |
+ /// request. |
+ num getNum(String name, {num orElse()}) => |
+ _getTyped(name, 'a number', (value) => value is num, orElse); |
+ |
+ /// Returns the integer parameter named [name]. |
+ /// |
+ /// If there is no parameter named [name] and [orElse] was passed, this will |
+ /// call [orElse] and return its result. If [orElse] was not passed, this will |
+ /// reject the request. |
+ /// |
+ /// If the parameter named [name] is not an integer or if the request passed |
+ /// positional parameters rather than named parameters, this will reject the |
+ /// request. |
+ int getInt(String name, {int orElse()}) => |
+ _getTyped(name, 'an integer', (value) => value is int, orElse); |
Bob Nystrom
2014/03/20 18:25:58
This will do weird things in dart2js. If the value
nweiz
2014/03/20 22:55:41
I thought about only having [getNum], but I think
|
+ |
+ /// Returns the boolean parameter named [name]. |
+ /// |
+ /// If there is no parameter named [name] and [orElse] was passed, this will |
+ /// call [orElse] and return its result. If [orElse] was not passed, this will |
+ /// reject the request. |
+ /// |
+ /// If the parameter named [name] is not a boolean or if the request passed |
+ /// positional parameters rather than named parameters, this will reject the |
+ /// request. |
+ bool getBool(String name, {bool orElse()}) => |
+ _getTyped(name, 'a boolean', (value) => value is bool, orElse); |
+ |
+ /// Returns the string parameter named [name]. |
+ /// |
+ /// If there is no parameter named [name] and [orElse] was passed, this will |
+ /// call [orElse] and return its result. If [orElse] was not passed, this will |
+ /// reject the request. |
+ /// |
+ /// If the parameter named [name] is not a string or if the request passed |
+ /// positional parameters rather than named parameters, this will reject the |
+ /// request. |
+ String getString(String name, {String orElse()}) => |
+ _getTyped(name, 'a string', (value) => value is String, orElse); |
+ |
+ /// Returns the list parameter named [name]. |
+ /// |
+ /// If there is no parameter named [name] and [orElse] was passed, this will |
+ /// call [orElse] and return its result. If [orElse] was not passed, this will |
+ /// reject the request. |
+ /// |
+ /// It's safe to modify the returned list. |
Bob Nystrom
2014/03/20 18:25:58
If we claim to support this, we're stuck with the
nweiz
2014/03/20 22:55:41
Thinking this over more I think it's probably bett
Bob Nystrom
2014/03/21 00:36:02
SGTM.
|
+ /// |
+ /// If the parameter named [name] is not a list or if the request passed |
+ /// positional parameters rather than named parameters, this will reject the |
+ /// request. |
+ List getList(String name, {List orElse()}) => |
+ _getTyped(name, 'an Array', (value) => value is List, orElse).toList(); |
+ |
+ /// Returns the map parameter named [name]. |
+ /// |
+ /// If there is no parameter named [name] and [orElse] was passed, this will |
+ /// call [orElse] and return its result. If [orElse] was not passed, this will |
+ /// reject the request. |
+ /// |
+ /// It's safe to modify the returned map. |
Bob Nystrom
2014/03/20 18:25:58
Ditto.
nweiz
2014/03/20 22:55:41
See above.
|
+ /// |
+ /// If the parameter named [name] is not a map or if the request passed |
+ /// positional parameters rather than named parameters, this will reject the |
+ /// request. |
+ Map getMap(String name, {Map orElse()}) { |
+ var result = _getTyped(name, 'an Object', (value) => value is Map, orElse); |
+ return new Map.from(result); |
+ } |
+ |
+ /// Returns a view of the map parameter named [name] as a [Parameters] object. |
+ /// |
+ /// This can be useful if the request parameters contains nested objects whose |
+ /// structure needs to be validated. |
+ /// |
+ /// If there is no parameter named [name], if the parameter named [name] is |
+ /// not a map, or if the request passed positional parameters rather than |
Bob Nystrom
2014/03/20 18:25:58
Why can't the nested parameter be a list?
nweiz
2014/03/20 22:55:41
N/A
|
+ /// named parameters, this will reject the request. |
+ Parameters getNested(String name) { |
+ var map = getMap(name, orElse: () => {}); |
+ var nested = new Parameters(method, map, _prefixName(name)); |
+ return nested; |
+ } |
+ |
+ /// Returns [index]th positional parameter array. |
Bob Nystrom
2014/03/20 18:25:58
This is confusingly written. How about: "Returns t
nweiz
2014/03/20 22:55:41
N/A
|
+ /// |
+ /// If the request passed named parameters rather than positional parameters, |
+ /// or if there aren't enough positional parameters to return the [index]th, |
Bob Nystrom
2014/03/20 18:25:58
"indexth" -> "indexth one".
nweiz
2014/03/20 22:55:41
N/A
|
+ /// this will reject the request. |
+ getPositional(int index) { |
+ _assertPositional(); |
+ if (index < value.length) return value[index]; |
+ throw new RpcException.invalidParams('Method "$method" requires at least ' |
+ '${index + 1} arguments.'); |
+ } |
+ |
+ /// Get a parameter named [named] that matches [test], or the value of calling |
+ /// [orElse]. |
+ /// |
+ /// [type] is used for the error message. It should begin with an indefinite |
+ /// article. |
+ _getTyped(String name, String type, bool test(value), orElse()) { |
+ var result = getNamed(name, orElse: orElse); |
+ if (!value.containsKey(name)) return result; |
+ if (test(value[name])) return value[name]; |
+ |
+ throw new RpcException.invalidParams('Parameter "${_prefixName(name)}" ' |
+ 'for method "$method" must be $type, but was "${value[name]}".'); |
+ } |
+ |
+ /// Asserts that [value] is a positional argument list. |
+ void _assertPositional() { |
+ if (value is List) return; |
+ throw new RpcException.invalidParams('Parameters for method "$method" ' |
+ 'must be passed by position.'); |
Bob Nystrom
2014/03/20 18:25:58
Handle nesting in the error message, here and belo
nweiz
2014/03/20 22:55:41
Done.
|
+ } |
+ |
+ /// Asserts that [value] is a named argument map. |
+ void _assertNamed() { |
+ if (value is Map) return; |
+ throw new RpcException.invalidParams('Parameters for method "$method" ' |
+ 'must be passed by name.'); |
+ } |
+ |
+ /// Returns [name] with [_prefix] attached. |
+ String _prefixName(String name) => _prefix == null ? name : "$_prefix.$name"; |
+} |