Page MenuHomePhabricator

D21092.diff
No OneTemporary

D21092.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -487,6 +487,10 @@
'ArcanistUselessOverridingMethodXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistUselessOverridingMethodXHPASTLinterRuleTestCase.php',
'ArcanistUserAbortException' => 'exception/usage/ArcanistUserAbortException.php',
'ArcanistUserConfigurationSource' => 'config/source/ArcanistUserConfigurationSource.php',
+ 'ArcanistUserRef' => 'ref/user/ArcanistUserRef.php',
+ 'ArcanistUserSymbolHardpointQuery' => 'ref/user/ArcanistUserSymbolHardpointQuery.php',
+ 'ArcanistUserSymbolRef' => 'ref/user/ArcanistUserSymbolRef.php',
+ 'ArcanistUserSymbolRefInspector' => 'ref/user/ArcanistUserSymbolRefInspector.php',
'ArcanistVariableReferenceSpacingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistVariableReferenceSpacingXHPASTLinterRule.php',
'ArcanistVariableReferenceSpacingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistVariableReferenceSpacingXHPASTLinterRuleTestCase.php',
'ArcanistVariableVariableXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistVariableVariableXHPASTLinterRule.php',
@@ -527,6 +531,7 @@
'ConduitClientException' => 'conduit/ConduitClientException.php',
'ConduitClientTestCase' => 'conduit/__tests__/ConduitClientTestCase.php',
'ConduitFuture' => 'conduit/ConduitFuture.php',
+ 'ConduitSearchFuture' => 'conduit/ConduitSearchFuture.php',
'ExecFuture' => 'future/exec/ExecFuture.php',
'ExecFutureTestCase' => 'future/exec/__tests__/ExecFutureTestCase.php',
'ExecPassthruTestCase' => 'future/exec/__tests__/ExecPassthruTestCase.php',
@@ -537,6 +542,7 @@
'FilesystemException' => 'filesystem/FilesystemException.php',
'FilesystemTestCase' => 'filesystem/__tests__/FilesystemTestCase.php',
'Future' => 'future/Future.php',
+ 'FutureAgent' => 'conduit/FutureAgent.php',
'FutureIterator' => 'future/FutureIterator.php',
'FutureIteratorTestCase' => 'future/__tests__/FutureIteratorTestCase.php',
'FuturePool' => 'future/FuturePool.php',
@@ -1455,6 +1461,10 @@
'ArcanistUselessOverridingMethodXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistUserAbortException' => 'ArcanistUsageException',
'ArcanistUserConfigurationSource' => 'ArcanistFilesystemConfigurationSource',
+ 'ArcanistUserRef' => 'ArcanistRef',
+ 'ArcanistUserSymbolHardpointQuery' => 'ArcanistWorkflowHardpointQuery',
+ 'ArcanistUserSymbolRef' => 'ArcanistSymbolRef',
+ 'ArcanistUserSymbolRefInspector' => 'ArcanistRefInspector',
'ArcanistVariableReferenceSpacingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistVariableReferenceSpacingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistVariableVariableXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
@@ -1495,6 +1505,7 @@
'ConduitClientException' => 'Exception',
'ConduitClientTestCase' => 'PhutilTestCase',
'ConduitFuture' => 'FutureProxy',
+ 'ConduitSearchFuture' => 'FutureAgent',
'ExecFuture' => 'PhutilExecutableFuture',
'ExecFutureTestCase' => 'PhutilTestCase',
'ExecPassthruTestCase' => 'PhutilTestCase',
@@ -1505,6 +1516,7 @@
'FilesystemException' => 'Exception',
'FilesystemTestCase' => 'PhutilTestCase',
'Future' => 'Phobject',
+ 'FutureAgent' => 'Future',
'FutureIterator' => array(
'Phobject',
'Iterator',
diff --git a/src/conduit/ConduitSearchFuture.php b/src/conduit/ConduitSearchFuture.php
new file mode 100644
--- /dev/null
+++ b/src/conduit/ConduitSearchFuture.php
@@ -0,0 +1,103 @@
+<?php
+
+final class ConduitSearchFuture
+ extends FutureAgent {
+
+ private $conduitEngine;
+ private $method;
+ private $constraints;
+
+ private $objects = array();
+ private $cursor;
+
+ public function setConduitEngine(ArcanistConduitEngine $conduit_engine) {
+ $this->conduitEngine = $conduit_engine;
+ return $this;
+ }
+
+ public function getConduitEngine() {
+ return $this->conduitEngine;
+ }
+
+ public function setMethod($method) {
+ $this->method = $method;
+ return $this;
+ }
+
+ public function getMethod() {
+ return $this->method;
+ }
+
+ public function setConstraints($constraints) {
+ $this->constraints = $constraints;
+ return $this;
+ }
+
+ public function getConstraints() {
+ return $this->constraints;
+ }
+
+ public function isReady() {
+ if ($this->hasResult()) {
+ return true;
+ }
+
+ $futures = $this->getFutures();
+ $future = head($futures);
+
+ if (!$future) {
+ $future = $this->newFuture();
+ }
+
+ if (!$future->isReady()) {
+ $this->setFutures(array($future));
+ return false;
+ } else {
+ $this->setFutures(array());
+ }
+
+ $result = $future->resolve();
+
+ foreach ($this->readResults($result) as $object) {
+ $this->objects[] = $object;
+ }
+
+ $cursor = idxv($result, array('cursor', 'after'));
+
+ if ($cursor === null) {
+ $this->setResult($this->objects);
+ return true;
+ }
+
+ $this->cursor = $cursor;
+ $future = $this->newFuture();
+ $this->setFutures(array($future));
+
+ return false;
+ }
+
+ private function newFuture() {
+ $engine = $this->getConduitEngine();
+
+ $method = $this->getMethod();
+ $constraints = $this->getConstraints();
+
+ $parameters = array(
+ 'constraints' => $constraints,
+ );
+
+ if ($this->cursor !== null) {
+ $parameters['after'] = (string)$this->cursor;
+ }
+
+ $conduit_call = $engine->newCall($method, $parameters);
+ $conduit_future = $engine->newFuture($conduit_call);
+
+ return $conduit_future;
+ }
+
+ private function readResults(array $data) {
+ return idx($data, 'data');
+ }
+
+}
diff --git a/src/conduit/FutureAgent.php b/src/conduit/FutureAgent.php
new file mode 100644
--- /dev/null
+++ b/src/conduit/FutureAgent.php
@@ -0,0 +1,38 @@
+<?php
+
+abstract class FutureAgent
+ extends Future {
+
+ private $futures = array();
+
+ final protected function setFutures(array $futures) {
+ $this->futures = $futures;
+ }
+
+ final protected function getFutures() {
+ return $this->futures;
+ }
+
+ final public function getReadSockets() {
+ $sockets = array();
+ foreach ($this->getFutures() as $future) {
+ foreach ($future->getReadSockets() as $read_socket) {
+ $sockets[] = $read_socket;
+ }
+ }
+
+ return $sockets;
+ }
+
+ final public function getWriteSockets() {
+ $sockets = array();
+ foreach ($this->getFutures() as $future) {
+ foreach ($future->getWriteSockets() as $read_socket) {
+ $sockets[] = $read_socket;
+ }
+ }
+
+ return $sockets;
+ }
+
+}
diff --git a/src/ref/user/ArcanistUserRef.php b/src/ref/user/ArcanistUserRef.php
new file mode 100644
--- /dev/null
+++ b/src/ref/user/ArcanistUserRef.php
@@ -0,0 +1,33 @@
+<?php
+
+final class ArcanistUserRef
+ extends ArcanistRef {
+
+ private $parameters;
+
+ public function getRefDisplayName() {
+ return pht('User "%s"', $this->getUsername());
+ }
+
+ public static function newFromConduit(array $parameters) {
+ $ref = new self();
+ $ref->parameters = $parameters;
+ return $ref;
+ }
+
+ public static function newFromConduitWhoami(array $parameters) {
+ // NOTE: The "user.whoami" call returns a different structure than
+ // "user.search". Mangle the data so it looks similar.
+
+ $parameters['fields'] = array(
+ 'username' => idx($parameters, 'userName'),
+ );
+
+ return self::newFromConduit($parameters);
+ }
+
+ public function getUsername() {
+ return idxv($this->parameters, array('fields', 'username'));
+ }
+
+}
diff --git a/src/ref/user/ArcanistUserSymbolHardpointQuery.php b/src/ref/user/ArcanistUserSymbolHardpointQuery.php
new file mode 100644
--- /dev/null
+++ b/src/ref/user/ArcanistUserSymbolHardpointQuery.php
@@ -0,0 +1,155 @@
+<?php
+
+final class ArcanistUserSymbolHardpointQuery
+ extends ArcanistWorkflowHardpointQuery {
+
+ public function getHardpoints() {
+ return array(
+ ArcanistUserSymbolRef::HARDPOINT_OBJECT,
+ );
+ }
+
+ protected function canLoadRef(ArcanistRef $ref) {
+ return ($ref instanceof ArcanistUserSymbolRef);
+ }
+
+ public function loadHardpoint(array $refs, $hardpoint) {
+ $id_map = array();
+ $phid_map = array();
+ $username_map = array();
+ $function_map = array();
+
+ foreach ($refs as $key => $ref) {
+ switch ($ref->getSymbolType()) {
+ case ArcanistUserSymbolRef::TYPE_ID:
+ $id_map[$key] = $ref->getSymbol();
+ break;
+ case ArcanistUserSymbolRef::TYPE_PHID:
+ $phid_map[$key] = $ref->getSymbol();
+ break;
+ case ArcanistUserSymbolRef::TYPE_USERNAME:
+ $username_map[$key] = $ref->getSymbol();
+ break;
+ case ArcanistUserSymbolRef::TYPE_FUNCTION:
+ $symbol = $ref->getSymbol();
+ if ($symbol !== 'viewer()') {
+ throw new Exception(
+ pht(
+ 'Only the function "viewer()" is supported.'));
+ }
+ $function_map[$key] = $symbol;
+ break;
+ }
+ }
+
+ $futures = array();
+
+ if ($function_map) {
+ // The only function we support is "viewer()".
+ $function_future = $this->newConduit(
+ 'user.whoami',
+ array());
+
+ $futures[] = $function_future;
+ } else {
+ $function_future = null;
+ }
+
+ if ($id_map) {
+ $id_future = $this->newConduitSearch(
+ 'user.search',
+ array(
+ 'ids' => array_values(array_fuse($id_map)),
+ ));
+
+ $futures[] = $id_future;
+ } else {
+ $id_future = null;
+ }
+
+ if ($phid_map) {
+ $phid_future = $this->newConduitSearch(
+ 'user.search',
+ array(
+ 'phids' => array_values(array_fuse($phid_map)),
+ ));
+
+ $futures[] = $phid_future;
+ } else {
+ $phid_future = null;
+ }
+
+ if ($username_map) {
+ $username_future = $this->newConduitSearch(
+ 'user.search',
+ array(
+ 'usernames' => array_values(array_fuse($username_map)),
+ ));
+
+ $futures[] = $username_future;
+ } else {
+ $username_future = null;
+ }
+
+ yield $this->yieldFutures($futures);
+
+ $result_map = array();
+
+ if ($id_future) {
+ $id_results = $id_future->resolve();
+ $id_results = ipull($id_results, null, 'id');
+
+ foreach ($id_map as $key => $id) {
+ $result_map[$key] = idx($id_results, $id);
+ }
+ }
+
+ if ($phid_future) {
+ $phid_results = $phid_future->resolve();
+ $phid_results = ipull($phid_results, null, 'phid');
+
+ foreach ($phid_map as $key => $phid) {
+ $result_map[$key] = idx($phid_results, $phid);
+ }
+ }
+
+ if ($username_future) {
+ $raw_results = $username_future->resolve();
+
+ $username_results = array();
+ foreach ($raw_results as $raw_result) {
+ $username = idxv($raw_result, array('fields', 'username'));
+ $username_results[$username] = $raw_result;
+ }
+
+ foreach ($username_map as $key => $username) {
+ $result_map[$key] = idx($username_results, $username);
+ }
+ }
+
+ foreach ($result_map as $key => $raw_result) {
+ if ($raw_result === null) {
+ continue;
+ }
+
+ $result_map[$key] = ArcanistUserRef::newFromConduit($raw_result);
+ }
+
+ if ($function_future) {
+ $raw_result = $function_future->resolve();
+
+ if ($raw_result === null) {
+ $function_ref = null;
+ } else {
+ $function_ref = ArcanistUserRef::newFromConduitWhoami($raw_result);
+ }
+
+ foreach ($function_map as $key => $function) {
+ $result_map[$key] = $function_ref;
+ }
+ }
+
+ yield $this->yieldMap($result_map);
+ }
+
+}
diff --git a/src/ref/user/ArcanistUserSymbolRef.php b/src/ref/user/ArcanistUserSymbolRef.php
new file mode 100644
--- /dev/null
+++ b/src/ref/user/ArcanistUserSymbolRef.php
@@ -0,0 +1,56 @@
+<?php
+
+final class ArcanistUserSymbolRef
+ extends ArcanistSymbolRef {
+
+ private $type;
+
+ const TYPE_ID = 'id';
+ const TYPE_PHID = 'phid';
+ const TYPE_USERNAME = 'username';
+ const TYPE_FUNCTION = 'function';
+
+ public function getRefDisplayName() {
+ return pht('User Symbol "%s"', $this->getSymbol());
+ }
+
+ public function getSymbolType() {
+ return $this->type;
+ }
+
+ protected function resolveSymbol($symbol) {
+ $matches = null;
+
+ $is_id = preg_match('/^([1-9]\d*)\z/', $symbol, $matches);
+ if ($is_id) {
+ $this->type = self::TYPE_ID;
+ return (int)$matches[1];
+ }
+
+ $is_phid = preg_match('/^PHID-USER-\S+\z/', $symbol, $matches);
+ if ($is_phid) {
+ $this->type = self::TYPE_PHID;
+ return $matches[0];
+ }
+
+ $is_function = preg_match('/^\S+\(\s*\)\s*\z/', $symbol, $matches);
+ if ($is_function) {
+ $this->type = self::TYPE_FUNCTION;
+ return $matches[0];
+ }
+
+ $is_username = preg_match('/^@?(\S+)\z/', $symbol, $matches);
+ if ($is_username) {
+ $this->type = self::TYPE_USERNAME;
+ return $matches[1];
+ }
+
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'The format of user symbol "%s" is unrecognized. Expected a '.
+ 'username like "alice" or "@alice", or a user PHID, or a user '.
+ 'ID, or a special function like "viewer()".',
+ $symbol));
+ }
+
+}
diff --git a/src/ref/user/ArcanistUserSymbolRefInspector.php b/src/ref/user/ArcanistUserSymbolRefInspector.php
new file mode 100644
--- /dev/null
+++ b/src/ref/user/ArcanistUserSymbolRefInspector.php
@@ -0,0 +1,22 @@
+<?php
+
+final class ArcanistUserSymbolRefInspector
+ extends ArcanistRefInspector {
+
+ public function getInspectFunctionName() {
+ return 'user';
+ }
+
+ public function newInspectRef(array $argv) {
+ if (count($argv) !== 1) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'Expected exactly one argument to "user(...)" with a '.
+ 'user symbol.'));
+ }
+
+ return id(new ArcanistUserSymbolRef())
+ ->setSymbol($argv[0]);
+ }
+
+}
diff --git a/src/toolset/query/ArcanistWorkflowHardpointQuery.php b/src/toolset/query/ArcanistWorkflowHardpointQuery.php
--- a/src/toolset/query/ArcanistWorkflowHardpointQuery.php
+++ b/src/toolset/query/ArcanistWorkflowHardpointQuery.php
@@ -51,13 +51,35 @@
abstract protected function canLoadRef(ArcanistRef $ref);
- final public function yieldConduit($method, array $parameters) {
+ final public function newConduitSearch($method, $constraints) {
+ $conduit_engine = $this->getWorkflow()
+ ->getConduitEngine();
+
+ $conduit_future = id(new ConduitSearchFuture())
+ ->setConduitEngine($conduit_engine)
+ ->setMethod($method)
+ ->setConstraints($constraints);
+
+ return $conduit_future;
+ }
+
+ final public function yieldConduitSearch($method, $constraints) {
+ $conduit_future = $this->newConduitSearch($method, $constraints);
+ return $this->yieldFuture($conduit_future);
+ }
+
+ final public function newConduit($method, $parameters) {
$conduit_engine = $this->getWorkflow()
->getConduitEngine();
$call_object = $conduit_engine->newCall($method, $parameters);
$call_future = $conduit_engine->newFuture($call_object);
+ return $call_future;
+ }
+
+ final public function yieldConduit($method, array $parameters) {
+ $call_future = $this->newConduit($method, $parameters);
return $this->yieldFuture($call_future);
}
diff --git a/src/workflow/ArcanistInspectWorkflow.php b/src/workflow/ArcanistInspectWorkflow.php
--- a/src/workflow/ArcanistInspectWorkflow.php
+++ b/src/workflow/ArcanistInspectWorkflow.php
@@ -53,7 +53,7 @@
$all_refs = array();
foreach ($objects as $description) {
$matches = null;
- $pattern = '/^([\w-]+)(?:\(([^)]+)\))?\z/';
+ $pattern = '/^([\w-]+)(?:\((.*)\))?\z/';
if (!preg_match($pattern, $description, $matches)) {
throw new PhutilArgumentUsageException(
pht(

File Metadata

Mime Type
text/plain
Expires
Sun, May 12, 5:45 AM (3 w, 4 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6290226
Default Alt Text
D21092.diff (15 KB)

Event Timeline