Skip to main content

ConfigRegistry

Struct ConfigRegistry 

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

Process-shared registry.

Implementations§

Source§

impl ConfigRegistry

Source

pub fn new() -> Self

An empty registry with no event publisher. Options arrive via Self::init_from_linkme (every options! declaration linked into the binary) and Self::register (runtime / plugin options).

§Examples

Declared options, read and written by type:

use lattice_config::{ConfigRegistry, Tabstop};

let reg = ConfigRegistry::new();
assert!(reg.get_typed::<Tabstop>().is_none()); // nothing registered yet
reg.init_from_linkme();

assert_eq!(*reg.get_typed::<Tabstop>().unwrap(), 4); // the declared default
reg.set_typed::<Tabstop>(2).unwrap();
assert_eq!(*reg.get_typed::<Tabstop>().unwrap(), 2);
// The option's validator (1..=32) rejects the write; the old value stays.
assert!(reg.set_typed::<Tabstop>(0).is_err());
assert_eq!(*reg.get_typed::<Tabstop>().unwrap(), 2);

A runtime-registered option, read through its handle:

use lattice_config::ConfigRegistry;
use lattice_config::option::Option;

let reg = ConfigRegistry::new();
let depth = reg.register(
    Option::<i64>::builder("myplugin.depth", 3, "Search depth.")
        .aliases(&["mpd"])
        .validate(|d| if *d > 0 { Ok(()) } else { Err(format!("depth must be > 0, got {d}")) })
        .build(),
);
assert_eq!(*reg.get(depth), 3);
assert_eq!(reg.parse_and_set_command("mpd=5").unwrap(), "myplugin.depth=5");
assert_eq!(reg.with(depth, |d| *d), 5);
assert!(reg.set(depth, -1).is_err());
Source

pub fn set_event_publisher(&self, publisher: EventPublisher)

Install the [Event::OptionChanged] sink. Idempotent replacement – calling twice swaps the closure. Designed to be called once at boot from the consumer that owns the EventBus (the App today; future plugin-host paths route through the same closure).

Source

pub fn register<T: OptionType>(&self, option: Option<T>) -> OptionHandle<T>

Register a typed option. Returns an OptionHandle<T> for hot-path access. Returns Err if option.name or any alias is already registered (duplicate-name detection is a programming-error guard; recoverable via the Result so the caller can decide whether a duplicate is fatal).

Source

pub fn try_register<T: OptionType>( &self, option: Option<T>, ) -> Result<OptionHandle<T>, ConfigError>

Fallible registration. Most callers want Self::register; this exists for plugins / dynamic code that can fail gracefully on a name collision.

Source

pub fn register_with_typeid<T: OptionType>( &self, option: Option<T>, type_id: TypeId, ) -> OptionHandle<T>

Variant of Self::register that additionally records a TypeId ↔ option-index mapping for type-keyed reads via Self::get_typed. Called from the crate::options! macro’s register_fn thunk during Self::init_from_linkme; not generally called by hand.

type_id is TypeId::of::<D>() where D is the crate::OptionDecl type. Two declarations with the same TypeId cannot exist (Rust’s type system enforces it cross-crate), so duplicate-typeid is a programming error and panics; the macro never produces it.

Source

pub fn get_typed<D: OptionDecl>(&self) -> Option<Arc<D::Value>>
where D::Value: Clone + Send + Sync + 'static,

Type-keyed read: returns the resolved value for the OptionDecl type D. D::Value is the value type. Returns None if the option has not been registered (no register_with_typeid call seen for this TypeId); this is legitimate transient state during boot before Self::init_from_linkme runs.

Source

pub fn handle_for_decl<D: OptionDecl>(&self) -> Option<OptionHandle<D::Value>>

Type-keyed handle lookup: recover the legacy OptionHandle<D::Value> from an OptionDecl type. Used by the M.2.0b backwards-compatibility shim in core_options::register_core_options to populate the CoreOptions struct with handles after Self::init_from_linkme has run.

Returns None if D was not registered. M.2.0c retires the OptionHandle<T> API and this method along with it.

Source

pub fn set_typed<D: OptionDecl>(&self, value: D::Value) -> Result<(), String>
where D::Value: Clone + Send + Sync + 'static,

Type-keyed write: set the option declared by D to value. Runs the option’s validator before committing, publishes [Event::OptionChanged] on success, returns the validator error (or a missing-registration error) on failure.

Hot-path-equivalent to the legacy set(handle, value) shape; one TypeId lookup per write. Writes are rare (user :set foo=bar cmdline, programmatic toggles), so the extra hash bears no measurable cost.

Source

pub fn bootstrap_resolved_with_current_values(&self, out: &mut ResolvedOptions)

Bootstrap a crate::ResolvedOptions cache with every registered option’s current value (layer 5 + 6 of the resolution stack – mode-architecture.md §6.1). Walks the typeid map and writes each option’s crate::ErasedOption::current_value_erased into the cache, keyed by TypeId.

