Skip to main content

TemplateRegistry

Struct TemplateRegistry 

Source
pub struct TemplateRegistry { /* private fields */ }
Expand description

Registry for template resolution from multiple sources.

The registry maintains a unified view of templates from:

  • Inline strings (highest priority)
  • Multiple filesystem directories
  • Embedded content (for release builds)

§Resolution Order

When looking up a template name:

  1. Check inline templates first
  2. Check file-based templates in registration order
  3. Return error if not found

§Thread Safety

The registry is not thread-safe. For concurrent access, wrap in appropriate synchronization primitives.

§Example

let mut registry = TemplateRegistry::new();

// Add inline template (highest priority)
registry.add_inline("header", "{{ title }}");

// Add from directory
registry.add_template_dir("./templates")?;

// Resolve and get content
let content = registry.get_content("header")?;

Implementations§

Source§

impl TemplateRegistry

Source

pub fn new() -> Self

Creates an empty template registry.

Source

pub fn add_inline( &mut self, name: impl Into<String>, content: impl Into<String>, )

Adds an inline template with the given name.

Inline templates have the highest priority and will shadow any file-based templates with the same name.

§Arguments
  • name - The template name for resolution
  • content - The template content
§Example
registry.add_inline("header", "{{ title | style(\"title\") }}");
Source

pub fn add_template_dir<P: AsRef<Path>>( &mut self, path: P, ) -> Result<(), RegistryError>

Adds a template directory to search for files.

Templates in the directory are resolved by their relative path without extension. For example, with directory ./templates:

  • "config"./templates/config.jinja
  • "todos/list"./templates/todos/list.jinja
§Errors

Returns an error if the directory doesn’t exist.

Source

pub fn add_from_files( &mut self, files: Vec<TemplateFile>, ) -> Result<(), RegistryError>

Adds templates discovered from a directory scan.

This method processes a list of TemplateFile entries, typically produced by walk_template_dir, and registers them for resolution.

§Resolution Names

Each file is registered under two names:

  • Without extension: "config" for config.jinja
  • With extension: "config.jinja" for config.jinja
§Extension Priority

If multiple files share the same base name with different extensions (e.g., config.jinja and config.j2), the higher-priority extension wins for the extensionless name. Both can still be accessed by full name.

§Collision Detection

If a template name conflicts with one from a different source directory, an error is returned with details about both files.

§Arguments
  • files - Template files discovered during directory walking
§Errors

Returns RegistryError::Collision if templates from different directories resolve to the same name.

Source

pub fn add_embedded(&mut self, templates: HashMap<String, String>)

Adds pre-embedded templates (for release builds).

Embedded templates are treated as inline templates, stored directly in memory without filesystem access.

§Arguments
  • templates - Map of template name to content
Source

pub fn add_framework( &mut self, name: impl Into<String>, content: impl Into<String>, )

Adds framework templates (lowest priority fallback).

Framework templates are provided by the standout framework and serve as defaults that can be overridden by user templates with the same name. They are checked last during resolution.

Framework templates typically use the standout/ namespace to avoid accidental collision with user templates (e.g., standout/list-view).

§Arguments
  • name - The template name (e.g., "standout/list-view")
  • content - The template content
§Example
registry.add_framework("standout/list-view", include_str!("templates/list-view.jinja"));
Source

pub fn add_framework_entries(&mut self, entries: &[(&str, &str)])

Adds multiple framework templates from embedded entries.

This is similar to [from_embedded_entries] but adds templates to the framework (lowest priority) tier instead of inline (highest priority).

§Arguments
  • entries - Slice of (name_with_ext, content) pairs
Source

pub fn clear_framework(&mut self)

Clears all framework templates.

This is useful when you want to disable all framework-provided defaults and require explicit template configuration.

Source

pub fn from_embedded_entries(entries: &[(&str, &str)]) -> Self

Creates a registry from embedded template entries.

This is the primary entry point for compile-time embedded templates, typically called by the embed_templates! macro.

§Arguments
  • entries - Slice of (name_with_ext, content) pairs where name_with_ext is the relative path including extension (e.g., "report/summary.jinja")
§Processing

This method applies the same logic as runtime file loading:

  1. Extension stripping: "report/summary.jinja""report/summary"
  2. Extension priority: When multiple files share a base name, the higher-priority extension wins (see TEMPLATE_EXTENSIONS)
  3. Dual registration: Each template is accessible by both its base name and its full name with extension
§Example
use standout::TemplateRegistry;

// Typically generated by embed_templates! macro
let entries: &[(&str, &str)] = &[
    ("list.jinja", "Hello {{ name }}"),
    ("report/summary.jinja", "Report: {{ title }}"),
];

let registry = TemplateRegistry::from_embedded_entries(entries);

// Access by base name or full name
assert!(registry.get("list").is_ok());
assert!(registry.get("list.jinja").is_ok());
assert!(registry.get("report/summary").is_ok());
Source

pub fn get(&self, name: &str) -> Result<ResolvedTemplate, RegistryError>

Looks up a template by name.

Names can be specified with or without extension:

  • "config" resolves to config.jinja (or highest-priority extension)
  • "config.jinja" resolves to exactly that file
§Resolution Priority

Templates are resolved in this order:

  1. Inline templates (highest priority)
  2. File-based templates from add_from_files
  3. Directory-based templates from add_template_dir
  4. Framework templates (lowest priority)

This allows user templates to override framework defaults.

§Errors

Returns RegistryError::NotFound if the template doesn’t exist.

Source

pub fn get_content(&self, name: &str) -> Result<String, RegistryError>

Gets the content of a template, reading from disk if necessary.

For inline templates, returns the stored content directly. For file templates, reads the file from disk (enabling hot reload).

§Errors

Returns an error if the template is not found or cannot be read from disk.

Source

pub fn refresh(&mut self) -> Result<(), RegistryError>

Refreshes the registry from registered directories.

This re-walks all registered template directories and rebuilds the resolution map. Call this if:

  • You’ve added template directories after the first render
  • Template files have been added/removed from disk
§Panics

Panics if a collision is detected (same name from different directories).

Source

pub fn len(&self) -> usize

Returns the number of registered templates.

Note: This counts both extensionless and with-extension entries, so it may be higher than the number of unique template files.

Source

pub fn is_empty(&self) -> bool

Returns true if no templates are registered.

Source

pub fn names(&self) -> impl Iterator<Item = &str>

Returns an iterator over all registered template names.

Source

pub fn clear(&mut self)

Clears all templates from the registry.

Source

pub fn has_framework_templates(&self) -> bool

Returns true if the registry has framework templates.

Source

pub fn framework_names(&self) -> impl Iterator<Item = &str>

Returns an iterator over framework template names.

Trait Implementations§

Source§

impl Default for TemplateRegistry

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl From<EmbeddedSource<TemplateResource>> for TemplateRegistry

Source§

fn from(source: EmbeddedTemplates) -> Self

Converts embedded templates into a TemplateRegistry.

In debug mode, if the source path exists, templates are loaded from disk (enabling hot-reload). Otherwise, embedded content is used.

Auto Trait Implementations§

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<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> NoneValue for T
where T: Default,

Source§

type NoneType = T

Source§

fn null_value() -> T

The none-equivalent value.
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 = Infallible

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

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

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<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

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