mod location {
    use nexus_lang::Location;
    use pretty_assertions::assert_eq;

    #[test]
    fn new() {
        let mut l = Location::new(0, 1, 1);

        assert_eq!(l, Location::default());

        assert_eq!(l.index, 0);
        assert_eq!(l.line, 1);
        assert_eq!(l.column, 1);

        for i in 2..11 {
            l.next_column();

            assert_eq!(l.index, i - 1);
            assert_eq!(l.line, 1);
            assert_eq!(l.column, i);
        }

        for i in 2..11 {
            l.newline();

            assert_eq!(l.index, i + 8);
            assert_eq!(l.line, i);
            assert_eq!(l.column, 1);
        }
    }

    #[test]
    fn from_string() {
        assert_eq!(
            Location::from_string("Hi\nThere\n!!!"),
            Location::new(12, 3, 3)
        );
    }

    #[test]
    fn newline() {
        let mut l = Location::new(12, 3, 3);

        assert_eq!(l.index, 12);
        assert_eq!(l.line, 3);
        assert_eq!(l.column, 3);

        l.newline();

        assert_eq!(l.index, 13);
        assert_eq!(l.line, 4);
        assert_eq!(l.column, 1);
    }

    #[test]
    fn next_column() {
        let mut l = Location::new(12, 3, 3);

        assert_eq!(l.index, 12);
        assert_eq!(l.line, 3);
        assert_eq!(l.column, 3);

        l.next_column();

        assert_eq!(l.index, 13);
        assert_eq!(l.line, 3);
        assert_eq!(l.column, 4);
    }

    #[test]
    fn as_source_offset() {
        let s = "Hi\nThere\n!!!";
        let p = Location::new(11, 3, 3);

        assert_eq!(p.index, p.as_source_offset(s).offset());

        // Unicode test.
        assert_eq!(
            Location::new(0, 1, 5).as_source_offset("ŮñĭçøƋɇ").offset(),
            8
        );

        // Out-of-range validation (points at last byte).
        assert_eq!(Location::new(0, 10, 10).as_source_offset(s).offset(), 12);
    }

    #[test]
    fn with_column_offset() {
        let l = Location::new(12, 3, 3);
        let l2 = l.with_column_offset(2);

        assert_eq!(l2.index, 12);
        assert_eq!(l2.line, 3);
        assert_eq!(l2.column, 5);
    }
}

mod source {
    use miette::NamedSource;
    use nexus_lang::Source;
    use pretty_assertions::assert_eq;

    #[test]
    fn new() {
        let code = "fn main() {}";
        let source = Source::new(&code);

        assert_eq!(source.code(), code);
        assert_eq!(source.name(), None);
    }

    #[test]
    fn new_with_name() {
        let code = "fn main() {}";
        let name = "main.rs";
        let source = Source::new_with_name(&code, &name);

        assert_eq!(source.code(), code);
        assert_eq!(source.name(), Some(name));
    }

    #[test]
    fn with_name() {
        let code = "fn main() {}";
        let source = Source::new(&code);
        let source_named = source.with_name(&"main.rs");

        assert_eq!(source_named.code(), code);
        assert_eq!(source_named.name(), Some("main.rs"));
    }

    #[test]
    fn from_str() {
        let code = "fn main() {}";
        let source: Source = code.into();

        assert_eq!(source.code(), code);
        assert_eq!(source.name(), None);
    }

    #[test]
    fn into_named_source() {
        let code = "fn main() {}";
        let name = "main.rs";
        let source = Source::new_with_name(&code, &name);
        let named_source: NamedSource<String> = source.into();

        assert_eq!(named_source.name(), name);
        assert_eq!(named_source.inner(), code);
    }

    #[test]
    fn into_named_source_without_name() {
        let code = "fn main() {}";
        let source = Source::new(&code);
        let named_source: NamedSource<String> = source.into();

        assert_eq!(named_source.name(), "");
        assert_eq!(named_source.inner(), code);
    }

    #[test]
    fn display() {
        let code = "fn main() {}";
        let source: Source = code.into();

        let _ = format!("{source}");
    }
}
