kas-macros 0.17.0

KAS GUI / macros
Documentation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License in the LICENSE-APACHE file or at:
//     https://www.apache.org/licenses/LICENSE-2.0

//! Common parsing code for collections and layouts

use crate::collection::{CellInfo, GridDimensions, NameGenerator};
use syn::parse::{ParseStream, Result};
use syn::{Error, Ident, Token};
use syn::{braced, bracketed};

#[allow(non_camel_case_types)]
mod kw {
    use syn::custom_keyword;

    custom_keyword!(column);
    custom_keyword!(row);
}

pub trait Parser {
    type Output;

    fn parse(input: ParseStream, core_gen: &mut NameGenerator) -> Result<Self::Output>;
}

pub fn parse_list<P: Parser>(
    input: ParseStream,
    core_gen: &mut NameGenerator,
) -> Result<Vec<P::Output>> {
    let mut items = vec![];
    while !input.is_empty() {
        items.push(P::parse(input, core_gen)?);

        if input.is_empty() {
            break;
        }

        let _: Token![,] = input.parse()?;
    }

    Ok(items)
}

pub fn parse_grid<P: Parser>(
    input: ParseStream,
    core_gen: &mut NameGenerator,
) -> Result<(GridDimensions, Vec<CellInfo>, Vec<P::Output>)> {
    let mut dim = GridDimensions::default();
    let mut infos = vec![];
    let mut items = vec![];
    while !input.is_empty() {
        let mut require_comma = true;
        if input.peek2(Token![!]) {
            let lookahead = input.lookahead1();
            if lookahead.peek(kw::column) {
                let _: kw::column = input.parse()?;
                let _: Token![!] = input.parse()?;

                let inner;
                let _ = bracketed!(inner in input);
                let col = dim.cols;
                let mut row = 0;
                while !inner.is_empty() {
                    if let Ok(_) = inner.parse::<Token![_]>() {
                        // empty item
                    } else {
                        let layout = P::parse(&inner, core_gen)?;
                        let cell = CellInfo::new(col, row);
                        dim.update(&cell);
                        infos.push(cell);
                        items.push(layout);
                    }
                    row += 1;

                    if inner.is_empty() {
                        break;
                    }

                    inner.parse::<Token![,]>()?;
                }
            } else if lookahead.peek(kw::row) {
                let _: kw::row = input.parse()?;
                let _: Token![!] = input.parse()?;

                let inner;
                let _ = bracketed!(inner in input);
                let mut col = 0;
                let row = dim.rows;
                while !inner.is_empty() {
                    if let Ok(_) = inner.parse::<Token![_]>() {
                        // empty item
                    } else {
                        let layout = P::parse(&inner, core_gen)?;
                        let cell = CellInfo::new(col, row);
                        dim.update(&cell);
                        infos.push(cell);
                        items.push(layout);
                    }
                    col += 1;

                    if inner.is_empty() {
                        break;
                    }

                    inner.parse::<Token![,]>()?;
                }
            } else {
                let ident: Ident = input.parse()?;
                let tok: Token![!] = input.parse()?;
                let span = ident.span();
                let span = span.join(tok.span).unwrap_or(span);
                return Err(Error::new(span, "expected: `column!` or `row!`"));
            }
        } else {
            let cell = input.parse()?;
            dim.update(&cell);
            let _: Token![=>] = input.parse()?;

            let layout;
            if input.peek(syn::token::Brace) {
                let inner;
                let _ = braced!(inner in input);
                layout = P::parse(&inner, core_gen)?;
                require_comma = false;
            } else {
                layout = P::parse(input, core_gen)?;
            }
            infos.push(cell);
            items.push(layout);
        }

        if input.is_empty() {
            break;
        }

        if let Err(e) = input.parse::<Token![,]>() {
            if require_comma {
                return Err(e);
            }
        }
    }

    Ok((dim, infos, items))
}