oh_my_toml 0.1.0

Awesome TOML configuration derive macro
Documentation
#![cfg(false)]
#![cfg(test)]

//! Tests for error messages

use std::num::NonZero;

use nonempty::NonEmpty;

mod common;

/// Extracts `"array of 4 string"` text from an error's output:
///
/// ```text
///  × Type mismatch
///   ╭─[test_config.toml:1:8]
/// 1 │ item = 4
///   ·        ┬
///   ·        ╰─┤ expected:
///   ·          │     array of 4 string
///   ·          │ but found:
///   ·          │     int
///   ╰────
/// ```
///
/// Since all error types are different structs, we cannot just
/// programatically access their "details" fields
fn extract_expected_type(s: &str) -> String {
    let m = &s[s.find("expected:").unwrap()..];
    let a = m.lines().find(|line| line.contains("")).unwrap().trim();
    eprintln!("A: {a}");
    let a = a.split_whitespace().skip(2).collect::<Vec<_>>().join(" ");
    a.trim().to_string()
}

macro_rules! check_error_types {
    (
        $toml_value:literal ->
        $(
            $ty:ty => $expected:literal
        )*
    ) => {{
        $(
            let output = extract_expected_type(&report_of_impl_deserialize_item!($toml_value $ty));

            assert_eq!(output, $expected, concat!("Wrong error output\n  type: ", stringify!($ty)));
        )*
    }};
}

#[test]
fn primitives() {
    check_error_types! { "[]" ->
        i8 => "int"
        i16 => "int"
        i32 => "int"
        i64 => "int"
        i128 => "int"
        isize => "int"
        u8 => "positive int"
        u16 => "positive int"
        u32 => "positive int"
        u64 => "positive int"
        u128 => "positive int"
        usize => "positive int"
        f32 => "floating-point number"
        f64 => "floating-point number"
        bool => "bool"
        char => "character"
    }
}

#[test]
fn atomic_numbers() {
    check_error_types! { "[]" ->
        std::sync::atomic::AtomicI8 => "int"
        std::sync::atomic::AtomicI16 => "int"
        std::sync::atomic::AtomicI32 => "int"
        std::sync::atomic::AtomicI64 => "int"
        std::sync::atomic::AtomicIsize => "int"
        std::sync::atomic::AtomicU8 => "positive int"
        std::sync::atomic::AtomicU16 => "positive int"
        std::sync::atomic::AtomicU32 => "positive int"
        std::sync::atomic::AtomicU64 => "positive int"
        std::sync::atomic::AtomicUsize => "positive int"
    }
}

#[test]
fn non_zero() {
    check_error_types! { "[]" ->
        NonZero<i8> => "non-zero int"
        NonZero<i16> => "non-zero int"
        NonZero<i32> => "non-zero int"
        NonZero<i64> => "non-zero int"
        NonZero<i128> => "non-zero int"
        NonZero<isize> => "non-zero int"
        NonZero<u8> => "non-zero positive int"
        NonZero<u16> => "non-zero positive int"
        NonZero<u32> => "non-zero positive int"
        NonZero<u64> => "non-zero positive int"
        NonZero<u128> => "non-zero positive int"
        NonZero<usize> => "non-zero positive int"
    }
}

#[test]
fn network_types() {
    check_error_types! { "[]" ->
        std::net::IpAddr => "IP Address"
        std::net::Ipv4Addr => "IPv4 Address"
        std::net::Ipv6Addr => "IPv6 Address"

        std::net::SocketAddr => "Socket Address"
        std::net::SocketAddrV4 => "Socket Address (v4)"
        std::net::SocketAddrV6 => "Socket Address (v6)"
    }
}

#[test]
fn sequence() {
    check_error_types! { "4" ->
        Vec<i8> => "list of int"
        std::collections::VecDeque<i8> => "list of int"
        std::collections::LinkedList<i8> => "list of int"
        std::collections::BinaryHeap<i8> => "list of int"
        std::collections::BTreeSet<i8> => "list of int"
        std::collections::HashSet<i8> => "list of int"
        NonEmpty<i8> => "non-empty list of int"
    }
}

#[test]
fn strings() {
    check_error_types! { "[]" ->
        String => "string"
        std::path::PathBuf => "path"
        std::ffi::CString => "c-string"
        std::ffi::OsString => "os-string"
    }
}

#[test]
fn toml_values() {
    check_error_types! { "4" ->
        toml_edit::Datetime => "date"
        toml_edit::Array => "list"
        toml_edit::InlineTable => "inline table"
        toml_edit::ArrayOfTables => "array of tables"
        toml_edit::Table => "table"
    }
}

#[test]
fn arrays() {
    check_error_types! { "4" ->
        [i8; 8] => "array of 8 int"
        [[i8; 4]; 8] => "array of 8 array of 4 int"
        Vec<[i8; 4]> => "list of array of 4 int"
    }
}

#[test]
fn tuples() {
    check_error_types! { "4" ->
        (String, i32) => "list: [string, int]"
        (String, String, NonEmpty<[NonEmpty<(i32, std::num::NonZeroUsize)>; 8]>) => "list: [string, string, non-empty list of array of 8 non-empty list of list: [int, non-zero positive int]]"
        (String, Vec<String>, Vec<Vec<String>>) => "list: [string, list of string, list of list of string]"
    }
}