hooks: rustfmt: handle multiple commits

The rustfmt hook was relying on ${PREUPLOAD_FILES} which is only correct
when evaluating HEAD. Update the hook to parse the correct version of
the file.

Before 2.0, rustfmt does not support --check when passing the source via
stdin. Handle this case by comparing the input with the output and
report a command to fix the issue to the user.

Bug: 164111102
Test: Added multiples changes to system/security (typo and incorrect
  indentation). Run pre-upload.py manually.
Change-Id: Ibd1bfcb9e630f0b21abfb1e1feb1248fcddf2236
diff --git a/rh/hooks.py b/rh/hooks.py
index 7c13bf1..42427d5 100644
--- a/rh/hooks.py
+++ b/rh/hooks.py
@@ -792,8 +792,27 @@
         return None
 
     rustfmt = options.tool_path('rustfmt')
-    cmd = [rustfmt] + options.args(('--check', '${PREUPLOAD_FILES}',), filtered)
-    return _check_cmd('rustfmt', project, commit, cmd)
+    cmd = [rustfmt] + options.args((), filtered)
+    ret = []
+    for d in filtered:
+        data = rh.git.get_file_content(commit, d.file)
+        result = _run(cmd, input=data)
+        # If the parsing failed, stdout will contain enough details on the
+        # location of the error.
+        if result.returncode:
+            ret.append(rh.results.HookResult(
+                'rustfmt', project, commit, error=result.stdout,
+                files=(d.file,)))
+            continue
+        # TODO(b/164111102): rustfmt stable does not support --check on stdin.
+        # If no error is reported, compare stdin with stdout.
+        if data != result.stdout:
+            msg = ('To fix, please run: %s' %
+                   rh.shell.cmd_to_str(cmd + [d.file]))
+            ret.append(rh.results.HookResult(
+                'rustfmt', project, commit, error=msg,
+                files=(d.file,)))
+    return ret
 
 
 def check_xmllint(project, commit, _desc, diff, options=None):
diff --git a/rh/hooks_unittest.py b/rh/hooks_unittest.py
index be3a4b3..daa240b 100755
--- a/rh/hooks_unittest.py
+++ b/rh/hooks_unittest.py
@@ -725,8 +725,17 @@
                                ('foo.py',))
 
     def test_rustfmt(self, mock_check, _mock_run):
-        self._test_file_filter(mock_check, rh.hooks.check_rustfmt,
-                               ('foo.rs',))
+        # First call should do nothing as there are no files to check.
+        ret = rh.hooks.check_rustfmt(
+            self.project, 'commit', 'desc', (), options=self.options)
+        self.assertEqual(ret, None)
+        self.assertFalse(mock_check.called)
+
+        # Second call will have some results.
+        diff = [rh.git.RawDiffEntry(file='lib.rs')]
+        ret = rh.hooks.check_rustfmt(
+            self.project, 'commit', 'desc', diff, options=self.options)
+        self.assertNotEqual(ret, None)
 
     def test_xmllint(self, mock_check, _mock_run):
         """Verify the xmllint builtin hook."""