Add management header to hosts files and enhance serialization formatting; update tests to reflect changes.
This commit is contained in:
parent
5a2e0d2623
commit
0ee720c5ef
3 changed files with 163 additions and 6 deletions
|
@ -60,6 +60,8 @@
|
|||
- ✅ **Error handling**: Comprehensive error handling with user feedback
|
||||
- ✅ **Keyboard shortcuts**: All edit mode shortcuts implemented and tested
|
||||
- ✅ **Live testing**: Manual testing confirms all functionality works correctly
|
||||
- ✅ **Human-readable formatting**: Tab-based column alignment with proper spacing
|
||||
- ✅ **Management header**: Automatic addition of management header to hosts files
|
||||
|
||||
### Phase 4: Advanced Edit Features
|
||||
- ❌ **Add new entries**: Create new host entries
|
||||
|
|
|
@ -92,9 +92,12 @@ class HostsParser:
|
|||
"""
|
||||
lines = []
|
||||
|
||||
# Ensure header has management line
|
||||
header_comments = self._ensure_management_header(hosts_file.header_comments)
|
||||
|
||||
# Add header comments
|
||||
if hosts_file.header_comments:
|
||||
for comment in hosts_file.header_comments:
|
||||
if header_comments:
|
||||
for comment in header_comments:
|
||||
if comment.strip():
|
||||
lines.append(f"# {comment}")
|
||||
else:
|
||||
|
@ -118,6 +121,132 @@ class HostsParser:
|
|||
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
def _ensure_management_header(self, header_comments: list) -> list:
|
||||
"""
|
||||
Ensure the header contains the management line with proper formatting.
|
||||
|
||||
Args:
|
||||
header_comments: List of existing header comments
|
||||
|
||||
Returns:
|
||||
List of header comments with management line added if needed
|
||||
"""
|
||||
management_line = "Managed by hosts - https://git.s1q.dev/phg/hosts"
|
||||
|
||||
# Check if management line already exists
|
||||
for comment in header_comments:
|
||||
if "git.s1q.dev/phg/hosts" in comment:
|
||||
return header_comments
|
||||
|
||||
# If no header exists, create default header
|
||||
if not header_comments:
|
||||
return [
|
||||
"#",
|
||||
"Host Database",
|
||||
"",
|
||||
management_line,
|
||||
"#"
|
||||
]
|
||||
|
||||
# Check for enclosing comment patterns
|
||||
enclosing_pattern = self._detect_enclosing_pattern(header_comments)
|
||||
|
||||
if enclosing_pattern:
|
||||
# Insert management line within the enclosing pattern
|
||||
return self._insert_in_enclosing_pattern(header_comments, management_line, enclosing_pattern)
|
||||
else:
|
||||
# No enclosing pattern, append management line
|
||||
result = header_comments.copy()
|
||||
result.append(management_line)
|
||||
return result
|
||||
|
||||
def _detect_enclosing_pattern(self, header_comments: list) -> dict | None:
|
||||
"""
|
||||
Detect if header has enclosing comment patterns like ###, # #, etc.
|
||||
|
||||
Args:
|
||||
header_comments: List of header comments
|
||||
|
||||
Returns:
|
||||
Dictionary with pattern info or None if no pattern detected
|
||||
"""
|
||||
if len(header_comments) < 2:
|
||||
return None
|
||||
|
||||
# Look for matching patterns at start and end, ignoring management line if present
|
||||
first_line = header_comments[0].strip()
|
||||
|
||||
# Find the last line that could be a closing pattern (not the management line)
|
||||
last_pattern_index = -1
|
||||
for i in range(len(header_comments) - 1, -1, -1):
|
||||
line = header_comments[i].strip()
|
||||
if "git.s1q.dev/phg/hosts" not in line:
|
||||
last_pattern_index = i
|
||||
break
|
||||
|
||||
if last_pattern_index <= 0:
|
||||
return None
|
||||
|
||||
last_line = header_comments[last_pattern_index].strip()
|
||||
|
||||
# Check for ### pattern
|
||||
if first_line == "###" and last_line == "###":
|
||||
return {
|
||||
'type': 'triple_hash',
|
||||
'start_index': 0,
|
||||
'end_index': last_pattern_index,
|
||||
'pattern': '###'
|
||||
}
|
||||
|
||||
# Check for # # pattern
|
||||
if first_line == "#" and last_line == "#":
|
||||
return {
|
||||
'type': 'single_hash',
|
||||
'start_index': 0,
|
||||
'end_index': last_pattern_index,
|
||||
'pattern': '#'
|
||||
}
|
||||
|
||||
# Check for other repeating patterns (like ####, #####, etc.)
|
||||
if len(first_line) > 1 and first_line == last_line and all(c == '#' for c in first_line):
|
||||
return {
|
||||
'type': 'repeating_hash',
|
||||
'start_index': 0,
|
||||
'end_index': last_pattern_index,
|
||||
'pattern': first_line
|
||||
}
|
||||
|
||||
return None
|
||||
|
||||
def _insert_in_enclosing_pattern(self, header_comments: list, management_line: str, pattern_info: dict) -> list:
|
||||
"""
|
||||
Insert management line within an enclosing comment pattern.
|
||||
|
||||
Args:
|
||||
header_comments: List of header comments
|
||||
management_line: Management line to insert
|
||||
pattern_info: Information about the enclosing pattern
|
||||
|
||||
Returns:
|
||||
Updated list of header comments
|
||||
"""
|
||||
result = header_comments.copy()
|
||||
|
||||
# Find the best insertion point (before the closing pattern)
|
||||
insert_index = pattern_info['end_index']
|
||||
|
||||
# Look for an empty line before the closing pattern to insert after it
|
||||
# Otherwise, insert right before the closing pattern
|
||||
if insert_index > 1 and header_comments[insert_index - 1].strip() == "":
|
||||
# Insert after the empty line, before closing pattern
|
||||
result.insert(insert_index, management_line)
|
||||
else:
|
||||
# Insert empty line and management line before closing pattern
|
||||
result.insert(insert_index, "")
|
||||
result.insert(insert_index + 1, management_line)
|
||||
|
||||
return result
|
||||
|
||||
def _calculate_column_widths(self, entries: list) -> tuple[int, int]:
|
||||
"""
|
||||
Calculate the maximum width needed for IP and hostname columns.
|
||||
|
|
|
@ -168,7 +168,12 @@ class TestHostsParser:
|
|||
parser = HostsParser()
|
||||
content = parser.serialize(hosts_file)
|
||||
|
||||
expected = """127.0.0.1\tlocalhost
|
||||
expected = """# #
|
||||
# Host Database
|
||||
#
|
||||
# Managed by hosts - https://git.s1q.dev/phg/hosts
|
||||
# #
|
||||
127.0.0.1\tlocalhost
|
||||
192.168.1.1\trouter
|
||||
"""
|
||||
assert content == expected
|
||||
|
@ -198,6 +203,7 @@ class TestHostsParser:
|
|||
|
||||
expected = """# Header comment 1
|
||||
# Header comment 2
|
||||
# Managed by hosts - https://git.s1q.dev/phg/hosts
|
||||
127.0.0.1\tlocalhost\t# Loopback
|
||||
# 10.0.0.1\ttest
|
||||
|
||||
|
@ -211,7 +217,13 @@ class TestHostsParser:
|
|||
parser = HostsParser()
|
||||
content = parser.serialize(hosts_file)
|
||||
|
||||
assert content == "\n"
|
||||
expected = """# #
|
||||
# Host Database
|
||||
#
|
||||
# Managed by hosts - https://git.s1q.dev/phg/hosts
|
||||
# #
|
||||
"""
|
||||
assert content == expected
|
||||
|
||||
def test_write_hosts_file(self):
|
||||
"""Test writing hosts file to disk."""
|
||||
|
@ -226,7 +238,14 @@ class TestHostsParser:
|
|||
# Read back and verify
|
||||
with open(f.name, 'r') as read_file:
|
||||
content = read_file.read()
|
||||
assert content == "127.0.0.1\tlocalhost\n"
|
||||
expected = """# #
|
||||
# Host Database
|
||||
#
|
||||
# Managed by hosts - https://git.s1q.dev/phg/hosts
|
||||
# #
|
||||
127.0.0.1\tlocalhost
|
||||
"""
|
||||
assert content == expected
|
||||
|
||||
os.unlink(f.name)
|
||||
|
||||
|
@ -259,7 +278,14 @@ class TestHostsParser:
|
|||
# Check new content
|
||||
with open(f.name, 'r') as new_file:
|
||||
new_content = new_file.read()
|
||||
assert new_content == "127.0.0.1\tlocalhost\n"
|
||||
expected = """# #
|
||||
# Host Database
|
||||
#
|
||||
# Managed by hosts - https://git.s1q.dev/phg/hosts
|
||||
# #
|
||||
127.0.0.1\tlocalhost
|
||||
"""
|
||||
assert new_content == expected
|
||||
|
||||
# Cleanup
|
||||
os.unlink(backup_path)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue