Skip to main content

IrModules

Struct IrModules 

Source
pub struct IrModules<A> { /* private fields */ }
Expand description

The page modules, loaded at start by the strict loader, and in a debug build reread when the build’s watch mode rewrites one.

load_ir_modules_strict hands the application its modules once, so a page edited under forma-build --watch showed its change only after a server restart. An application that holds its modules here instead asks for a route’s module per request, with IrModules::get:

  • In a release build get is a map lookup. Nothing is watched or checked: the modules are the ones embedded in the binary, which cannot change.
  • In a debug build (cfg(debug_assertions)) get first rereads the manifest’s entry for the route and the module’s file, and when the bytes differ from the module it holds, loads the new one through the same check as start (IrModule::parse, the strict loader’s). A module that fails it keeps the previous one, and the failure is logged once per distinct file, as a tracing warning carrying its LOAD-1xx code; the next good write replaces it. A route the manifest starts naming after start is loaded on its first get; a route the manifest stops naming keeps its last module.

Why per request, and why debug builds: in a debug build rust-embed reads every file from disk on each call, so rereading the manifest and the module costs two small reads and a comparison, and the next request sees the build’s last write, with no watcher thread, no file-watching dependency and no path for forma-server to know beyond the Embed type. The watch mode publishes a module by rename and only when its bytes change, so a read never sees half a file and an unchanged module is never reloaded. In a release build rust-embed serves the bytes compiled in, so there is nothing to reread, and the check is not compiled: the switch is rust-embed’s own. (An application that enables rust-embed’s debug-embed feature has its files compiled into debug builds too; get then finds nothing changed.)

“Debug build” here means forma-server compiled with debug assertions (cfg(debug_assertions)), not a profile’s name. Cargo’s default release profile turns them off, so a default release binary reads nothing per request. A profile that sets debug-assertions = true, for the whole build or for forma-server alone ([profile.release.package.forma-server]), compiles the check into what it calls a release binary: each get then rereads the files, from the build folder on disk (rust-embed reads from disk under the same switch), or, with debug-embed, from the embedded bytes at the cost of a lock and a comparison. Keep debug assertions off in the profile you deploy.

The strict loader’s render_modes map has no counterpart here: a route is server-rendered exactly when get(route) returns a module, and a route without one (ssr: false) is served with render_browser_shell.

use forma_server::{assets, IrModules, PageConfig};
use std::sync::Arc;

// At start, before the port binds: refuse to start on any error.
fn start<Assets: rust_embed::Embed>() -> Result<Arc<IrModules<Assets>>, String> {
    let manifest = assets::try_load_manifest::<Assets>()?;
    let modules = IrModules::<Assets>::load(&manifest).map_err(|errors| {
        errors.iter().map(|e| e.to_string()).collect::<Vec<_>>().join("; ")
    })?;
    Ok(Arc::new(modules))
}

// Per request: hold the route's module for the render.
fn module<Assets: rust_embed::Embed>(modules: &IrModules<Assets>) -> Option<Arc<forma_ir::IrModule>> {
    modules.get("/") // then `ir_module: module.as_deref()` in the PageConfig
}

Verified by: crates/forma-server/tests/dev_reload.rs > “an_edited_page_is_served_on_the_next_request_and_a_broken_edit_keeps_the_previous_page” Verified by: crates/forma-server/src/ir_compat.rs > “a_release_build_reads_nothing_per_request_and_a_debug_build_rereads”

Implementations§

Source§

impl<A: Embed> IrModules<A>

Source

pub fn load(manifest: &AssetManifest) -> Result<Self, Vec<IrLoadError>>

Load every module the manifest declares, through the strict loader: the modules, or every failure, as load_ir_modules_strict returns them. Call it at start and refuse to start on Err.

§Errors

One IrLoadError per module that is not embedded or fails its check, naming route, file and code.

Source

pub fn get(&self, route_pattern: &str) -> Option<Arc<IrModule>>

The route’s module, or None for a route with none (a route the build declares ssr: false, or one the manifest never named). In a debug build, the module the build last wrote, if it loads; see the type’s documentation.

Trait Implementations§

Source§

impl<A> Debug for IrModules<A>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<A> !Freeze for IrModules<A>

§

impl<A> RefUnwindSafe for IrModules<A>
where PhantomData<fn() -> A>: RefUnwindSafe,

§

impl<A> Send for IrModules<A>
where PhantomData<fn() -> A>: Send,

§

impl<A> Sync for IrModules<A>
where PhantomData<fn() -> A>: Sync,

§

impl<A> Unpin for IrModules<A>
where PhantomData<fn() -> A>: Unpin,

§

impl<A> UnsafeUnpin for IrModules<A>
where PhantomData<fn() -> A>: UnsafeUnpin,

§

impl<A> UnwindSafe for IrModules<A>
where PhantomData<fn() -> A>: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<A, B, T> HttpServerConnExec<A, B> for T
where B: Body,

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more