codekingpro/portable-devtools
115k
1import re2from abc import ABCMeta, abstractmethod3from typing import Iterator, Mapping, Optional, Pattern4 5_posix_variable: Pattern[str] = re.compile(6 r"""7 \$\{8 (?P<name>[^\}:]*)9 (?::-10 (?P<default>[^\}]*)11 )?12 \}13 """,14 re.VERBOSE,15)16 17 18class Atom(metaclass=ABCMeta):19 def __ne__(self, other: object) -> bool:20 result = self.__eq__(other)21 if result is NotImplemented:22 return NotImplemented23 return not result24 25 @abstractmethod26 def resolve(self, env: Mapping[str, Optional[str]]) -> str: ...27 28 29class Literal(Atom):30 def __init__(self, value: str) -> None:31 self.value = value32 33 def __repr__(self) -> str:34 return f"Literal(value={self.value})"35 36 def __eq__(self, other: object) -> bool:37 if not isinstance(other, self.__class__):38 return NotImplemented39 return self.value == other.value40 41 def __hash__(self) -> int:42 return hash((self.__class__, self.value))43 44 def resolve(self, env: Mapping[str, Optional[str]]) -> str:45 return self.value46 47 48class Variable(Atom):49 def __init__(self, name: str, default: Optional[str]) -> None:50 self.name = name51 self.default = default52 53 def __repr__(self) -> str:54 return f"Variable(name={self.name}, default={self.default})"55 56 def __eq__(self, other: object) -> bool:57 if not isinstance(other, self.__class__):58 return NotImplemented59 return (self.name, self.default) == (other.name, other.default)60 61 def __hash__(self) -> int:62 return hash((self.__class__, self.name, self.default))63 64 def resolve(self, env: Mapping[str, Optional[str]]) -> str:65 default = self.default if self.default is not None else ""66 result = env.get(self.name, default)67 return result if result is not None else ""68 69 70def parse_variables(value: str) -> Iterator[Atom]:71 cursor = 072 73 for match in _posix_variable.finditer(value):74 (start, end) = match.span()75 name = match["name"]76 default = match["default"]77 78 if start > cursor:79 yield Literal(value=value[cursor:start])80 81 yield Variable(name=name, default=default)82 cursor = end83 84 length = len(value)85 if cursor < length:86 yield Literal(value=value[cursor:length])87 