diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 47e2e0c..fa65a04 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,23 +3,51 @@ name: Build on: [push, pull_request] jobs: - build: - name: ${{ matrix.os }} - ${{ matrix.rust }} + clippy: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest] + rust: [stable, nightly] + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.rust }} + components: clippy + - run: cargo clippy --workspace --no-default-features + - run: cargo clippy --workspace --all-features + + fmt: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest] + rust: [nightly] + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.rust }} + components: rustfmt + - run: cargo fmt --all -- --check + + test: runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] - rust: [stable, nightly, 1.65.0] + rust: [stable, nightly, '1.65.0'] steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - components: clippy, rustfmt - - run: cargo build - - run: cargo test - - run: cargo test --features itoa - working-directory: markup - - run: cargo fmt -- --check - if: ${{ matrix.os == 'ubuntu-latest' }} - - run: cargo clippy --all-targets + - uses: taiki-e/install-action@v2 + with: + tool: cargo-hack + - run: cargo generate-lockfile -Zminimal-versions + env: + RUSTC_BOOTSTRAP: 1 + if: ${{ matrix.rust == '1.65.0' }} + - run: cargo hack test --workspace --feature-powerset --optional-deps diff --git a/markup-proc-macro/src/ast.rs b/markup-proc-macro/src/ast.rs index a88a666..93bd1c4 100644 --- a/markup-proc-macro/src/ast.rs +++ b/markup-proc-macro/src/ast.rs @@ -30,8 +30,7 @@ pub struct Element { pub id: Option, pub classes: Vec, pub attributes: Vec, - pub children: Vec, - pub close: bool, + pub children: Option>, } #[derive(Debug)] diff --git a/markup-proc-macro/src/generate.rs b/markup-proc-macro/src/generate.rs index de8a367..1609043 100644 --- a/markup-proc-macro/src/generate.rs +++ b/markup-proc-macro/src/generate.rs @@ -125,7 +125,6 @@ impl Generate for Element { classes, attributes, children, - close, } = self; stream.raw("<"); stream.expr(name, writer); @@ -203,14 +202,14 @@ impl Generate for Element { } } - stream.raw(">"); - - children.generate(stream, writer); - - if *close { + if let Some(children) = children { + stream.raw(">"); + children.generate(stream, writer); stream.raw(""); + } else { + stream.raw(" />"); } } } diff --git a/markup-proc-macro/src/parse.rs b/markup-proc-macro/src/parse.rs index a3d574a..e836aec 100644 --- a/markup-proc-macro/src/parse.rs +++ b/markup-proc-macro/src/parse.rs @@ -165,21 +165,19 @@ impl Parse for Element { } }; - let (children, close) = { + let children = { let lookahead = input.lookahead1(); if lookahead.peek(syn::token::Semi) { let _: syn::Token![;] = input.parse()?; - (Vec::new(), false) + None } else if lookahead.peek(syn::token::Brace) { let children; syn::braced!(children in input); - (children.parse::>()?.0, true) + let Many(children) = children.parse()?; + Some(children) } else if lookahead.peek(syn::LitStr) { let string = input.parse::()?; - ( - vec![syn::parse_quote_spanned!(string.span() => #string)], - true, - ) + Some(vec![syn::parse_quote_spanned!(string.span() => #string)]) } else { return Err(lookahead.error()); } @@ -191,7 +189,6 @@ impl Parse for Element { classes, attributes, children, - close, }) } } diff --git a/markup/src/lib.rs b/markup/src/lib.rs index d87f171..2bdaf6f 100644 --- a/markup/src/lib.rs +++ b/markup/src/lib.rs @@ -121,7 +121,7 @@ impl Render for Raw { impl RenderAttributeValue for Raw {} #[inline] -pub fn raw(value: impl std::fmt::Display) -> impl Render + RenderAttributeValue { +pub fn raw(value: impl std::fmt::Display) -> impl RenderAttributeValue { Raw(value) } @@ -222,8 +222,10 @@ tuple_impl! { A B C D E F G H } tuple_impl! { A B C D E F G H I } tuple_impl! { A B C D E F G H I J } +pub type RenderFn<'a> = dyn Fn(&mut dyn std::fmt::Write) -> std::fmt::Result + 'a; + pub struct DynRender<'a> { - f: Box std::fmt::Result + 'a>, + f: Box>, } pub fn new<'a, F>(f: F) -> DynRender<'a> diff --git a/markup/tests/tests.rs b/markup/tests/tests.rs index 0ca768d..c58e95d 100644 --- a/markup/tests/tests.rs +++ b/markup/tests/tests.rs @@ -55,7 +55,7 @@ t! { br; } }, - A {} => "

