docs: function parseMorayParameters(args) {
      var cueballopts, uoptions, port;
      var havetarget, havemax, u;
  
      /*
       * Some combination of "srvDomain", "url", or "host" (with optional "port")
       * must be specified.  We check the types here, and we'll check for the
       * presence of various combinations below.
       */
      assert.object(args, 'args');
      assert.optionalString(args.srvDomain, 'args.srvDomain');
      assert.optionalString(args.url, 'args.url');
      assert.optionalString(args.host, 'args.host');
      assert.optionalObject(args.cueballOptions, 'args.cueballOptions');
  
      /*
       * For legacy reasons, "port" may be a string or number.
       */
      assert.ok(typeof (args.port) == 'undefined' ||
          typeof (args.port) == 'string' ||
          typeof (args.port) == 'number',
          'args.port must be a string or number');
  
      /*
       * Process cueball options first, since that's common to each form of input.
       * We'll start with our default set of cueball options and override whatever
       * the user asked for.
       */
      cueballopts = jsprim.deepCopy(dflCueballOptions);
      uoptions = args.cueballOptions;
      if (typeof (uoptions) == 'object' && uoptions !== null) {
          /*
           * It's not allowed to specify cueballOptions with any of the legacy
           * options that we still support.
           */
          assert.ok(typeof (args.connectTimeout) == 'undefined',
              'cannot combine "cueballOptions" with "connectTimeout"');
          assert.ok(typeof (args.dns) == 'undefined',
              'cannot combine "cueballOptions" with "dns"');
          assert.ok(typeof (args.maxConnections) == 'undefined',
              'cannot combine "cueballOptions" with "maxConnections"');
          assert.ok(typeof (args.retry) == 'undefined',
              'cannot combine "cueballOptions" with "retry"');
  
          /*
           * It's not allowed to specify "domain" in cueballOptions.  You're
           * supposed to provide that information via one of the above options.
           */
          assert.equal(typeof (uoptions.domain), 'undefined',
              '"domain" may not be specified in cueballOptions');
  
          /*
           * For "defaultPort", "service", "maxDNSConcurrency", and "resolvers",
           * we'll just take whatever the user provided.
           */
          assert.optionalNumber(uoptions.defaultPort,
              'args.cueballOptions.defaultPort');
          if (typeof (uoptions.defaultPort) == 'number') {
              cueballopts.defaultPort = uoptions.defaultPort;
          }
  
          assert.optionalString(uoptions.service, 'args.cueballOptions.service');
          if (typeof (uoptions.service) == 'string') {
              cueballopts.service = uoptions.service;
          }
  
          assert.optionalNumber(uoptions.maxDNSConcurrency,
              'args.cueballOptions.maxDNSConcurrency');
          if (typeof (uoptions.maxDNSConcurrency) == 'number') {
              cueballopts.maxDNSConcurrency = uoptions.maxDNSConcurrency;
          }
  
          assert.optionalArrayOfString(uoptions.resolvers,
              'args.cueballOptions.resolvers');
          if (typeof (uoptions.resolvers) == 'object' &&
              uoptions.resolvers !== null) {
              cueballopts.resolvers = uoptions.resolvers;
          }
  
          /*
           * For "target" and "maximum", we demand that if the user specify one,
           * then they ought to specify both.  We could relax this and say that if
           * only one is specified, we'll pick a default for the other, but at
           * this point it seems clearer to require the consumer to specify both
           * (and it seems no harder for the them, either).
           */
          assert.optionalNumber(uoptions.target, 'args.cueballOptions.target');
          assert.optionalNumber(uoptions.maximum, 'args.cueballOptions.maximum');
          havetarget = typeof (uoptions.target) == 'number';
          havemax = typeof (uoptions.maximum) == 'number';
          if ((havetarget && !havemax) || (!havetarget && havemax)) {
              throw (new Error(
                  'must specify neither or both of "target" and "maximum"'));
          }
  
          if (havetarget) {
              cueballopts.target = uoptions.target;
              cueballopts.maximum = uoptions.maximum;
          }
  
          /*
           * Similarly, if the caller specified "recovery" at all, we will assume
           * they completely specified what they want and we will not mess with
           * it.
           */
          assert.optionalObject(uoptions.recovery,
              'args.cueballOptions.recovery');
          if (typeof (uoptions.recovery) == 'object' &&
              uoptions.recovery !== null) {
              cueballopts.recovery = jsprim.deepCopy(uoptions.recovery);
          }
      } else {
          /*
           * Many of the client options determine how we configure the cueball
           * module.  For compatibility with pre-cueball clients, we accept the
           * old options and translate them into arguments for cueball.  Modern
           * clients may specify cueball options directly, in which case we demand
           * that they have not specified any of these legacy options.
           */
          populateLegacyOptions(cueballopts, args);
      }
  
      /*
       * Now that we've got the easy properties out of the way, pick apart the
       * options used to configure "domain" and "service".
       */
      if (typeof (args.srvDomain) == 'string') {
          assert.ok(!net.isIP(args.srvDomain),
              'cannot use "srvDomain" with an IP address');
          assert.notEqual(typeof (args.host), 'string',
              'cannot specify "host" with "srvDomain"');
          assert.equal(typeof (args.port), 'undefined',
              'cannot specify "port" with "srvDomain"');
          assert.notEqual(typeof (args.url), 'string',
              'cannot specify "url" with "srvDomain"');
  
          cueballopts.domain = args.srvDomain;
          return ({
              'mode': 'srv',
              'cueballOptions': cueballopts,
              'label': cueballopts.domain
          });
      }
  
      /*
       * If the user didn't specify "srvDomain", then they must have specified a
       * hostname or IP address and a port (possibly falling back to the default
       * port).  In this case, we connect directly to the specified IP (or one of
       * IPs specified by A records associated with the hostname).  We don't want
       * cueball to use SRV records in this mode.  There's not currently a way to
       * disable this, so we set a bogus service name and a very short timeout.
       */
      cueballopts.service = '_moraybogus._tcp';
      assert.strictEqual(cueballopts.recovery.dns_srv.retries, 0);
      cueballopts.recovery.dns_srv.timeout = 1;
      cueballopts.recovery.dns_srv.maxTimeout = 1;
  
      /*
       * This logic mirrors the legacy behavior of createClient, however
       * unnecessarily complicated that was.  Specifically, the desired host comes
       * from "args.host" if present, and otherwise from parsing "args.url".  The
       * desired port comes from "args.port" (as either a string or number) if
       * present, and otherwise the URL as long as "host" wasn't also specified,
       * and otherwise the default port 2020.
       */
      if (typeof (args.host) == 'string') {
          assert.notEqual(typeof (args.url), 'string',
              'cannot specify "host" with "url"');
          cueballopts.domain = args.host;
      } else {
          /* We've already checked this condition early on. */
          assert.strictEqual(typeof (args.url), 'string',
              'at least one of "srvDomain", "url", and "host" must be specified');
          u = url.parse(args.url);
          cueballopts.domain = u.hostname;
          if (u.port !== null) {
              port = u.port;
          }
      }
  
      if (typeof (args.port) == 'number' || typeof (args.port) == 'string') {
          port = args.port;
      }
  
      if (typeof (port) == 'number') {
          cueballopts.defaultPort = port;
      } else if (typeof (port) == 'string') {
          cueballopts.defaultPort = parseInt(port, 10);
          assert.ok(!isNaN(cueballopts.defaultPort), '"port" must be a number');
      }
  
      return ({
          'mode': 'direct',
          'cueballOptions': cueballopts,
          'label': cueballopts.domain + ':' + cueballopts.defaultPort
      });
  }