dream-ini 0.1.0

Import Morrowind.ini settings into OpenMW configuration files
Documentation
// SPDX-License-Identifier: GPL-3.0-only

pub(crate) fn dependency_sort(mut source: Vec<(String, Vec<String>)>) -> Vec<String> {
    let mut result = Vec::new();
    while let Some((element, _)) = source.first() {
        let element = element.clone();
        dependency_sort_step(&element, &mut source, &mut result);
    }
    result
}

fn dependency_sort_step(
    element: &str,
    source: &mut Vec<(String, Vec<String>)>,
    result: &mut Vec<String>,
) {
    let Some(index) = source.iter().position(|(name, _)| name == element) else {
        return;
    };
    let (name, dependencies) = source.remove(index);
    for dependency in dependencies {
        dependency_sort_step(&dependency, source, result);
    }
    result.push(name);
}

pub(crate) fn apply_morrowind_expansion_order(files: &mut Vec<String>) {
    if !contains_ignore_ascii_case(files, "Morrowind.esm") {
        return;
    }

    let Some(tribunal_index) = position_ignore_ascii_case(files, "Tribunal.esm") else {
        return;
    };
    let Some(bloodmoon_index) = position_ignore_ascii_case(files, "Bloodmoon.esm") else {
        return;
    };

    if bloodmoon_index < tribunal_index {
        let tribunal = files.remove(tribunal_index);
        let bloodmoon_index = position_ignore_ascii_case(files, "Bloodmoon.esm")
            .expect("Bloodmoon.esm remains present");
        files.insert(bloodmoon_index, tribunal);
    }
}

fn contains_ignore_ascii_case(values: &[String], needle: &str) -> bool {
    position_ignore_ascii_case(values, needle).is_some()
}

fn position_ignore_ascii_case(values: &[String], needle: &str) -> Option<usize> {
    values
        .iter()
        .position(|value| value.eq_ignore_ascii_case(needle))
}