//! > Test expansion of generate_trait with one generic parameter.

//! > test_runner_name
test_expand_plugin(expect_diagnostics: true)

//! > cairo_code
/// Some doc.
#[generate_trait]
impl SomeImpl<T> of SomeTrait<T> {
    fn by_val(self: T) -> T {
        self
    }
    // With some doc.
    fn by_snap(self: @T) -> T {
        self
    }
    fn by_ref(ref a: T) {}
    fn with_mut(mut a: usize) {
        a += 1;
    }
    // With other doc.
    fn multi_val(ref a: T, b: T, c: @T) {}
    fn with_generics<V>(ref a: V, b: Box<T>, c: Box<V>) -> Box<(T, V)> {
        box::into_box((box::unbox(b), box::unbox(c)))
    }
    fn with_nopanic(self: T) -> T nopanic {
        self
    }
    fn with_implicits(self: T) -> T implicits(RangeCheck) {
        self
    }
}

#[generate_trait]
fn not_impl() {}

//! > expanded_cairo_code
/// Some doc.
#[generate_trait]
impl SomeImpl<T> of SomeTrait<T> {
    fn by_val(self: T) -> T {
        self
    }
    // With some doc.
    fn by_snap(self: @T) -> T {
        self
    }
    fn by_ref(ref a: T) {}
    fn with_mut(mut a: usize) {
        a += 1;
    }
    // With other doc.
    fn multi_val(ref a: T, b: T, c: @T) {}
    fn with_generics<V>(ref a: V, b: Box<T>, c: Box<V>) -> Box<(T, V)> {
        box::into_box((box::unbox(b), box::unbox(c)))
    }
    fn with_nopanic(self: T) -> T nopanic {
        self
    }
    fn with_implicits(self: T) -> T implicits(RangeCheck) {
        self
    }
}

#[generate_trait]
fn not_impl() {}
trait SomeTrait<T> {
    fn by_val(self: T) -> T;
    // With some doc.
    fn by_snap(self: @T) -> T;
    fn by_ref(ref a: T);
    fn with_mut(a: usize);
    // With other doc.
    fn multi_val(ref a: T, b: T, c: @T);
    fn with_generics<V>(ref a: V, b: Box<T>, c: Box<V>) -> Box<(T, V)>;
    fn with_nopanic(self: T) -> T nopanic;
    fn with_implicits(self: T) -> T implicits(RangeCheck);
}

//! > expected_diagnostics
error: `generate_trait` may only be applied to `impl`s
 --> test_src/lib.cairo:28:1
#[generate_trait]
^^^^^^^^^^^^^^^^^

//! > ==========================================================================

//! > Test expansion of generate_trait with one generic parameter and custom attributes.

//! > test_runner_name
test_expand_plugin(expect_diagnostics: false)

//! > cairo_code
#[generate_trait(trait_attrs(inline(attr), implicit_precedence(attr2)))]
impl OtherImpl<T> of TraitWithAttrs<T>;

//! > expanded_cairo_code
#[generate_trait(trait_attrs(inline(attr), implicit_precedence(attr2)))]
impl OtherImpl<T> of TraitWithAttrs<T>;
#[inline(attr)]
#[implicit_precedence(attr2)]
trait TraitWithAttrs<T>;

//! > expected_diagnostics

//! > ==========================================================================

//! > Test expansion of generate_trait without generic parameters.

//! > test_runner_name
test_expand_plugin(expect_diagnostics: false)

//! > cairo_code
#[generate_trait]
impl SomeImpl of SomeTrait {
    fn foo() {}
    fn bar(a: usize) -> usize {
        a
    }
}

//! > expanded_cairo_code
#[generate_trait]
impl SomeImpl of SomeTrait {
    fn foo() {}
    fn bar(a: usize) -> usize {
        a
    }
}
trait SomeTrait {
    fn foo();
    fn bar(a: usize) -> usize;
}

//! > expected_diagnostics

//! > ==========================================================================

//! > Test diagnostics of generate_trait.

//! > test_runner_name
test_expand_plugin(expect_diagnostics: true)

//! > cairo_code
#[generate_trait]
impl ImplWithBadTrait<T> of Some::Path::To::Trait<T> {}

#[generate_trait]
impl ImplNotMatchingGenerics<T> of TraitNotMatchingGenerics<S> {}

#[generate_trait]
impl ImplWithUnsupportedAttributeArg<T> of OtherTrait<T> {}

//! > expected_diagnostics
error: Generated trait must have a single element path.
 --> test_src/lib.cairo:2:29
impl ImplWithBadTrait<T> of Some::Path::To::Trait<T> {}
                            ^^^^^^^^^^^^^^^^^^^^^^^^


error: Generated trait must have generic args matching the impl's generic params.
 --> test_src/lib.cairo:5:36
impl ImplNotMatchingGenerics<T> of TraitNotMatchingGenerics<S> {}
                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^

//! > expanded_cairo_code
#[generate_trait]
impl ImplWithBadTrait<T> of Some::Path::To::Trait<T> {}

#[generate_trait]
impl ImplNotMatchingGenerics<T> of TraitNotMatchingGenerics<S> {}

#[generate_trait]
impl ImplWithUnsupportedAttributeArg<T> of OtherTrait<T> {}
trait TraitNotMatchingGenerics<T> {}
trait OtherTrait<T> {}

//! > ==========================================================================

//! > Test expansion of generate_trait with one impl generic parameter.

//! > test_runner_name
test_expand_plugin

//! > cairo_code
trait T {}

#[generate_trait]
impl MyImpl<impl I: T> of MyTrait<I>;

//! > expanded_cairo_code
trait T {}

#[generate_trait]
impl MyImpl<impl I: T> of MyTrait<I>;
trait MyTrait<impl I: T>;

//! > expected_diagnostics

//! > ==========================================================================

//! > Test expansion of generate_trait trait type and const.

//! > test_runner_name
test_expand_plugin

//! > cairo_code
#[generate_trait]
impl MyImpl of MyTrait {
    type MyType = usize;
    const MY_CONST: usize = 42;
    fn foo() {}
}

//! > expanded_cairo_code
#[generate_trait]
impl MyImpl of MyTrait {
    type MyType = usize;
    const MY_CONST: usize = 42;
    fn foo() {}
}
trait MyTrait {
    type MyType;
    const MY_CONST: usize;
    fn foo();
}

//! > expected_diagnostics

//! > ==========================================================================

//! > Test expansion of generate_trait with an associated impl.

//! > test_runner_name
test_expand_plugin

//! > cairo_code
#[generate_trait]
impl MyImpl of MyTrait {
    impl AnImpl = SomeImpl;
}

//! > expanded_cairo_code
#[generate_trait]
impl MyImpl of MyTrait {
    impl AnImpl = SomeImpl;
}
trait MyTrait {
}

//! > expected_diagnostics
error: Only functions, types, and constants are supported in #[generate_trait].
 --> test_src/lib.cairo:3:5
    impl AnImpl = SomeImpl;
    ^^^^^^^^^^^^^^^^^^^^^^^
