Skip to content

Commit

Permalink
Add cell_collection! macro
Browse files Browse the repository at this point in the history
  • Loading branch information
dhardy committed Feb 22, 2024
1 parent 2730a3b commit f575171
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 2 deletions.
2 changes: 1 addition & 1 deletion crates/kas-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub use crate::core::*;
pub use action::Action;
pub use decorations::Decorations;
pub use kas_macros::{autoimpl, extends, impl_default, widget};
pub use kas_macros::{collection, impl_anon, impl_scope, widget_index};
pub use kas_macros::{cell_collection, collection, impl_anon, impl_scope, widget_index};
#[doc(inline)] pub use popup::Popup;
#[doc(inline)] pub(crate) use popup::PopupDescriptor;
#[doc(inline)]
Expand Down
86 changes: 85 additions & 1 deletion crates/kas-macros/src/collection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
use proc_macro2::{Span, TokenStream as Toks};
use quote::{quote, quote_spanned, ToTokens, TokenStreamExt};
use syn::parenthesized;
use syn::parse::{Error, Parse, ParseStream, Result};
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::token::Comma;
use syn::{braced, parenthesized};
use syn::{Expr, Ident, Lifetime, LitInt, LitStr, Token};

#[derive(Debug)]
Expand Down Expand Up @@ -190,6 +190,7 @@ impl Item {
}

pub struct Collection(Vec<Item>);
pub struct CellCollection(Vec<CellInfo>, Collection);

impl Parse for Collection {
fn parse(inner: ParseStream) -> Result<Self> {
Expand All @@ -210,6 +211,44 @@ impl Parse for Collection {
}
}

impl Parse for CellCollection {
fn parse(inner: ParseStream) -> Result<Self> {
let mut gen = NameGenerator::default();

let mut cells = vec![];
let mut items = vec![];
while !inner.is_empty() {
cells.push(inner.parse()?);
let _: Token![=>] = inner.parse()?;

let item;
let require_comma;
if inner.peek(syn::token::Brace) {
let inner2;
let _ = braced!(inner2 in inner);
item = Item::parse(&inner2, &mut gen)?;
require_comma = false;
} else {
item = Item::parse(inner, &mut gen)?;
require_comma = true;
}
items.push(item);

if inner.is_empty() {
break;
}

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

Ok(CellCollection(cells, Collection(items)))
}
}

impl Collection {
pub fn impl_parts(&self) -> (Toks, Toks, Toks, Toks, Toks) {
let mut data_ty = None;
Expand Down Expand Up @@ -352,3 +391,48 @@ impl Collection {
toks
}
}

impl CellCollection {
pub fn expand(&self) -> Toks {
let name = Ident::new("_Collection", Span::call_site());
let (impl_generics, ty_generics, stor_ty, stor_def, collection) = self.1.impl_parts();

let mut cell_info_rules = quote! {};
let mut dim = GridDimensions::default();
for (index, cell) in self.0.iter().enumerate() {
cell_info_rules.append_all(quote! {
#index => Some(#cell),
});
dim.update(cell);
}

let toks = quote! {{
struct #name #impl_generics {
#stor_ty
}

impl #impl_generics ::kas::Collection for #name #ty_generics {
#collection
}

impl #impl_generics ::kas::CellCollection for #name #ty_generics {
fn cell_info(&self, index: usize) -> Option<::kas::layout::GridCellInfo> {
match index {
#cell_info_rules
_ => None,
}
}

fn grid_dimensions(&self) -> ::kas::layout::GridDimensions {
#dim
}
}

#name {
#stor_def
}
}};
// println!("{}", toks);
toks
}
}
16 changes: 16 additions & 0 deletions crates/kas-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,22 @@ pub fn collection(input: TokenStream) -> TokenStream {
.into()
}

/// Generate an anonymous struct which implements [`kas::CellCollection`]
///
/// Each item must be either a string literal (inferred as a static label) or a
/// widget (implements [`kas::Widget`](https://docs.rs/kas/latest/kas/trait.Widget.html)).
///
/// For example usage, see [`Grid`](https://docs.rs/kas/latest/kas/widgets/struct.Grid.html).
///
/// [`kas::Collection`]: https://docs.rs/kas/latest/kas/trait.Collection.html
#[proc_macro_error]
#[proc_macro]
pub fn cell_collection(input: TokenStream) -> TokenStream {
parse_macro_input!(input as collection::CellCollection)
.expand()
.into()
}

/// A trait implementation is an extension over some base
///
/// Usage as follows:
Expand Down
11 changes: 11 additions & 0 deletions crates/kas-widgets/src/grid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ impl_scope! {
/// ## Performance
///
/// Most operations are `O(n)` in the number of children.
///
/// ## Example
/// ```
/// use kas::cell_collection;
/// # use kas_widgets::Grid;
/// let _grid = Grid::new(cell_collection! {
/// (0, 0) => "one",
/// (1, 0) => "two",
/// (0..2, 1) => "three",
/// });
/// ```
#[widget]
pub struct Grid<C: CellCollection> {
core: widget_core!(),
Expand Down

0 comments on commit f575171

Please sign in to comment.