Used by crate::Resolver::resolve_into callers that want a fully-populated resolved cache without manually chaining a “default” layer. After this returns, the caller layers any mode / buffer-local / modal overrides on top via the resolver.

Source

pub fn type_id_for_name(&self, name: &str) -> Option<TypeId>

Look up the TypeId registered for an option’s canonical name. Returns None if the option was not registered via register_with_typeid (e.g. a bare try_register call, or the option simply doesn’t exist).

Source

pub fn init_from_linkme(&self)

Boot loop: walk the OPTION_DECLS linkme slice and register every option declared anywhere in the workspace. Idempotent: calling more than once is a no-op (the second call observes that every option is already registered and returns without doing anything). Boot panics on a true programming error (e.g. two distinct OptionDecl types happen to declare the same display name across crates).

After this returns, every options! { ... } declaration is reachable via Self::get_typed / Self::lookup / the cmdline :set / TOML loader. Boot is the moment when “the option exists” goes from “compile-time fact in the declaring crate” to “runtime fact in the registry.”

Source

pub fn get<T: OptionType>(&self, handle: OptionHandle<T>) -> Arc<T> ⓘ

Wait-free typed read.

Returns Arc<T>; Arc::clone is one atomic increment. Returns the option’s default-via-clone fallback if the handle’s type or index doesn’t match – safer than panicking, callers can detect via Self::try_get if they care to. Handles obtained from Self::register are always type-correct, so this only matters for forged ones.

Source

pub fn try_get<T: OptionType>(&self, handle: OptionHandle<T>) -> Option<Arc<T>>

Fallible typed read. Returns None if the handle’s index is out of range or refers to an option of a different T.

Source

pub fn with<T: OptionType, R>( &self, handle: OptionHandle<T>, f: impl FnOnce(&T) -> R, ) -> R

Closure-style read. Avoids the Arc::clone cost of Self::get for one-shot reads.

Source

pub fn set<T: OptionType>( &self, handle: OptionHandle<T>, value: T, ) -> Result<(), String>

Typed write through a handle. Runs the option’s validator before committing. Publishes [Event::OptionChanged] on success.

Source

pub fn lookup(&self, name: &str) -> Option<Arc<dyn ErasedOption>>

Look up an option by name (or alias). Returns the erased view – the by-name path is for cmdline / customize / plugin introspection where the type isn’t known at compile time.

Source

pub fn get_bool_by_name(&self, name: &str) -> Option<bool>

Read a bool-typed option’s current value by name. Returns None if the option doesn’t exist, isn’t boolean, or its erased current value fails to downcast to bool (last case is defense-in-depth – shouldn’t happen given is_bool() agreement). Used by the host’s mode-mirror cascade (see Mode::mirrors_option) so display modes can stay in sync with their typed-option counterparts without hardcoded per-mode special cases in the cascade handler.

Source

pub fn get_int_by_name(&self, name: &str) -> Option<i64>

Read an i64-typed option’s current value by name. Returns None if the option doesn’t exist or its erased current value isn’t an i64 (the downcast IS the type check — there’s no is_int predicate, and a wrong-type option simply fails the downcast). The i64 shape covers every integer option (the options! macro stores counts / sizes as i64). Used by subsystems that read a numeric option by name without importing its decl type — e.g. lattice-diff’s UnchangedFoldSource reading ui.diff.context.

Source

pub fn get_string_by_name(&self, name: &str) -> Option<String>

Read a String-typed option’s current value by name. The downcast IS the type check, as with Self::get_int_by_name. Used by subsystems that read an enum-ish option by name without importing its decl type — e.g. the host reading a plugin’s trim-scope, which is registered dynamically and therefore has no decl type to import.

Source

pub fn iter(&self) -> Vec<Arc<dyn ErasedOption>>

Iterate every registered option in registration order. Used by completion (gen:options) and the customize buffer view to enumerate.

Source

pub fn len(&self) -> usize

Number of registered options. Counts live options only — a tombstoned (unregistered) slot does not count (PH7.12b).

Source

pub fn is_empty(&self) -> bool

true when no live option is registered (tombstoned slots do not count).

Source

pub fn unregister(&self, name: &str) -> bool

Remove an option by name (or any alias), the teardown seam for a plugin reload / unload (PH7.12b). Drops every name+alias mapping pointing at the option’s slot plus any TypeId mapping, tombstones the by_id slot (freeing the Arc), and recycles the index through the free-list so a re-register reuses it. Idempotent: false if nothing is registered under name. Driven by the plugin host’s teardown bundle with the option names a plugin registered (PluginState::config_contributions); built-in options have no unload path in practice. Any live OptionHandle for the removed option is left dangling by contract — its slot is None, so a by-handle read returns nothing rather than another option’s value; the index is only ever reused by a new registration, never silently re-pointed under an existing handle.

Source

pub fn record_failed_assignment( &self, name: &str, message: String, source: Option<PathBuf>, )

