Skip to content

Commit

Permalink
props docs
Browse files Browse the repository at this point in the history
  • Loading branch information
ccbrown committed Sep 22, 2024
1 parent d3566fe commit 369568c
Show file tree
Hide file tree
Showing 14 changed files with 87 additions and 77 deletions.
4 changes: 2 additions & 2 deletions examples/form.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use iocraft::prelude::*;

#[props]
#[derive(Default, Props)]
struct FormFieldProps {
label: String,
value: Option<State<String>>,
Expand Down Expand Up @@ -37,7 +37,7 @@ fn FormField(props: &FormFieldProps) -> impl Into<AnyElement<'static>> {
}
}

#[props]
#[derive(Default, Props)]
struct FormProps<'a> {
first_name_out: Option<&'a mut String>,
last_name_out: Option<&'a mut String>,
Expand Down
2 changes: 1 addition & 1 deletion examples/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ impl User {
}
}

#[props]
#[derive(Default, Props)]
struct UsersTableProps<'a> {
users: Option<&'a Vec<User>>,
}
Expand Down
45 changes: 10 additions & 35 deletions packages/iocraft-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,18 +127,18 @@ pub fn element(input: TokenStream) -> TokenStream {
quote!(#element).into()
}

struct ParsedCovariant {
struct ParsedProps {
def: ItemStruct,
}

impl Parse for ParsedCovariant {
impl Parse for ParsedProps {
fn parse(input: ParseStream) -> Result<Self> {
let def: ItemStruct = input.parse()?;
Ok(Self { def })
}
}

impl ToTokens for ParsedCovariant {
impl ToTokens for ParsedProps {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let def = &self.def;
let name = &def.ident;
Expand Down Expand Up @@ -231,43 +231,18 @@ impl ToTokens for ParsedCovariant {
}

tokens.extend(quote! {
unsafe impl #generics ::iocraft::Covariant for #name #bracketed_generic_names #where_clause {}
unsafe impl #generics ::iocraft::Props for #name #bracketed_generic_names #where_clause {}
});
}
}

/// Makes a struct as being covariant. If the struct is not actually covariant, compilation will fail.
#[proc_macro_derive(Covariant)]
/// Makes a struct available for use as component properties.
///
/// Most importantly, this marks a struct as being
/// [covariant](https://doc.rust-lang.org/nomicon/subtyping.html). If the struct is not actually
/// covariant, compilation will fail.
#[proc_macro_derive(Props)]
pub fn derive_covariant_type(item: TokenStream) -> TokenStream {
let props = parse_macro_input!(item as ParsedCovariant);
quote!(#props).into()
}

struct ParsedProps {
props: ItemStruct,
}

impl Parse for ParsedProps {
fn parse(input: ParseStream) -> Result<Self> {
let props: ItemStruct = input.parse()?;
Ok(Self { props })
}
}

impl ToTokens for ParsedProps {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let props = &self.props;

tokens.extend(quote! {
#[derive(Default, ::iocraft::Covariant)]
#props
});
}
}

/// Defines a struct containing properties to be accepted by components.
#[proc_macro_attribute]
pub fn props(_attr: TokenStream, item: TokenStream) -> TokenStream {
let props = parse_macro_input!(item as ParsedProps);
quote!(#props).into()
}
Expand Down
4 changes: 2 additions & 2 deletions packages/iocraft-macros/tests/component.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
#![allow(dead_code)]

use iocraft::{components::Box, AnyElement, Hooks};
use iocraft_macros::{component, element, props};
use iocraft_macros::{component, element, Props};

#[component]
fn MyComponent() -> impl Into<AnyElement<'static>> {
element!(Box)
}

#[props]
#[derive(Default, Props)]
struct MyProps {
foo: String,
}
Expand Down
6 changes: 3 additions & 3 deletions packages/iocraft-macros/tests/element.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#![allow(dead_code)]

use iocraft::{element, AnyElement, Component, Covariant, Element, Percent};
use iocraft::{element, AnyElement, Component, Element, Percent, Props};

#[derive(Default)]
struct MyComponent;

#[derive(Covariant, Default)]
#[derive(Default, Props)]
struct MyComponentProps {
foo: String,
percent: Percent,
Expand All @@ -22,7 +22,7 @@ impl Component for MyComponent {

struct MyContainer;

#[derive(Covariant, Default)]
#[derive(Default, Props)]
struct MyContainerProps {
children: Vec<AnyElement<'static>>,
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,35 +1,35 @@
#![allow(dead_code)]

use iocraft_macros::Covariant;
use iocraft_macros::Props;

#[derive(Covariant)]
#[derive(Props)]
struct Unit;

#[derive(Covariant)]
#[derive(Props)]
struct BasicStruct {
foo: i32,
}

#[derive(Covariant)]
#[derive(Props)]
struct StructWithLifetime<'lt> {
foo: &'lt i32,
bar: &'lt mut i32,
}

#[derive(Covariant)]
#[derive(Props)]
struct StructWithLifetimeAndConsts<'lt, const N: usize, const M: usize> {
foo: &'lt i32,
bar: &'lt mut i32,
baz: [i32; N],
qux: [i32; M],
}

#[derive(Covariant)]
#[derive(Props)]
struct StructWithTypeGeneric<T> {
foo: T,
}

#[derive(Covariant)]
#[derive(Props)]
struct StructWithLifetimeAndTypeGeneric<'lt, T> {
foo: &'lt T,
}
8 changes: 4 additions & 4 deletions packages/iocraft-macros/tests/with_layout_style_props.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
use iocraft::Display;
use iocraft_macros::{props, with_layout_style_props};
use iocraft_macros::{with_layout_style_props, Props};

#[with_layout_style_props]
#[props]
#[derive(Default, Props)]
struct MyProps {
foo: String,
}

#[with_layout_style_props]
#[props]
#[derive(Default, Props)]
struct MyPropsWithLifetime<'lt> {
foo: Option<&'lt str>,
}

#[with_layout_style_props]
#[props]
#[derive(Default, Props)]
struct MyPropsWithTypeGeneric<T> {
foo: Option<T>,
}
Expand Down
4 changes: 2 additions & 2 deletions packages/iocraft/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::{
context::ContextStack,
element::{ElementKey, ElementType},
hook::{AnyHook, Hook, Hooks},
props::{AnyProps, Covariant},
props::{AnyProps, Props},
render::{ComponentDrawer, ComponentUpdater, UpdateContext},
};
use futures::future::poll_fn;
Expand Down Expand Up @@ -72,7 +72,7 @@ impl<C: Component> ComponentHelperExt for ComponentHelper<C> {
/// level component type definitions.
pub trait Component: Any + Unpin {
/// The type of properties that the component accepts.
type Props<'a>: Covariant
type Props<'a>: Props
where
Self: 'a;

Expand Down
6 changes: 3 additions & 3 deletions packages/iocraft/src/components/box.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
AnyElement, CanvasTextStyle, Color, Component, ComponentDrawer, ComponentUpdater, Covariant,
Edges, Hooks,
AnyElement, CanvasTextStyle, Color, Component, ComponentDrawer, ComponentUpdater, Edges, Hooks,
Props,
};
use iocraft_macros::with_layout_style_props;
use taffy::{LengthPercentage, Rect};
Expand Down Expand Up @@ -137,7 +137,7 @@ impl BorderStyle {

/// The props which can be passed to the [`Box`] component.
#[with_layout_style_props]
#[derive(Covariant, Default)]
#[derive(Default, Props)]
pub struct BoxProps<'a> {
/// The elements to render inside of the box.
pub children: Vec<AnyElement<'a>>,
Expand Down
4 changes: 2 additions & 2 deletions packages/iocraft/src/components/context_provider.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{AnyElement, Component, ComponentUpdater, Context, Covariant, Hooks};
use crate::{AnyElement, Component, ComponentUpdater, Context, Hooks, Props};

/// The props which can be passed to the [`ContextProvider`] component.
#[derive(Covariant, Default)]
#[derive(Default, Props)]
pub struct ContextProviderProps<'a> {
/// The children of the component.
pub children: Vec<AnyElement<'a>>,
Expand Down
4 changes: 2 additions & 2 deletions packages/iocraft/src/components/text.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
CanvasTextStyle, Color, Component, ComponentDrawer, ComponentUpdater, Covariant, Hooks, Weight,
CanvasTextStyle, Color, Component, ComponentDrawer, ComponentUpdater, Hooks, Props, Weight,
};
use taffy::{AvailableSpace, Size};
use unicode_width::UnicodeWidthStr;
Expand Down Expand Up @@ -37,7 +37,7 @@ pub enum TextDecoration {
}

/// The props which can be passed to the [`Text`] component.
#[derive(Default, Covariant)]
#[derive(Default, Props)]
pub struct TextProps {
/// The color to make the text.
pub color: Option<Color>,
Expand Down
6 changes: 3 additions & 3 deletions packages/iocraft/src/components/text_input.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
CanvasTextStyle, Color, Component, ComponentDrawer, ComponentUpdater, Covariant, Handler,
Hooks, KeyCode, KeyEvent, KeyEventKind, TerminalEvent, TerminalEvents,
CanvasTextStyle, Color, Component, ComponentDrawer, ComponentUpdater, Handler, Hooks, KeyCode,
KeyEvent, KeyEventKind, Props, TerminalEvent, TerminalEvents,
};
use futures::stream::Stream;
use std::{
Expand All @@ -10,7 +10,7 @@ use std::{
use unicode_width::UnicodeWidthStr;

/// The props which can be passed to the [`TextInput`] component.
#[derive(Default, Covariant)]
#[derive(Default, Props)]
pub struct TextInputProps {
/// The color to make the text.
pub color: Option<Color>,
Expand Down
2 changes: 1 addition & 1 deletion packages/iocraft/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
//! - Output colored and styled UIs to the terminal or ASCII output anywhere else.
//! - Create animated or interactive elements with event handling and hooks.
//! - Build fullscreen terminal applications with ease.
//! - Pass [props](macro@props) and [context](crate::components::ContextProvider) by reference to avoid unnecessary cloning.
//! - Pass [props](crate::Props) and [context](crate::components::ContextProvider) by reference to avoid unnecessary cloning.
//!
//! ## Getting Started
//!
Expand Down
55 changes: 45 additions & 10 deletions packages/iocraft/src/props.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,51 @@
use std::marker::PhantomData;

/// This trait marks a type as being covariant.
/// This trait makes a struct available for use as component properties.
///
/// # Examples
///
/// ```
/// # use iocraft::prelude::*;
/// #[derive(Default, Props)]
/// struct MyProps {
/// foo: String,
/// }
/// ```
///
/// Unowned data is okay too:
///
/// ```
/// # use iocraft::prelude::*;
/// #[derive(Default, Props)]
/// struct MyProps<'a> {
/// foo: &'a str,
/// }
/// ```
///
/// However, a field that would make the struct
/// [invariant](https://doc.rust-lang.org/nomicon/subtyping.html) is not okay and will not compile:
///
/// ```compile_fail
/// # use iocraft::prelude::*;
/// # struct MyType<'a> {
/// # _foo: &'a str,
/// # }
/// #[derive(Default, Props)]
/// struct MyProps<'a, 'b> {
/// foo: &'a mut MyType<'b>,
/// }
/// ```
///
/// # Safety
///
/// If the type is not actually covariant, then the safety of the program is compromised. You can
/// use the `#[derive(Covariant)]` macro to implement this trait safely. If the type is not
/// actually covariant, the derive macro will not compile.
pub unsafe trait Covariant {}
/// This requires the type to be [covariant](https://doc.rust-lang.org/nomicon/subtyping.html). If
/// implemented for a type that is not actually covariant, then the safety of the program is
/// compromised. You can use the `#[derive(Props)]` macro to implement this trait safely. If the
/// type is not actually covariant, the derive macro will give you an error at compile-time.
pub unsafe trait Props {}

#[doc(hidden)]
#[derive(Clone, Copy, iocraft_macros::Covariant, Default)]
#[derive(Clone, Copy, iocraft_macros::Props, Default)]
pub struct NoProps;

struct DropRawImpl<T> {
Expand All @@ -37,7 +72,7 @@ pub struct AnyProps<'a> {
}

impl<'a> AnyProps<'a> {
pub(crate) fn owned<T: Covariant + 'a>(props: T) -> Self {
pub(crate) fn owned<T: Props + 'a>(props: T) -> Self {
let raw = Box::into_raw(Box::new(props));
Self {
raw: raw as *mut (),
Expand All @@ -48,19 +83,19 @@ impl<'a> AnyProps<'a> {
}
}

pub(crate) fn borrowed<T: Covariant>(props: &'a mut T) -> Self {
pub(crate) fn borrowed<T: Props>(props: &'a mut T) -> Self {
Self {
raw: props as *const T as *mut (),
drop: None,
_marker: PhantomData,
}
}

pub(crate) unsafe fn downcast_ref_unchecked<T: Covariant>(&self) -> &T {
pub(crate) unsafe fn downcast_ref_unchecked<T: Props>(&self) -> &T {
unsafe { &*(self.raw as *const T) }
}

pub(crate) unsafe fn downcast_mut_unchecked<T: Covariant>(&mut self) -> &mut T {
pub(crate) unsafe fn downcast_mut_unchecked<T: Props>(&mut self) -> &mut T {
unsafe { &mut *(self.raw as *mut T) }
}

Expand Down

0 comments on commit 369568c

Please sign in to comment.