sketchbook 0.0.2

Interactive visual applications in Rust
Documentation
//! Interactive visual applications in Rust.
//!
//! ```
//! # #![allow(clippy::needless_doctest_main)]
//! use sketchbook::*;
//! use sketchbook::ream::Single;
//!
//! #[apply(derive_sketch)]
//! #[sketch(
//!     env = minimal,
//!     aspects = (/* aspects go here */),
//! )]
//! struct App {
//!     #[page] page: minimal::Page,
//! }
//!
//! impl Setup for App {
//!     fn setup(page: minimal::Page, _: ()) -> Self {
//!         Self { page }
//!     }
//! }
//!
//! fn main() {
//!     Book::<Single<App>>::bind();
//! }
//! ```
//!
//! ## Environments
//!
//! | Crate                                                       | Description                                                      | Status         |
//! |-------------------------------------------------------------|------------------------------------------------------------------|----------------|
//! | [sketchbook-wgpu](https://crates.io/crates/sketchbook-wgpu) | Directly uses `winit` and `wgpu` to run sketches.                | In Development |
//! | sketchbook-web                                              | Runs sketches in a web browser.                                  | Planned        |
//! | sketchbook-avr                                              | Runs sketches on AVR micro-controllers (E.g. AVR based Arduinos) | Planned        |
//!
//! ## Feature Flags
#![cfg_attr(
    docsrs,
    cfg_attr(doc, doc = ::document_features::document_features!())
)]
// #![warn(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
// only enables the `doc_cfg` feature when
// the `docsrs` configuration attribute is defined
#![cfg_attr(docsrs, feature(doc_cfg))]
#![deny(
    bad_style,
    const_err,
    dead_code,
    improper_ctypes,
    non_shorthand_field_patterns,
    no_mangle_generic_items,
    overflowing_literals,
    path_statements,
    patterns_in_fns_without_body,
    private_in_public,
    unconditional_recursion,
    unused,
    unused_allocation,
    unused_comparisons,
    unused_parens,
    while_true
)]
#![warn(
    rust_2018_idioms,
    missing_debug_implementations,
    missing_docs,
    trivial_casts,
    trivial_numeric_casts,
    unused_extern_crates,
    unused_import_braces,
    unused_qualifications,
    unused_results,
    unsafe_code
)]
#![deny(clippy::unwrap_used, clippy::expect_used)]

#[cfg(feature = "alloc")]
extern crate alloc;

pub mod compose;

mod env;

pub mod ream;

mod book;

mod real;

pub mod aspects;

mod geometry;

pub use book::*;
// pub use ream::*;
pub use env::*;
// pub use aspects::*;
pub use geometry::*;
pub use real::*;

/// Base trait for all sketches.
///
/// A user does not need to interact with this trait directly.
///
/// A sketch is a type that contains a page from an environment.
/// The sketch is then run within the environment and handles the events it generates.
pub trait Sketch {
    /// Environment the sketch can run in.
    type Env: Environment;

    /// Get page for sketch.
    fn get_page(&self) -> &PageOf<Self::Env>;

    /// Get page mutabily for sketch.
    fn get_page_mut(&mut self) -> &mut PageOf<Self::Env>;

    /// Handle an event from the environment.
    ///
    /// This is called by the ream the sketch is running in.
    fn handle_environment_event(&mut self, event: &EventOf<Self::Env>);
}

/// Allow aspect extension traits to be called directly on `S` instead of using the page field.
impl<C, S> compose::AsPart<C> for S
where
    S: Sketch,
    PageOf<S::Env>: compose::AsPart<C>,
{
    fn as_part(&self) -> &C {
        self.get_page().as_part()
    }

    fn as_part_mut(&mut self) -> &mut C {
        self.get_page_mut().as_part_mut()
    }
}

/// Trait for setting up a sketch given some input.
pub trait Setup<Input = ()>
where
    Self: Sketch,
{
    /// Setup sketch using a page from the environment and given input.
    fn setup(page: PageOf<Self::Env>, input: Input) -> Self;
}

