def count_resolved(rules_dir, rule_name, key): """Count entries in resolved rule field""" try: resolved = _rule_resolve_internal(rules_dir, rule_name) print(len(resolved.get(key, []))) except: print(0) def _rule_resolve_internal(rules_dir, rule_name, visited=None): """Internal recursive resolver — returns dict, does not print""" if visited is None: visited = set() if rule_name in visited: raise ValueError(f"Circular dependency detected: {rule_name}") visited.add(rule_name) rule_file = find_rule_file(rules_dir, rule_name) with open(rule_file) as f: rule = json.load(f) merged = { 'allow_ips': [], 'allow_ports': [], 'block_ips': [], 'block_ports': [], 'dns_redirect': False } for base_name in rule.get('extends', []): base = _rule_resolve_internal(rules_dir, base_name, visited.copy()) merged['allow_ips'] += base.get('allow_ips', []) merged['allow_ports'] += base.get('allow_ports', []) merged['block_ips'] += base.get('block_ips', []) merged['block_ports'] += base.get('block_ports', []) if base.get('dns_redirect'): merged['dns_redirect'] = True # Merge own fields — use .get() with defaults for all fields merged['allow_ips'] = list(dict.fromkeys( merged['allow_ips'] + rule.get('allow_ips', []))) merged['allow_ports'] = list(dict.fromkeys( merged['allow_ports'] + rule.get('allow_ports', []))) merged['block_ips'] = list(dict.fromkeys( merged['block_ips'] + rule.get('block_ips', []))) merged['block_ports'] = list(dict.fromkeys( merged['block_ports'] + rule.get('block_ports', []))) if rule.get('dns_redirect', False): merged['dns_redirect'] = True merged['name'] = rule.get('name', rule_name) merged['desc'] = rule.get('desc', '') merged['group'] = rule.get('group', '') merged['extends'] = rule.get('extends', []) return merged