from pathlib import Path from typing import NamedTuple from loguru import logger class EnvFile(NamedTuple): env_path: Path config: dict[str, str] env_type: str def __repr__(self) -> str: return f"EnvFile(type={self.env_type})" class DependencyRule(NamedTuple): child: str dependency: str def _get_indices_with_string(in_list: list[EnvFile], string: str) -> list[int]: """returns all indices of items in in_list, where item.env_type matches string""" return [index for index, element in enumerate(in_list) if element.env_type == string] def _swap_item_with_previous(in_list: list[EnvFile], index: int): """swaps item at index N with item at index N-1""" assert index > 0, "cannot swap with negative index" in_list[index], in_list[index - 1] = in_list[index - 1], in_list[index] def is_rule_satisfied(in_list: list[EnvFile], rule: DependencyRule, swap=False) -> bool: """returns if the ordering in in_list is compliant with the given rule if swap=True, some reordering will happen in case of a violated rule""" child_indices = _get_indices_with_string(in_list, rule.child) parent_indices = _get_indices_with_string(in_list, rule.dependency) for child_index in child_indices: for parent_index in parent_indices: if not parent_index < child_index: if swap: _swap_item_with_previous(in_list, parent_index) return False return True def sort_env_files_by_rule(env_list: list[EnvFile], rules: list[DependencyRule]) -> list[EnvFile]: in_list = env_list.copy() for _ in range(10_000): rule_satisfied: list[bool] = [] for rule in rules: rule_satisfied.append(is_rule_satisfied(in_list, rule, swap=True)) if all(rule_satisfied): return in_list logger.error("could not find order that satisfys all rules") raise ValueError