/// Implements the `Sketch` trait for a struct.
///
/// To enable event handlers for aspects put the event in the `aspects` list of the `sketch`
/// attribute.
/// See a spesific aspect's docs for examples of using events.
/// ```
/// # use sketchbook::minimal;
/// # use sketchbook::derive_sketch;
/// derive_sketch! {
///     #[sketch(
///         env = minimal,
///         aspects = (/* aspects go here */),
///     )]
///     struct App {
///         #[page] page: minimal::Page,
///     }
/// }
/// ```
///
/// ## Usage With `macro_rules_attribute`
/// ```
/// # use sketchbook::minimal;
/// # use sketchbook::derive_sketch;
/// # use macro_rules_attribute::apply;
/// #[apply(derive_sketch)]
/// #[sketch(
///     env = minimal,
///     aspects = (/* aspects go here */),
/// )]
/// struct App {
///     #[page] page: minimal::Page,
/// }
/// ```
#[macro_export]
macro_rules! derive_sketch {
    {
        $(#[doc = $docs:literal])?
        #[sketch(env = $env_name:ident $(, aspects = ($($aspect_name:ident),* $(,)?))? $(,)?)]
        $struct_vis:vis
        struct $sketch_name:ident {
            $($($before_vis:vis $before_field_name:ident : $before_field_type:ty),+,)?
            #[page] $page_vis:vis $page_name:ident: $page_type:ty
            $(,$($after_vis:vis $after_field_name:ident : $after_field_type:ty),+)?$(,)?
        }
    } => {
        $(#[doc = $docs])?
        $struct_vis
        struct $sketch_name {
            $($($before_vis $before_field_name : $before_field_type),+,)?
            $page_vis $page_name: $page_type
            $(,$($after_vis $after_field_name : $after_field_type),+)?
        }

        #[automatically_derived]
        #[allow(unused_qualifications)]
        impl $crate::Sketch for $sketch_name {
            type Env = $env_name::Env;

            fn get_page(&self) -> &$crate::PageOf<$env_name::Env> {
                &self.$page_name
            }

            fn get_page_mut(&mut self) -> &mut $crate::PageOf<$env_name::Env> {
                &mut self.$page_name
            }

            fn handle_environment_event(
                &mut self,
                event: &$crate::EventOf<Self::Env>,
            ) {
                use $crate::compose::TryAsPart;
                $($(
                    if let Some(event) = event.try_as_part() {
                        <Self as $aspect_name::RunHandlers>::run_handlers(self, event);
                    }
                )*)?
            }
        }

        $($(
            #[automatically_derived]
            #[allow(unused_qualifications)]
            impl $aspect_name::RunHandlers for $sketch_name {}
        )*)?
    };
    {
        $(#[doc = $docs:literal])?
        #[sketch(env = $env_name:ident $(, aspects = ($($aspect_name:ident),* $(,)?))? $(,)?)]
        $struct_vis:vis
        struct $sketch_name:ident {
            $($code:tt)*
        }
    } => {
        compile_error!("Missing `#[page]` attribute on field for environment's page.");
    };
    {
        $(#[doc = $docs:literal])?
        $(#[sketch($($attr:tt)*)])?
        $struct_vis:vis
        struct $sketch_name:ident {
            $($code:tt)*
        }
    } => {
        compile_error!("Missing `#[sketch(env = .., aspects = (.., ...))]` attribute.");
    };
    {
        $($tokens:tt)*
    } => {
        compile_error!("Unable to parse struct. See docs for `derive_sketch!` for expected syntax.");
    }
}

/// Re-export of `macro_rules_attribute`'s `apply` macro.
///
/// Visit [the crates.io page](https://crates.io/crates/macro_rules_attribute) for
/// `macro_rules_attribute` for information about it.
///
/// Visit [the docs.rs page](https://docs.rs/macro_rules_attribute/latest/macro_rules_attribute/attr.apply.html)
/// for more details about this specific macro.
#[cfg(feature = "derive")]
pub use macro_rules_attribute::apply;