~ubuntu-branches/ubuntu/wily/phabricator/wily

« back to all changes in this revision

Viewing changes to src/parser/ArcanistBaseCommitParser.php

  • Committer: Package Import Robot
  • Author(s): Richard Sellam
  • Date: 2014-11-01 23:20:06 UTC
  • mto: This revision was merged to the branch mainline in revision 4.
  • Revision ID: package-import@ubuntu.com-20141101232006-mvlnp0cil67tsboe
Tags: upstream-0~git20141101/arcanist
Import upstream version 0~git20141101, component arcanist

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
 
 
3
final class ArcanistBaseCommitParser {
 
4
 
 
5
  private $api;
 
6
  private $try;
 
7
  private $verbose = false;
 
8
 
 
9
  public function __construct(ArcanistRepositoryAPI $api) {
 
10
    $this->api = $api;
 
11
    return $this;
 
12
  }
 
13
 
 
14
  private function tokenizeBaseCommitSpecification($raw_spec) {
 
15
    if (!$raw_spec) {
 
16
      return array();
 
17
    }
 
18
 
 
19
    $spec = preg_split('/\s*,\s*/', $raw_spec);
 
20
    $spec = array_filter($spec);
 
21
 
 
22
    foreach ($spec as $rule) {
 
23
      if (strpos($rule, ':') === false) {
 
24
        throw new ArcanistUsageException(
 
25
          "Rule '{$rule}' is invalid, it must have a type and name like ".
 
26
          "'arc:upstream'.");
 
27
      }
 
28
    }
 
29
 
 
30
    return $spec;
 
31
  }
 
32
 
 
33
  private function log($message) {
 
34
    if ($this->verbose) {
 
35
      fwrite(STDERR, $message."\n");
 
36
    }
 
37
  }
 
38
 
 
39
  public function resolveBaseCommit(array $specs) {
 
40
    $specs += array(
 
41
      'runtime' => '',
 
42
      'local'   => '',
 
43
      'project' => '',
 
44
      'user'    => '',
 
45
      'system'  => '',
 
46
    );
 
47
 
 
48
    foreach ($specs as $source => $spec) {
 
49
      $specs[$source] = self::tokenizeBaseCommitSpecification($spec);
 
50
    }
 
51
 
 
52
    $this->try = array(
 
53
      'runtime',
 
54
      'local',
 
55
      'project',
 
56
      'user',
 
57
      'system',
 
58
    );
 
59
 
 
60
    while ($this->try) {
 
61
      $source = head($this->try);
 
62
 
 
63
      if (!idx($specs, $source)) {
 
64
        $this->log("No rules left from source '{$source}'.");
 
65
        array_shift($this->try);
 
66
        continue;
 
67
      }
 
68
 
 
69
      $this->log("Trying rules from source '{$source}'.");
 
70
 
 
71
      $rules = &$specs[$source];
 
72
      while ($rule = array_shift($rules)) {
 
73
        $this->log("Trying rule '{$rule}'.");
 
74
 
 
75
        $commit = $this->resolveRule($rule, $source);
 
76
 
 
77
        if ($commit === false) {
 
78
          // If a rule returns false, it means to go to the next ruleset.
 
79
          break;
 
80
        } else if ($commit !== null) {
 
81
          $this->log("Resolved commit '{$commit}' from rule '{$rule}'.");
 
82
          return $commit;
 
83
        }
 
84
      }
 
85
    }
 
86
 
 
87
    return null;
 
88
  }
 
89
 
 
90
  /**
 
91
   * Handle resolving individual rules.
 
92
   */
 
93
  private function resolveRule($rule, $source) {
 
94
    // NOTE: Returning `null` from this method means "no match".
 
95
    // Returning `false` from this method means "stop current ruleset".
 
96
 
 
97
    list($type, $name) = explode(':', $rule, 2);
 
98
    switch ($type) {
 
99
      case 'literal':
 
100
        return $name;
 
101
      case 'git':
 
102
      case 'hg':
 
103
        return $this->api->resolveBaseCommitRule($rule, $source);
 
104
      case 'arc':
 
105
        return $this->resolveArcRule($rule, $name, $source);
 
106
      default:
 
107
        throw new ArcanistUsageException(
 
108
          "Base commit rule '{$rule}' (from source '{$source}') ".
 
109
          "is not a recognized rule.");
 
110
    }
 
111
  }
 
112
 
 
113
 
 
114
  /**
 
115
   * Handle resolving "arc:*" rules.
 
116
   */
 
117
  private function resolveArcRule($rule, $name, $source) {
 
118
    $name = $this->updateLegacyRuleName($name);
 
119
 
 
120
    switch ($name) {
 
121
      case 'verbose':
 
122
        $this->verbose = true;
 
123
        $this->log('Enabled verbose mode.');
 
124
        break;
 
125
      case 'prompt':
 
126
        $reason = 'it is what you typed when prompted.';
 
127
        $this->api->setBaseCommitExplanation($reason);
 
128
        return phutil_console_prompt('Against which commit?');
 
129
      case 'local':
 
130
      case 'user':
 
131
      case 'project':
 
132
      case 'runtime':
 
133
      case 'system':
 
134
        // Push the other source on top of the list.
 
135
        array_unshift($this->try, $name);
 
136
        $this->log("Switching to source '{$name}'.");
 
137
        return false;
 
138
      case 'yield':
 
139
        // Cycle this source to the end of the list.
 
140
        $this->try[] = array_shift($this->try);
 
141
        $this->log("Yielding processing of rules from '{$source}'.");
 
142
        return false;
 
143
      case 'halt':
 
144
        // Dump the whole stack.
 
145
        $this->try = array();
 
146
        $this->log('Halting all rule processing.');
 
147
        return false;
 
148
      case 'skip':
 
149
        return null;
 
150
      case 'empty':
 
151
      case 'upstream':
 
152
      case 'outgoing':
 
153
      case 'bookmark':
 
154
      case 'amended':
 
155
      case 'this':
 
156
        return $this->api->resolveBaseCommitRule($rule, $source);
 
157
      default:
 
158
        $matches = null;
 
159
        if (preg_match('/^exec\((.*)\)$/', $name, $matches)) {
 
160
          $root = $this->api->getWorkingCopyIdentity()->getProjectRoot();
 
161
          $future = new ExecFuture('%C', $matches[1]);
 
162
          $future->setCWD($root);
 
163
          list($err, $stdout) = $future->resolve();
 
164
          if (!$err) {
 
165
            return trim($stdout);
 
166
          } else {
 
167
            return null;
 
168
          }
 
169
        } else if (preg_match('/^nodiff\((.*)\)$/', $name, $matches)) {
 
170
          return $this->api->resolveBaseCommitRule($rule, $source);
 
171
        }
 
172
 
 
173
        throw new ArcanistUsageException(
 
174
          "Base commit rule '{$rule}' (from source '{$source}') ".
 
175
          "is not a recognized rule.");
 
176
    }
 
177
  }
 
178
 
 
179
  private function updateLegacyRuleName($name) {
 
180
    $updated = array(
 
181
      'global' => 'user',
 
182
      'args'   => 'runtime',
 
183
    );
 
184
    $new_name = idx($updated, $name);
 
185
    if ($new_name) {
 
186
      $this->log("translating legacy name '$name' to '$new_name'");
 
187
      return $new_name;
 
188
    }
 
189
    return $name;
 
190
  }
 
191
 
 
192
}