", + A {} => "

", } t! { @@ -100,7 +100,7 @@ t! { } }, A {} => r#"7"#, - B {} => r#"barbazquuxquux"#, + B {} => r#"barbazquuxquux"#, C {} => r#""#, } @@ -132,7 +132,7 @@ t! { br[k = 6]; } }, - A {} => r#"

"#, + A {} => r#"

"#, } t! { @@ -338,8 +338,8 @@ t! { ${name} {} } }, - A {} => r#""#, - B { name: "foo-bar" } => r#""#, + A {} => r#""#, + B { name: "foo-bar" } => r#""#, } mod inner { @@ -358,7 +358,7 @@ t! { t15, { A() { - @for | Ok(x) | Err(x) in vec![Ok(1), Err(2), Ok(3)] { + @for | Ok(x) | Err(x) in [Ok(1), Err(2), Ok(3)] { @x } } diff --git a/ui-tests/fail-1.78/not-render.rs b/ui-tests/fail-1.78/not-render.rs new file mode 100644 index 0000000..d13c0e1 --- /dev/null +++ b/ui-tests/fail-1.78/not-render.rs @@ -0,0 +1,16 @@ +struct NotRender; + +markup::define! { + Layout(name: NotRender) { + @markup::doctype() + html["attr-user"=name] { + body { + strong { "Hello " @name "!" } + } + } + } +} + +fn main() { + +} diff --git a/ui-tests/fail-1.78/not-render.stderr b/ui-tests/fail-1.78/not-render.stderr new file mode 100644 index 0000000..d92da9f --- /dev/null +++ b/ui-tests/fail-1.78/not-render.stderr @@ -0,0 +1,35 @@ +error[E0277]: the trait bound `NotRender: RenderAttributeValue` is not satisfied + --> fail-1.78/not-render.rs:6:26 + | +6 | html["attr-user"=name] { + | ^^^^ the trait `RenderAttributeValue` is not implemented for `NotRender`, which is required by `&NotRender: RenderAttributeValue` + | + = help: the following other types implement trait `RenderAttributeValue`: + bool + char + isize + i8 + i16 + i32 + i64 + i128 + and $N others + = note: required for `&NotRender` to implement `RenderAttributeValue` + +error[E0277]: the trait bound `NotRender: Render` is not satisfied + --> fail-1.78/not-render.rs:8:36 + | +8 | strong { "Hello " @name "!" } + | ^^^^ the trait `Render` is not implemented for `NotRender`, which is required by `&NotRender: Render` + | + = help: the following other types implement trait `Render`: + bool + char + isize + i8 + i16 + i32 + i64 + i128 + and $N others + = note: required for `&NotRender` to implement `Render` diff --git a/ui-tests/src/lib.rs b/ui-tests/src/lib.rs index b2b5415..97fa05a 100644 --- a/ui-tests/src/lib.rs +++ b/ui-tests/src/lib.rs @@ -6,7 +6,9 @@ fn ui() { let fail = trybuild::TestCases::new(); if version.minor <= 65 { fail.compile_fail("fail-1.65/*.rs"); - } else { + } else if version.minor <= 77 { fail.compile_fail("fail-1.72/*.rs"); + } else { + fail.compile_fail("fail-1.78/*.rs"); } }