Coverage for lintro / parsers / rustfmt / rustfmt_parser.py: 94%
34 statements
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
1"""Parser for rustfmt output.
3Rustfmt with --check outputs diff-style format when files need formatting:
4- "Diff in /path/to/file.rs at line N:" followed by diff content
5- Or simply lists files that would be reformatted
7We parse this output to extract file paths and create RustfmtIssue objects.
8"""
10from __future__ import annotations
12import re
14from loguru import logger
16from lintro.parsers.rustfmt.rustfmt_issue import RustfmtIssue
18# Pattern to match "Diff in <file>:<line>:" format (actual rustfmt output)
19_DIFF_IN_RE = re.compile(r"^Diff in (?P<file>.+?):(?P<line>\d+):?$")
21# Pattern to match file paths that would be reformatted
22# cargo fmt -- --check may output just the file path when using certain options
23_FILE_PATH_RE = re.compile(r"^(?P<file>.+\.rs)$")
26def parse_rustfmt_output(output: str | None) -> list[RustfmtIssue]:
27 """Parse rustfmt output into issues.
29 Args:
30 output: Raw stdout/stderr from rustfmt/cargo fmt --check.
32 Returns:
33 List of parsed issues, one per file that needs formatting.
34 """
35 if not output:
36 return []
38 issues: list[RustfmtIssue] = []
39 seen_files: set[str] = set()
41 try:
42 for line in output.splitlines():
43 line = line.strip()
44 if not line:
45 continue
47 # Try to match "Diff in <file> at line <n>:" format
48 m = _DIFF_IN_RE.match(line)
49 if m:
50 file_path = m.group("file")
51 line_num = int(m.group("line"))
53 # Only add first occurrence per file to avoid duplicates
54 if file_path not in seen_files:
55 seen_files.add(file_path)
56 issues.append(
57 RustfmtIssue(
58 file=file_path,
59 line=line_num,
60 column=0,
61 message="File needs formatting",
62 fixable=True,
63 ),
64 )
65 continue
67 # Try to match standalone file paths (some rustfmt output modes)
68 m = _FILE_PATH_RE.match(line)
69 if m:
70 file_path = m.group("file")
71 if file_path not in seen_files:
72 seen_files.add(file_path)
73 issues.append(
74 RustfmtIssue(
75 file=file_path,
76 line=0,
77 column=0,
78 message="File needs formatting",
79 fixable=True,
80 ),
81 )
82 continue
84 except (ValueError, TypeError, AttributeError) as e:
85 logger.debug(f"Error parsing rustfmt output: {e}")
87 return issues