pub struct PluginManager { /* private fields */ }Expand description
Coordinates plugin discovery, cached metadata, and dispatch settings.
This is the main host-side facade for plugin integration. A typical caller constructs one manager, points it at explicit roots plus optional config and cache roots, then asks it for one of three things:
- plugin inventory via
PluginManager::list_plugins - merged command metadata via
PluginManager::command_catalogorPluginManager::command_policy_registry - dispatch-time configuration such as manager-local provider selections
If you are implementing the plugin executable itself rather than the host,
start in crate::core::plugin instead of here.
Implementations§
Source§impl PluginManager
impl PluginManager
Sourcepub fn refresh(&self)
pub fn refresh(&self)
Clears both passive and dispatch discovery caches.
Call this after changing plugin search roots or the filesystem state they point at so later browse or dispatch calls rescan discovery inputs. This does not clear in-memory command preferences such as provider selections.
§Examples
use osp_cli::plugin::PluginManager;
let manager = PluginManager::new(Vec::new());
manager.refresh();
assert!(manager.list_plugins().is_empty());Source§impl PluginManager
impl PluginManager
Sourcepub fn dispatch(
&self,
command: &str,
args: &[String],
context: &PluginDispatchContext,
) -> Result<ResponseV1, PluginDispatchError>
pub fn dispatch( &self, command: &str, args: &[String], context: &PluginDispatchContext, ) -> Result<ResponseV1, PluginDispatchError>
Runs a plugin command and returns its validated structured response.
command is the full command path resolved against the active plugin
catalog. args are passed to the plugin after that command name.
context carries runtime hints, optional environment overrides, and an
optional one-shot provider override for this dispatch only.
§Errors
Returns PluginDispatchError when provider resolution fails, the
plugin subprocess cannot be executed, the subprocess times out, the
plugin exits non-zero, or the returned JSON is syntactically or
semantically invalid.
§Examples
use osp_cli::plugin::{PluginDispatchContext, PluginDispatchError, PluginManager};
let err = PluginManager::new(Vec::new())
.dispatch("shared", &[], &PluginDispatchContext::default())
.unwrap_err();
assert!(matches!(err, PluginDispatchError::CommandNotFound { .. }));Sourcepub fn dispatch_passthrough(
&self,
command: &str,
args: &[String],
context: &PluginDispatchContext,
) -> Result<RawPluginOutput, PluginDispatchError>
pub fn dispatch_passthrough( &self, command: &str, args: &[String], context: &PluginDispatchContext, ) -> Result<RawPluginOutput, PluginDispatchError>
Runs a plugin command and returns raw stdout, stderr, and exit status.
Unlike PluginManager::dispatch, this does not attempt to decode or
validate plugin JSON output. Non-zero exit codes are returned in
RawPluginOutput::status_code rather than surfaced as
PluginDispatchError::NonZeroExit.
§Errors
Returns PluginDispatchError when provider resolution fails, the
plugin subprocess cannot be executed, or the subprocess times out.
§Examples
use osp_cli::plugin::{PluginDispatchContext, PluginDispatchError, PluginManager};
let err = PluginManager::new(Vec::new())
.dispatch_passthrough("shared", &[], &PluginDispatchContext::default())
.unwrap_err();
assert!(matches!(err, PluginDispatchError::CommandNotFound { .. }));Source§impl PluginManager
impl PluginManager
Sourcepub fn new(explicit_dirs: Vec<PathBuf>) -> Self
pub fn new(explicit_dirs: Vec<PathBuf>) -> Self
Creates a plugin manager with the provided explicit search roots.
§Examples
use osp_cli::plugin::PluginManager;
use std::path::PathBuf;
let manager = PluginManager::new(vec![PathBuf::from("/plugins")]);
assert_eq!(manager.explicit_dirs().len(), 1);Sourcepub fn explicit_dirs(&self) -> &[PathBuf]
pub fn explicit_dirs(&self) -> &[PathBuf]
Returns the explicit plugin search roots configured for this manager.
Sourcepub fn with_roots(
self,
config_root: Option<PathBuf>,
cache_root: Option<PathBuf>,
) -> Self
pub fn with_roots( self, config_root: Option<PathBuf>, cache_root: Option<PathBuf>, ) -> Self
Sets config and cache roots used for user plugin discovery and describe cache files.
The config root feeds the per-user plugin directory lookup. The cache root feeds the on-disk describe cache. This does not make command provider selections persistent by itself; those remain manager-local in-memory state.
§Examples
use osp_cli::plugin::PluginManager;
use std::path::PathBuf;
let manager = PluginManager::new(Vec::new()).with_roots(
Some(PathBuf::from("/config")),
Some(PathBuf::from("/cache")),
);
assert_eq!(manager.config_root(), Some(PathBuf::from("/config").as_path()));
assert_eq!(manager.cache_root(), Some(PathBuf::from("/cache").as_path()));Sourcepub fn config_root(&self) -> Option<&Path>
pub fn config_root(&self) -> Option<&Path>
Returns the configured config root used to resolve the user plugin directory.
Sourcepub fn cache_root(&self) -> Option<&Path>
pub fn cache_root(&self) -> Option<&Path>
Returns the configured cache root used for the describe metadata cache.
Sourcepub fn with_default_roots(self, allow_default_roots: bool) -> Self
pub fn with_default_roots(self, allow_default_roots: bool) -> Self
Enables or disables fallback to platform config/cache roots when explicit roots are not configured.
The default is true. Disable this when the caller wants plugin
discovery and describe-cache state to stay fully in-memory unless
explicit roots are provided.
Sourcepub fn default_roots_enabled(&self) -> bool
pub fn default_roots_enabled(&self) -> bool
Returns whether platform config/cache root fallback is enabled.
Sourcepub fn with_process_timeout(self, timeout: Duration) -> Self
pub fn with_process_timeout(self, timeout: Duration) -> Self
Sets the subprocess timeout used for plugin describe and dispatch calls.
Timeout values are clamped to at least one millisecond so the manager never stores a zero-duration subprocess timeout.
§Examples
use osp_cli::plugin::PluginManager;
use std::time::Duration;
let manager = PluginManager::new(Vec::new())
.with_process_timeout(Duration::from_millis(0));
assert_eq!(manager.process_timeout(), Duration::from_millis(1));Sourcepub fn process_timeout(&self) -> Duration
pub fn process_timeout(&self) -> Duration
Returns the subprocess timeout used for describe and dispatch calls.
Sourcepub fn with_path_discovery(self, allow_path_discovery: bool) -> Self
pub fn with_path_discovery(self, allow_path_discovery: bool) -> Self
Enables or disables fallback discovery through the process PATH.
PATH discovery is passive on browse/read surfaces. A PATH-discovered
plugin will not be executed for --describe during passive listing or
catalog building, so command metadata is unavailable there until the
first command dispatch to that plugin. Dispatching a command triggers
--describe as a cache miss and writes the result to the on-disk
describe cache; subsequent browse and catalog calls will then see the
full command metadata.
§Examples
use osp_cli::plugin::PluginManager;
let manager = PluginManager::new(Vec::new()).with_path_discovery(true);
assert!(manager.path_discovery_enabled());Sourcepub fn path_discovery_enabled(&self) -> bool
pub fn path_discovery_enabled(&self) -> bool
Returns whether fallback discovery through the process PATH is enabled.
Sourcepub fn list_plugins(&self) -> Vec<PluginSummary>
pub fn list_plugins(&self) -> Vec<PluginSummary>
Lists discovered plugins with health, command, and enablement status.
When PATH discovery is enabled, PATH-discovered plugins can appear here
before their command metadata is available because passive discovery
does not execute them for --describe.
§Examples
use osp_cli::plugin::PluginManager;
let plugins = PluginManager::new(Vec::new()).list_plugins();
assert!(plugins.is_empty());Sourcepub fn command_catalog(&self) -> Vec<CommandCatalogEntry>
pub fn command_catalog(&self) -> Vec<CommandCatalogEntry>
Builds the effective command catalog after provider resolution and health filtering.
This is the host-facing “what commands exist?” view used by help, completion, and similar browse/read surfaces. PATH-discovered plugins only contribute commands here after describe metadata has been cached; passive discovery alone is not enough.
§Examples
use osp_cli::plugin::PluginManager;
let catalog = PluginManager::new(Vec::new()).command_catalog();
assert!(catalog.is_empty());Sourcepub fn command_policy_registry(&self) -> CommandPolicyRegistry
pub fn command_policy_registry(&self) -> CommandPolicyRegistry
Builds a command policy registry from active plugin describe metadata.
Use this when plugin auth hints need to participate in the same runtime visibility and access evaluation as native commands. Commands that still require provider selection are omitted until one provider is selected explicitly.
§Examples
use osp_cli::plugin::PluginManager;
let registry = PluginManager::new(Vec::new()).command_policy_registry();
assert!(registry.is_empty());Sourcepub fn completion_words(&self) -> Vec<String>
pub fn completion_words(&self) -> Vec<String>
Returns completion words derived from the current plugin command catalog.
The returned list always includes the REPL backbone words used by the plugin/completion surface, even when no plugins are currently available.
§Examples
use osp_cli::plugin::PluginManager;
let words = PluginManager::new(Vec::new()).completion_words();
assert!(words.contains(&"help".to_string()));
assert!(words.contains(&"|".to_string()));Sourcepub fn repl_help_text(&self) -> String
pub fn repl_help_text(&self) -> String
Renders a plain-text help view for plugin commands in the REPL.
§Examples
use osp_cli::plugin::PluginManager;
let help = PluginManager::new(Vec::new()).repl_help_text();
assert!(help.contains("Backbone commands: help, exit, quit"));
assert!(help.contains("No plugin commands available."));Sourcepub fn command_providers(&self, command: &str) -> Vec<String>
pub fn command_providers(&self, command: &str) -> Vec<String>
Returns the available provider labels for a command after health and enablement filtering.
Unknown commands and commands with no currently available providers return an empty list.
§Examples
use osp_cli::plugin::PluginManager;
let providers = PluginManager::new(Vec::new()).command_providers("shared");
assert!(providers.is_empty());Sourcepub fn selected_provider_label(&self, command: &str) -> Option<String>
pub fn selected_provider_label(&self, command: &str) -> Option<String>
Returns the selected provider label when command resolution is unambiguous.
Returns None when the command is unknown, ambiguous, or currently
unavailable after health and enablement filtering.
§Examples
use osp_cli::plugin::PluginManager;
let provider = PluginManager::new(Vec::new()).selected_provider_label("shared");
assert_eq!(provider, None);Sourcepub fn doctor(&self) -> DoctorReport
pub fn doctor(&self) -> DoctorReport
Produces a doctor report with plugin health summaries and command conflicts.
§Examples
use osp_cli::plugin::PluginManager;
let report = PluginManager::new(Vec::new()).doctor();
assert!(report.plugins.is_empty());
assert!(report.conflicts.is_empty());Sourcepub fn select_provider(&self, command: &str, plugin_id: &str) -> Result<()>
pub fn select_provider(&self, command: &str, plugin_id: &str) -> Result<()>
Applies an explicit provider selection for a command on this manager.
The selection is kept in the manager’s in-memory command-preference
state and affects subsequent command resolution through this
PluginManager value. It is not written to disk.
§Examples
use osp_cli::plugin::PluginManager;
let manager = PluginManager::new(vec![plugins_dir]);
assert_eq!(manager.selected_provider_label("shared"), None);
manager.select_provider("shared", "beta")?;
assert_eq!(
manager.selected_provider_label("shared").as_deref(),
Some("beta (explicit)")
);§Errors
Returns an error when command or plugin_id is blank, when no
healthy provider currently exports command, or when plugin_id is
not one of the healthy providers for command.
Sourcepub fn clear_provider_selection(&self, command: &str) -> Result<bool>
pub fn clear_provider_selection(&self, command: &str) -> Result<bool>
Sourcepub fn validate_provider_selection(
&self,
command: &str,
plugin_id: &str,
) -> Result<()>
pub fn validate_provider_selection( &self, command: &str, plugin_id: &str, ) -> Result<()>
Verifies that a plugin is a healthy provider candidate for a command.
This validates the command/plugin pair against the manager’s current discovery view but does not change selection state or persist anything.
§Examples
use osp_cli::plugin::PluginManager;
let err = PluginManager::new(Vec::new())
.validate_provider_selection("shared", "alpha")
.unwrap_err();
assert!(err.to_string().contains("no healthy plugin provides command"));§Errors
Returns an error when no healthy provider currently exports command,
or when plugin_id is not one of the healthy providers for command.
Auto Trait Implementations§
impl !Freeze for PluginManager
impl RefUnwindSafe for PluginManager
impl Send for PluginManager
impl Sync for PluginManager
impl Unpin for PluginManager
impl UnsafeUnpin for PluginManager
impl UnwindSafe for PluginManager
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<D> OwoColorize for D
impl<D> OwoColorize for D
Source§fn fg<C>(&self) -> FgColorDisplay<'_, C, Self>where
C: Color,
fn fg<C>(&self) -> FgColorDisplay<'_, C, Self>where
C: Color,
Source§fn bg<C>(&self) -> BgColorDisplay<'_, C, Self>where
C: Color,
fn bg<C>(&self) -> BgColorDisplay<'_, C, Self>where
C: Color,
Source§fn black(&self) -> FgColorDisplay<'_, Black, Self>
fn black(&self) -> FgColorDisplay<'_, Black, Self>
Source§fn on_black(&self) -> BgColorDisplay<'_, Black, Self>
fn on_black(&self) -> BgColorDisplay<'_, Black, Self>
Source§fn red(&self) -> FgColorDisplay<'_, Red, Self>
fn red(&self) -> FgColorDisplay<'_, Red, Self>
Source§fn on_red(&self) -> BgColorDisplay<'_, Red, Self>
fn on_red(&self) -> BgColorDisplay<'_, Red, Self>
Source§fn green(&self) -> FgColorDisplay<'_, Green, Self>
fn green(&self) -> FgColorDisplay<'_, Green, Self>
Source§fn on_green(&self) -> BgColorDisplay<'_, Green, Self>
fn on_green(&self) -> BgColorDisplay<'_, Green, Self>
Source§fn yellow(&self) -> FgColorDisplay<'_, Yellow, Self>
fn yellow(&self) -> FgColorDisplay<'_, Yellow, Self>
Source§fn on_yellow(&self) -> BgColorDisplay<'_, Yellow, Self>
fn on_yellow(&self) -> BgColorDisplay<'_, Yellow, Self>
Source§fn blue(&self) -> FgColorDisplay<'_, Blue, Self>
fn blue(&self) -> FgColorDisplay<'_, Blue, Self>
Source§fn on_blue(&self) -> BgColorDisplay<'_, Blue, Self>
fn on_blue(&self) -> BgColorDisplay<'_, Blue, Self>
Source§fn magenta(&self) -> FgColorDisplay<'_, Magenta, Self>
fn magenta(&self) -> FgColorDisplay<'_, Magenta, Self>
Source§fn on_magenta(&self) -> BgColorDisplay<'_, Magenta, Self>
fn on_magenta(&self) -> BgColorDisplay<'_, Magenta, Self>
Source§fn purple(&self) -> FgColorDisplay<'_, Magenta, Self>
fn purple(&self) -> FgColorDisplay<'_, Magenta, Self>
Source§fn on_purple(&self) -> BgColorDisplay<'_, Magenta, Self>
fn on_purple(&self) -> BgColorDisplay<'_, Magenta, Self>
Source§fn cyan(&self) -> FgColorDisplay<'_, Cyan, Self>
fn cyan(&self) -> FgColorDisplay<'_, Cyan, Self>
Source§fn on_cyan(&self) -> BgColorDisplay<'_, Cyan, Self>
fn on_cyan(&self) -> BgColorDisplay<'_, Cyan, Self>
Source§fn white(&self) -> FgColorDisplay<'_, White, Self>
fn white(&self) -> FgColorDisplay<'_, White, Self>
Source§fn on_white(&self) -> BgColorDisplay<'_, White, Self>
fn on_white(&self) -> BgColorDisplay<'_, White, Self>
Source§fn default_color(&self) -> FgColorDisplay<'_, Default, Self>
fn default_color(&self) -> FgColorDisplay<'_, Default, Self>
Source§fn on_default_color(&self) -> BgColorDisplay<'_, Default, Self>
fn on_default_color(&self) -> BgColorDisplay<'_, Default, Self>
Source§fn bright_black(&self) -> FgColorDisplay<'_, BrightBlack, Self>
fn bright_black(&self) -> FgColorDisplay<'_, BrightBlack, Self>
Source§fn on_bright_black(&self) -> BgColorDisplay<'_, BrightBlack, Self>
fn on_bright_black(&self) -> BgColorDisplay<'_, BrightBlack, Self>
Source§fn bright_red(&self) -> FgColorDisplay<'_, BrightRed, Self>
fn bright_red(&self) -> FgColorDisplay<'_, BrightRed, Self>
Source§fn on_bright_red(&self) -> BgColorDisplay<'_, BrightRed, Self>
fn on_bright_red(&self) -> BgColorDisplay<'_, BrightRed, Self>
Source§fn bright_green(&self) -> FgColorDisplay<'_, BrightGreen, Self>
fn bright_green(&self) -> FgColorDisplay<'_, BrightGreen, Self>
Source§fn on_bright_green(&self) -> BgColorDisplay<'_, BrightGreen, Self>
fn on_bright_green(&self) -> BgColorDisplay<'_, BrightGreen, Self>
Source§fn bright_yellow(&self) -> FgColorDisplay<'_, BrightYellow, Self>
fn bright_yellow(&self) -> FgColorDisplay<'_, BrightYellow, Self>
Source§fn on_bright_yellow(&self) -> BgColorDisplay<'_, BrightYellow, Self>
fn on_bright_yellow(&self) -> BgColorDisplay<'_, BrightYellow, Self>
Source§fn bright_blue(&self) -> FgColorDisplay<'_, BrightBlue, Self>
fn bright_blue(&self) -> FgColorDisplay<'_, BrightBlue, Self>
Source§fn on_bright_blue(&self) -> BgColorDisplay<'_, BrightBlue, Self>
fn on_bright_blue(&self) -> BgColorDisplay<'_, BrightBlue, Self>
Source§fn bright_magenta(&self) -> FgColorDisplay<'_, BrightMagenta, Self>
fn bright_magenta(&self) -> FgColorDisplay<'_, BrightMagenta, Self>
Source§fn on_bright_magenta(&self) -> BgColorDisplay<'_, BrightMagenta, Self>
fn on_bright_magenta(&self) -> BgColorDisplay<'_, BrightMagenta, Self>
Source§fn bright_purple(&self) -> FgColorDisplay<'_, BrightMagenta, Self>
fn bright_purple(&self) -> FgColorDisplay<'_, BrightMagenta, Self>
Source§fn on_bright_purple(&self) -> BgColorDisplay<'_, BrightMagenta, Self>
fn on_bright_purple(&self) -> BgColorDisplay<'_, BrightMagenta, Self>
Source§fn bright_cyan(&self) -> FgColorDisplay<'_, BrightCyan, Self>
fn bright_cyan(&self) -> FgColorDisplay<'_, BrightCyan, Self>
Source§fn on_bright_cyan(&self) -> BgColorDisplay<'_, BrightCyan, Self>
fn on_bright_cyan(&self) -> BgColorDisplay<'_, BrightCyan, Self>
Source§fn bright_white(&self) -> FgColorDisplay<'_, BrightWhite, Self>
fn bright_white(&self) -> FgColorDisplay<'_, BrightWhite, Self>
Source§fn on_bright_white(&self) -> BgColorDisplay<'_, BrightWhite, Self>
fn on_bright_white(&self) -> BgColorDisplay<'_, BrightWhite, Self>
Source§fn bold(&self) -> BoldDisplay<'_, Self>
fn bold(&self) -> BoldDisplay<'_, Self>
Source§fn dimmed(&self) -> DimDisplay<'_, Self>
fn dimmed(&self) -> DimDisplay<'_, Self>
Source§fn italic(&self) -> ItalicDisplay<'_, Self>
fn italic(&self) -> ItalicDisplay<'_, Self>
Source§fn underline(&self) -> UnderlineDisplay<'_, Self>
fn underline(&self) -> UnderlineDisplay<'_, Self>
Source§fn blink(&self) -> BlinkDisplay<'_, Self>
fn blink(&self) -> BlinkDisplay<'_, Self>
Source§fn blink_fast(&self) -> BlinkFastDisplay<'_, Self>
fn blink_fast(&self) -> BlinkFastDisplay<'_, Self>
Source§fn reversed(&self) -> ReversedDisplay<'_, Self>
fn reversed(&self) -> ReversedDisplay<'_, Self>
Source§fn strikethrough(&self) -> StrikeThroughDisplay<'_, Self>
fn strikethrough(&self) -> StrikeThroughDisplay<'_, Self>
Source§fn color<Color>(&self, color: Color) -> FgDynColorDisplay<'_, Color, Self>where
Color: DynColor,
fn color<Color>(&self, color: Color) -> FgDynColorDisplay<'_, Color, Self>where
Color: DynColor,
OwoColorize::fg or
a color-specific method, such as OwoColorize::green, Read moreSource§fn on_color<Color>(&self, color: Color) -> BgDynColorDisplay<'_, Color, Self>where
Color: DynColor,
fn on_color<Color>(&self, color: Color) -> BgDynColorDisplay<'_, Color, Self>where
Color: DynColor,
OwoColorize::bg or
a color-specific method, such as OwoColorize::on_yellow, Read more