OC.11c: record that an assignment to name failed.

name is canonicalised here so an alias (:set ts=999) records under the name the option was declared with — a plugin asking about its own config would never think to ask about an alias, and an alias-keyed record is invisible to every reader.

Source

pub fn clear_failed_assignment(&self, name: &str)

OC.11c: forget any failure recorded for name — an assignment to it has since succeeded, so the record describes something untrue.

Source

pub fn clear_all_failed_assignments(&self)

OC.11c: drop every recorded failure.

Called at the start of a config LOAD, which is a fresh reading of the whole file: an option whose failing line the user deleted produces no message at all, so a per-message update would leave its diagnostic behind forever.

Source

pub fn failed_assignment(&self, name: &str) -> Option<ConfigDiagnostic>

OC.11c: the failure recorded against name, if the last assignment to it failed.

None means the last assignment succeeded or there never was one — not distinguished, deliberately: the caller’s question is “can I trust this value”, and both answers are yes.

Source

pub fn parse_and_set_command(&self, input: &str) -> Result<String, ConfigError>

Drive the cmdline :set syntax against the registry. Parses the input through parse_set, then dispatches to the matching option’s parse / set / negate / format path. Returns the echo line on success (canonical-name=value) — the caller surfaces it via the cmdline echo. Forms:

  • :set foo – echoes foo=current for non-bool, sets true for bool (vim convention).
  • :set nofoo – sets bool to false; ConfigError::NotBoolean for any other type.
  • :set foo=value – parses + validates + sets.
  • :set foo? – always echoes the current value.
  • :set foo& – resets to the registered default.

Every successful write publishes [Event::OptionChanged]; a query does not. The outcome also maintains the failed-assignment record (Self::failed_assignment): a failure on a known option is recorded (with no source file), a success clears it, and an unknown name records nothing.

§Examples
use lattice_config::{ConfigError, ConfigRegistry};

let reg = ConfigRegistry::new();
reg.init_from_linkme();

assert_eq!(reg.parse_and_set_command("ts=8").unwrap(), "tabstop=8");
assert_eq!(reg.parse_and_set_command("wrap").unwrap(), "wrap=true");
assert_eq!(reg.parse_and_set_command("nowrap").unwrap(), "wrap=false");
assert_eq!(reg.parse_and_set_command("tabstop&").unwrap(), "tabstop=4");

assert!(matches!(
    reg.parse_and_set_command("notabstop"),
    Err(ConfigError::NotBoolean(_))
));
assert!(matches!(
    reg.parse_and_set_command("nosuch=1"),
    Err(ConfigError::UnknownOption(_))
));

// A rejected write is remembered against the canonical name...
assert!(reg.parse_and_set_command("ts=0").is_err());
assert!(reg.failed_assignment("tabstop").is_some());
// ...until a later write succeeds.
reg.parse_and_set_command("tabstop=2").unwrap();
assert!(reg.failed_assignment("tabstop").is_none());
Source

pub fn parse_for_buffer_local( &self, input: &str, ) -> Result<(TypeId, Arc<dyn Any + Send + Sync>, String), ConfigError>

Parse an option spec string for use as a buffer-local override, without writing to the global registry. Returns the triple (TypeId, erased_value, canonical_name) needed to construct an crate::OptionOverride for the buffer-local layer.

  • NameOnly(name) — for bool options, returns erased true. For non-bool, returns Err (callers echo the value instead).
  • Negate(name) — returns erased false (rejects non-bool via the option’s own error message).
  • Assign { name, value } — parses value against the option’s crate::OptionType and validates without writing.
  • Query(name) — always returns ConfigError::QueryNotAllowed; callers use :setlocal name? echo path instead.
  • Reset(name) — returns ConfigError::QueryNotAllowed; the caller’s :setlocal name& clear-override path handles it before reaching here.
§Examples
use std::any::TypeId;
use lattice_config::{ConfigRegistry, OptionOverride, Tabstop};

let reg = ConfigRegistry::new();
reg.init_from_linkme();

let (type_id, value, name) = reg.parse_for_buffer_local("ts=2").unwrap();
assert_eq!(type_id, TypeId::of::<Tabstop>());
assert_eq!(name, "tabstop");
assert_eq!(value.downcast_ref::<i64>(), Some(&2));
// The global value is untouched.
assert_eq!(*reg.get_typed::<Tabstop>().unwrap(), 4);

let ov = OptionOverride { option_type_id: type_id, value, priority: Default::default() };
assert_eq!(ov.downcast_value::<i64>(), Some(&2));

Trait Implementations§

Source§

impl Debug for ConfigRegistry

Source§

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

Formats the value using the given formatter. Read more
Source§

impl Default for ConfigRegistry

Source§

fn default() -> ConfigRegistry

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

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.

§

impl<T> Instrument for T

§

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

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

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, 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.
§

impl<T> WithSubscriber for T

§

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
§

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

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