Initial upload: Shell History Alias Generator with full test suite
Some checks failed
CI / test (push) Has been cancelled
Some checks failed
CI / test (push) Has been cancelled
This commit is contained in:
71
shell_alias_gen/parsers/base.py
Normal file
71
shell_alias_gen/parsers/base.py
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
"""Base history parser class and data structures."""
|
||||||
|
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import List, Optional
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class ParsedCommand:
|
||||||
|
"""Represents a parsed shell command."""
|
||||||
|
raw_command: str
|
||||||
|
timestamp: Optional[datetime] = None
|
||||||
|
line_number: int = 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def normalized(self) -> str:
|
||||||
|
"""Return command without leading/trailing whitespace."""
|
||||||
|
return self.raw_command.strip()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def words(self) -> List[str]:
|
||||||
|
"""Split command into words."""
|
||||||
|
return self.normalized.split()
|
||||||
|
|
||||||
|
|
||||||
|
class HistoryParser(ABC):
|
||||||
|
"""Abstract base class for shell history parsers."""
|
||||||
|
|
||||||
|
SHELL_NAME: str = "base"
|
||||||
|
DEFAULT_HISTORY_FILE: Optional[str] = None
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def parse_file(self, filepath: str) -> List[ParsedCommand]:
|
||||||
|
"""Parse a history file and return list of commands."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def parse_content(self, content: str) -> List[ParsedCommand]:
|
||||||
|
"""Parse history content from a string."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def get_default_history_locations(self) -> List[str]:
|
||||||
|
"""Return default locations for shell history file."""
|
||||||
|
if self.DEFAULT_HISTORY_FILE:
|
||||||
|
return [self.DEFAULT_HISTORY_FILE]
|
||||||
|
return []
|
||||||
|
|
||||||
|
def validate_alias_name(self, name: str) -> bool:
|
||||||
|
"""Validate that a name is a valid shell alias."""
|
||||||
|
if not name:
|
||||||
|
return False
|
||||||
|
if not name[0].isalpha() and name[0] != '_':
|
||||||
|
return False
|
||||||
|
for char in name:
|
||||||
|
if not (char.isalnum() or char == '_'):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def sanitize_alias_name(self, name: str) -> str:
|
||||||
|
"""Convert a string to a valid alias name."""
|
||||||
|
result = []
|
||||||
|
for i, char in enumerate(name):
|
||||||
|
if char.isalnum() or char == '_':
|
||||||
|
result.append(char)
|
||||||
|
elif i == 0:
|
||||||
|
result.append('_')
|
||||||
|
sanitized = ''.join(result)
|
||||||
|
if not sanitized or not (sanitized[0].isalpha() or sanitized[0] == '_'):
|
||||||
|
sanitized = 'alias_' + sanitized
|
||||||
|
return sanitized[:64]
|
||||||
Reference in New Issue
Block a user