crux_core/
lib.rs

1//! Cross-platform app development in Rust
2//!
3//! Crux helps you share your app's business logic and behavior across mobile (iOS and Android) and web,
4//! as a single, reusable core built with Rust.
5//!
6//! Unlike React Native, the user interface layer is built natively, with modern declarative UI frameworks
7//! such as Swift UI, Jetpack Compose and React/Vue or a WASM based framework on the web.
8//!
9//! The UI layer is as thin as it can be, and all other work is done by the shared core.
10//! The interface with the core has static type checking across languages.
11//!
12//! ## Getting Started
13//!
14//! Crux applications are split into two parts: a Core written in Rust and a Shell written in the platform
15//! native language (e.g. Swift or Kotlin). It is also possible to use Crux from Rust shells.
16//! The Core architecture is based on [Elm architecture](https://guide.elm-lang.org/architecture/).
17//!
18//! Quick glossary of terms to help you follow the example:
19//!
20//! * Core - the shared core written in Rust
21//!
22//! * Shell - the native side of the app on each platform handling UI and executing side effects
23//!
24//! * App - the main module of the core containing the application logic, especially model changes
25//!   and side-effects triggered by events. An App can delegate to child apps, mapping Events and Effects.
26//!
27//! * Event - main input for the core, typically triggered by user interaction in the UI
28//!
29//! * Model - data structure (typically tree-like) holding the entire application state
30//!
31//! * View model - data structure describing the current state of the user interface
32//!
33//! * Effect - A side-effect the core can request from the shell. This is typically a form of I/O or similar
34//!   interaction with the host platform. Updating the UI is considered an effect.
35//!
36//! * Capability - A user-friendly API used to request effects and provide events that should be dispatched
37//!   when an effect is completed. For example, a HTTP client is a capability.
38//!
39//! * Command - A description of a side-effect to be executed by the shell. Commands can be combined
40//!   (synchronously with combinators, or asynchronously with Rust async) to run
41//!   sequentially or concurrently, or any combination thereof.
42//!
43//! Below is a minimal example of a Crux-based application Core:
44//!
45//! ```rust
46//!// src/app.rs
47//!use crux_core::{render::{self, Render}, App, macros::Effect, Command};
48//!use serde::{Deserialize, Serialize};
49//!
50//!// Model describing the application state
51//!#[derive(Default)]
52//!struct Model {
53//!    count: isize,
54//!}
55//!
56//!// Event describing the actions that can be taken
57//!#[derive(Serialize, Deserialize)]
58//!pub enum Event {
59//!    Increment,
60//!    Decrement,
61//!    Reset,
62//!}
63//!
64//!// Capabilities listing the side effects the Core
65//!// will use to request side effects from the Shell
66//!#[cfg_attr(feature = "typegen", derive(crux_core::macros::Export))]
67//!#[derive(Effect)]
68//!pub struct Capabilities {
69//!    pub render: Render<Event>,
70//!}
71//!
72//!#[derive(Default)]
73//!struct Hello;
74//!
75//!impl App for Hello {
76//!    // Use the above Event
77//!    type Event = Event;
78//!    // Use the above Model
79//!    type Model = Model;
80//!    type ViewModel = String;
81//!    // Use the above Capabilities
82//!    type Capabilities = Capabilities;
83//!    // Use the above generated Effect
84//!    type Effect = Effect;
85//!
86//!    fn update(&self, event: Event, model: &mut Model, caps: &Capabilities) -> Command<Effect, Event> {
87//!        match event {
88//!            Event::Increment => model.count += 1,
89//!            Event::Decrement => model.count -= 1,
90//!            Event::Reset => model.count = 0,
91//!        };
92//!
93//!        // Request a UI update
94//!        render::render()
95//!    }
96//!
97//!    fn view(&self, model: &Model) -> Self::ViewModel {
98//!        format!("Count is: {}", model.count)
99//!    }
100//!}
101//! ```
102//!
103//! ## Integrating with a Shell
104//!
105//! To use the application in a user interface shell, you need to expose the core interface for FFI.
106//! This "plumbing" will likely be simplified with macros in the future versions of Crux.
107//!
108//! ```rust,ignore
109//! // src/lib.rs
110//! pub mod app;
111//!
112//! use lazy_static::lazy_static;
113//! use wasm_bindgen::prelude::wasm_bindgen;
114//!
115//! pub use crux_core::bridge::{Bridge, Request};
116//! pub use crux_core::Core;
117//! pub use crux_http as http;
118//!
119//! pub use app::*;
120//!
121//! uniffi_macros::include_scaffolding!("hello");
122//!
123//! lazy_static! {
124//!     static ref CORE: Bridge<Effect, App> = Bridge::new(Core::new::<Capabilities>());
125//! }
126//!
127//! #[wasm_bindgen]
128//! pub fn process_event(data: &[u8]) -> Vec<u8> {
129//!     CORE.process_event(data)
130//! }
131//!
132//! #[wasm_bindgen]
133//! pub fn handle_response(id: u32, data: &[u8]) -> Vec<u8> {
134//!     CORE.handle_response(id, data)
135//! }
136//!
137//! #[wasm_bindgen]
138//! pub fn view() -> Vec<u8> {
139//!     CORE.view()
140//! }
141//! ```
142//!
143//! You will also need a `hello.udl` file describing the foreign function interface:
144//!
145//! ```ignore
146//! // src/hello.udl
147//! namespace hello {
148//!   sequence<u8> process_event([ByRef] sequence<u8> msg);
149//!   sequence<u8> handle_response([ByRef] sequence<u8> res);
150//!   sequence<u8> view();
151//! };
152//! ```
153//!
154//! Finally, you will need to set up the type generation for the `Model`, `Message` and `ViewModel` types.
155//! See [typegen](https://docs.rs/crux_core/latest/crux_core/typegen/index.html) for details.
156//!
157
158pub mod bridge;
159pub mod capability;
160pub mod command;
161pub mod testing;
162#[cfg(feature = "typegen")]
163pub mod typegen;
164
165mod capabilities;
166mod core;
167
168use serde::Serialize;
169
170pub use self::{
171    capabilities::*,
172    capability::{Capability, WithContext},
173    command::Command,
174    core::{Core, Effect, Request, ResolveError},
175};
176pub use crux_macros as macros;
177
178/// Implement [`App`] on your type to make it into a Crux app. Use your type implementing [`App`]
179/// as the type argument to [`Core`] or [`Bridge`](bridge::Bridge).
180pub trait App: Default {
181    /// Event, typically an `enum`, defines the actions that can be taken to update the application state.
182    type Event: Unpin + Send + 'static;
183    /// Model, typically a `struct` defines the internal state of the application
184    type Model: Default;
185    /// ViewModel, typically a `struct` describes the user interface that should be
186    /// displayed to the user
187    type ViewModel: Serialize;
188    /// Capabilities, usually a `struct`, lists the capabilities used by this application.
189    ///
190    /// Typically, Capabilities should contain at least an instance of the built-in [`Render`](crate::render::Render) capability.
191    ///
192    /// Note: this `Capabilities` associated type will be deprecated soon as part of the completion of
193    /// the migration to the new [`Command`](command) API.
194    type Capabilities;
195    /// Effect, the enum carrying effect requests created by capabilities.
196    /// Normally this type is derived from `Capabilities` using the `crux_macros::Effect` derive macro
197    type Effect: Effect + Unpin;
198
199    /// Update method defines the transition from one `model` state to another in response to an `event`.
200    ///
201    /// `update` may mutate the `model` and returns a [`Command`] describing
202    /// the managed side-effects to perform as a result of the `event`. Commands can be constructed by capabilities
203    /// and combined to run sequentially or concurrently. If migrating from previous version of crux, you
204    /// can return `Command::done()` for compatibility.
205    ///
206    /// For backwards compatibility, `update` may also use the capabilities provided by the `caps` argument
207    /// to instruct the shell to perform side-effects. The side-effects will run concurrently (capability
208    /// calls behave the same as go routines in Go or Promises in JavaScript) with each other and any
209    /// effects captured by the returned `Command`. Capability calls don't return anything, but may
210    /// take a `callback` event which should be dispatched when the effect completes.
211    ///
212    /// Typically, `update` should call at least [`Render::render`](crate::render::Render::render).
213    fn update(
214        &self,
215        event: Self::Event,
216        model: &mut Self::Model,
217        caps: &Self::Capabilities,
218    ) -> Command<Self::Effect, Self::Event>;
219
220    /// View method is used by the Shell to request the current state of the user interface
221    fn view(&self, model: &Self::Model) -> Self::ViewModel;
222}