
# 1. Capture all lines and convert to dot‑flat using Python
regexp: .*
code: <<CODE
!python
import sparrow6lib

lines = sparrow6lib.matched()
original = '\n'.join(lines)

section = ''
flat_lines = []
for line in original.splitlines():
    stripped = line.strip()
    if not stripped or stripped.startswith('#') or stripped.startswith(';'):
        continue
    if line.startswith('[') and line.rstrip().endswith(']'):
        section_raw = line.strip()[1:-1].strip()
        section = section_raw.replace('"', '').replace(' ', '.')
        continue
    if '=' in line and section:
        parts = line.split('=', 1)
        key = parts[0].strip()
        value = parts[1].strip().rstrip(';').strip()
        flat_lines.append(f"{section}.{key} = {value}")

with open('/tmp/bind-flat.conf', 'w') as f:
    f.write('\n'.join(flat_lines))
CODE

source: /tmp/bind-flat.conf

# 2. options.directory – soft check
~regexp: options \. directory \s* \= \s* ( <-[ \= \; ]>+ ) \s* \;?
code: <<CODE
!python
import sparrow6lib
c = sparrow6lib.capture()
if c:
    sparrow6lib.update_state({'directory': c[0]})
CODE
generator: <<CODE
!python
import sparrow6lib
caps = sparrow6lib.captures()
if caps:
    for c in caps:
        val = c[0]
        ok = val != ''
        print(f"assert: {ok} options.directory is '{val}'")
else:
    print("assert: 0 options.directory not found")
CODE

# 3. options.listen-on – soft check, must be 127.0.0.1
~regexp: options \. listen\-on \s* \= \s* \{ \s* ( <-[ } ; ]>+ ) \s* [ \; \s* ]? \} \s* \;?
code: <<CODE
!python
# no state needed
CODE
generator: <<CODE
!python
import sparrow6lib
caps = sparrow6lib.captures()
if caps:
    for c in caps:
        ip = c[0].strip()
        ok = ip == '127.0.0.1'
        print(f"assert: {ok} options.listen-on is '{ip}' (must be 127.0.0.1)")
else:
    print("assert: 0 options.listen-on not defined")
CODE

# 4. zone.<name>.type – soft, collect zone names and types
~regexp: zone \. ( <-[ \= \; ]>+ ) \. type \s* \= \s* ( <-[ \; ]>+ ) \s* \;?
code: <<CODE
!python
import sparrow6lib
state = sparrow6lib.get_state()
state.setdefault('all-zones', [])
state.setdefault('zone-types', {})
for c in sparrow6lib.captures():
    zone = c[0]
    if zone not in state['all-zones']:
        state['all-zones'].append(zone)
    state['zone-types'][zone] = c[1]
sparrow6lib.update_state(state)
CODE
generator: <<CODE
!python
import sparrow6lib
caps = sparrow6lib.captures()
if caps:
    for c in caps:
        zone, ztype = c[0], c[1]
        valid = ztype in ('master', 'slave')
        print(f"assert: {valid} zone {zone} type is '{ztype}'")
else:
    print("assert: 0 no zone type definitions found")
CODE

# 5. zone.<name>.file – soft
~regexp: zone \. ( <-[ \= \; ]>+ ) \. file \s* \= \s* ( <-[ \; ]>+ ) \s* \;?
code: <<CODE
!python
# soft, no state needed
CODE
generator: <<CODE
!python
import sparrow6lib
caps = sparrow6lib.captures()
if caps:
    for c in caps:
        zone, file = c[0], c[1]
        nonempty = file != ''
        print(f"assert: {nonempty} zone {zone} file is '{file}'")
else:
    print("assert: 0 no zone file definitions found")
CODE

# 6. zone.<name>.masters – soft, mark zones having a masters field
~regexp: zone \. ( <-[ \= \; ]>+ ) \. masters \s* \= \s* ( <-[ \; ]>+ ) \s* \;?
code: <<CODE
!python
import sparrow6lib
state = sparrow6lib.get_state()
state.setdefault('masters-zones', [])
for c in sparrow6lib.captures():
    zone = c[0]
    if zone not in state['masters-zones']:
        state['masters-zones'].append(zone)
sparrow6lib.update_state(state)
CODE
generator: <<CODE
!python
import sparrow6lib
caps = sparrow6lib.captures()
if caps:
    for c in caps:
        print(f"assert: 1 zone {c[0]} masters defined")
else:
    print("assert: 0 no zone masters definitions found")
CODE

# 7. Final hard check #1: all zones must be master or slave
generator: <<CODE
!python
import sparrow6lib
state = sparrow6lib.get_state()
zone_types = state.get('zone-types', {})
if zone_types:
    invalid = [z for z, t in zone_types.items() if t not in ('master', 'slave')]
    if invalid:
        print(f"assert: 0 zones with invalid type: {', '.join(invalid)}")
    else:
        print("assert: 1 all zones have type master or slave")
else:
    print("assert: 1 no zones defined")
CODE

# 8. Final hard check #2: all zones must have a masters field
generator: <<CODE
!python
import sparrow6lib
state = sparrow6lib.get_state()
all_zones = state.get('all-zones', [])
masters_zones = state.get('masters-zones', [])
for zone in all_zones:
    if zone in masters_zones:
        print(f"assert: 1 zone {zone} has masters")
    else:
        print(f"assert: 0 zone {zone} missing masters")
CODE
