// Enable all clippy lints and enforce, and opt out of individual lints
#![cfg_attr(feature = "cargo-clippy", warn(clippy::cargo, clippy::pedantic, clippy::nursery))]
#![cfg_attr(feature = "cargo-clippy", allow(clippy::must_use_candidate, clippy::non_ascii_literal))]
//
// Force certain lints to be errors
#![deny(unused_must_use)]

use anyhow::Context;
use std::fs::File;
use std::io::Write;

mod char;
mod component;
mod multi_char;
mod uri;

/// <https://blog.cyplo.dev/posts/2018/12/generate-rust-tests-from-data/>
fn main() -> anyhow::Result<()> {
    char::generate_escaped_tests().context("failed to generate escaped tests")?;
    char::generate_mark_tests().context("failed to generate mark tests")?;
    char::generate_p_char_tests().context("failed to generate p_char tests")?;
    char::generate_reserved_tests().context("failed to generate reserved tests")?;
    char::generate_unreserved_tests().context("failed to generate unreserved tests")?;
    char::generate_uri_c_tests().context("failed to generate uri_c tests")?;
    char::generate_uri_c_no_slash_tests().context("failed to generate uri_c_no_slash tests")?;

    multi_char::generate_fragment_tests().context("failed to generate fragment tests")?;
    multi_char::generate_param_tests().context("failed to generate param tests")?;
    multi_char::generate_port_tests().context("failed to generate port tests")?;
    multi_char::generate_query_tests().context("failed to generate query tests")?;
    multi_char::generate_reg_name_tests().context("failed to generate reg_name tests")?;
    multi_char::generate_rel_segment_tests().context("failed to generate rel_segment tests")?;
    multi_char::generate_user_info_tests().context("failed to generate user_info tests")?;

    component::generate_abs_path_tests().context("failed to generate abs_path tests")?;
    component::generate_authority_tests().context("failed to generate authority tests")?;
    component::generate_domain_label_tests().context("failed to generate domain_label tests")?;
    component::generate_hier_part_tests().context("failed to generate hier_part tests")?;
    component::generate_host_tests().context("failed to generate host tests")?;
    component::generate_hostname_tests().context("failed to generate hostname tests")?;
    component::generate_ipv4_address_tests().context("failed to generate ipv4_address tests")?;
    component::generate_net_path_tests().context("failed to generate net_path tests")?;
    component::generate_opaque_part_tests().context("failed to generate opaque_part tests")?;
    component::generate_path_segments_tests().context("failed to generate path_segments tests")?;
    component::generate_rel_path_tests().context("failed to generate rel_path tests")?;
    component::generate_scheme_tests().context("failed to generate scheme tests")?;
    component::generate_segment_tests().context("failed to generate segment tests")?;
    component::generate_server_tests().context("failed to generate server tests")?;
    component::generate_top_label_tests().context("failed to generate top_label tests")?;

    uri::generate_absolute_uri_tests().context("failed to generate absolute_uri tests")?;
    uri::generate_relative_uri_tests().context("failed to generate relative_uri tests")?;
    uri::generate_uri_reference_tests().context("failed to generate uri_reference tests")?;

    generate_boolean_tests().context("failed to generate boolean tests")?;
    generate_nom_tests().context("failed to generate nom tests")?;
    generate_option_tests().context("failed to generate option tests")?;
    generate_result_tests().context("failed to generate result tests")?;

    Ok(())
}

fn get_test_file(filename: &str) -> anyhow::Result<File> {
    let out_dir = std::env::var("OUT_DIR").context("failed to get OUT_DIR")?;
    let destination = std::path::Path::new(&out_dir).join(filename);

    File::create(&destination).context(format!("failed to create {}", filename))
}

fn split_last_delimited_char(s: &str) -> (&str, &str) {
    let len = s.len();
    (&s[0..len - 2], &s[len - 1..])
}

fn generate_boolean_tests() -> anyhow::Result<()> {
    let mut test_file = get_test_file("boolean_tests.rs")?;

    for (line, uri) in include_str!("../resources/tests/valid.txt").lines().enumerate() {
        write!(test_file, include_str!("../resources/tests/boolean.txt"), number = format!("{:03}", line), uri = uri)
            .context(format!("failed to write test boolean::case_{}", line))?;
    }

    Ok(())
}

fn generate_nom_tests() -> anyhow::Result<()> {
    let mut test_file = get_test_file("nom_tests.rs")?;

    for (line, uri) in include_str!("../resources/tests/valid.txt").lines().enumerate() {
        write!(test_file, include_str!("../resources/tests/nom.txt"), number = format!("{:03}", line), uri = uri)
            .context(format!("failed to write test nom::case_{}", line))?;
    }

    Ok(())
}

fn generate_option_tests() -> anyhow::Result<()> {
    let mut test_file = get_test_file("option_tests.rs")?;

    for (line, uri) in include_str!("../resources/tests/valid.txt").lines().enumerate() {
        write!(test_file, include_str!("../resources/tests/option.txt"), number = format!("{:03}", line), uri = uri)
            .context(format!("failed to write test option::case_{}", line))?;
    }

    Ok(())
}

fn generate_result_tests() -> anyhow::Result<()> {
    let mut test_file = get_test_file("result_tests.rs")?;

    for (line, uri) in include_str!("../resources/tests/valid.txt").lines().enumerate() {
        write!(test_file, include_str!("../resources/tests/result.txt"), number = format!("{:03}", line), uri = uri)
            .context(format!("failed to write test result::case_{}", line))?;
    }

    Ok(())
}
