Source code for validate_actions.domain_model.primitives

"""Primitive building blocks for creating a GHA ast."""
from dataclasses import dataclass, field
from typing import List

from yaml import ScalarToken, Token


[docs] @dataclass class Pos: """Position information for tracking locations in YAML source files. This class provides precise position tracking for AST nodes, enabling accurate error reporting and automatic fixes. Position information includes line number, column number, and character index within the file. Attributes: line: Zero-based line number in the source file col: Zero-based column number within the line idx: Character index from start of file Examples: Creating a position from YAML token: pos = Pos.from_token(token) Manual position creation: pos = Pos(line=5, col=10, idx=125) """ line: int col: int idx: int = 0 # TODO: this is not ideal. should be done properly. Let's see with other fixes
[docs] @classmethod def from_token(cls, token: Token) -> "Pos": """Creates a Pos instance from a PyYAML token. Args: token: PyYAML token containing position information Returns: Pos: Position object with line and column from the token """ return cls(token.start_mark.line, token.start_mark.column)
[docs] @dataclass(frozen=True) class Expression: """Represents a GitHub Actions expression like ${{ context.value }}. Expressions are parsed from strings and broken down into parts for validation. Each expression maintains position information for error reporting. Attributes: pos: Position in the source file where this expression starts string: Raw expression string as it appears in the YAML parts: List of String objects representing parsed parts of the expression Examples: Expression for ${{ github.event.pull_request.number }}: expr = Expression( pos=Pos(5, 10), string="github.event.pull_request.number", parts=[String("github"), String("event"), String("pull_request"), String("number")] ) """ pos: "Pos" string: str parts: List["String"]
[docs] @dataclass class String: """Represents a string value with position metadata and embedded expressions. This is the core string representation used throughout the AST. It preserves the original string content along with precise position information and any GitHub Actions expressions (${{ ... }}) found within the string. Attributes: string: The string value extracted from the YAML token pos: Position of the string in the source file (line and column) expr: List of Expression objects found within this string Examples: Simple string: s = String("hello world", Pos(1, 0)) String with expression: s = String("Hello ${{ github.actor }}", pos, [expr]) From YAML token: s = String.from_token(scalar_token) """ string: str pos: "Pos" expr: List[Expression] = field(default_factory=list)
[docs] @classmethod def from_token(cls, token: ScalarToken) -> "String": """Creates a String instance from a PyYAML ScalarToken. Args: token: ScalarToken containing string value and position information Returns: String: String object with value and position from the token """ return cls(token.value, Pos.from_token(token))
def __eq__(self, other): """Compare only based on string content.""" if isinstance(other, String): return self.string == other.string elif isinstance(other, str): return self.string == other return NotImplemented def __hash__(self): """Hash only based on string content.""" return hash(self.string) def __str__(self): """Ergonomic helper for string representation.""" return self.string def __repr__(self): """String representation for debugging.""" return f"String({self.string!r})"