Coverage for lintro / parsers / cargo_deny / cargo_deny_issue.py: 100%

32 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2026-04-03 18:53 +0000

1"""Models for cargo-deny issues.""" 

2 

3from __future__ import annotations 

4 

5from dataclasses import dataclass, field 

6from typing import ClassVar 

7 

8from lintro.parsers.base_issue import BaseIssue 

9 

10 

11@dataclass 

12class CargoDenyIssue(BaseIssue): 

13 """Represents a cargo-deny check issue. 

14 

15 cargo-deny checks for: 

16 - License violations (L codes) 

17 - Security advisories (A codes) 

18 - Banned dependencies (B codes) 

19 - Duplicate dependencies (D codes) 

20 - Source violations (S codes) 

21 

22 Attributes: 

23 DISPLAY_FIELD_MAP: Mapping of display field names to attribute names. 

24 code: Issue code (e.g., L001, A001, B001), empty string if not present. 

25 severity: Severity level (error, warning). 

26 crate_name: Name of the affected crate. 

27 crate_version: Version of the affected crate. 

28 advisory_id: RUSTSEC advisory ID (for security advisories). 

29 advisory_severity: Advisory severity (for security advisories). 

30 patched_versions: List of patched versions (for security advisories). 

31 """ 

32 

33 DISPLAY_FIELD_MAP: ClassVar[dict[str, str]] = { 

34 **BaseIssue.DISPLAY_FIELD_MAP, 

35 "severity": "severity", 

36 "code": "code", 

37 } 

38 

39 code: str = field(default="") 

40 severity: str | None = field(default=None) 

41 crate_name: str | None = field(default=None) 

42 crate_version: str | None = field(default=None) 

43 advisory_id: str | None = field(default=None) 

44 advisory_severity: str | None = field(default=None) 

45 patched_versions: list[str] | None = field(default=None) 

46 

47 def __post_init__(self) -> None: 

48 """Initialize the message field from issue details.""" 

49 if not self.message: 

50 self.message = self._build_message() 

51 

52 def _build_message(self) -> str: 

53 """Build a formatted message from issue details. 

54 

55 Returns: 

56 Formatted message string. 

57 """ 

58 parts: list[str] = [] 

59 

60 if self.crate_name: 

61 crate_info = self.crate_name 

62 if self.crate_version: 

63 crate_info = f"{crate_info}@{self.crate_version}" 

64 parts.append(f"crate {crate_info}") 

65 

66 if self.advisory_id: 

67 advisory_info = self.advisory_id 

68 if self.advisory_severity: 

69 advisory_info = f"{advisory_info} ({self.advisory_severity})" 

70 parts.append(advisory_info) 

71 

72 if self.patched_versions: 

73 parts.append(f"patched in: {', '.join(self.patched_versions)}") 

74 

75 return "; ".join(parts) if parts else ""