1use crate::manifest::PluginManifest;
2use codex_protocol::capabilities::SelectedCapabilityRoot;
3use codex_utils_path_uri::PathUri;
4use std::error::Error as StdError;
5use std::future::Future;
6use thiserror::Error;
7
8#[derive(Clone, Debug, PartialEq, Eq)]
10pub enum PluginResourceLocator {
11 Environment {
12 environment_id: String,
14 path: PathUri,
16 },
17}
18
19#[derive(Clone, Debug, PartialEq, Eq)]
21pub enum ResolvedPluginLocation {
22 Environment {
23 environment_id: String,
25 root: PathUri,
27 },
28}
29
30#[derive(Clone, Debug, PartialEq, Eq)]
32pub struct ResolvedPlugin {
33 selected_root_id: String,
34 location: ResolvedPluginLocation,
35 manifest_path: PluginResourceLocator,
36 manifest: PluginManifest<PluginResourceLocator>,
37}
38
39#[derive(Clone, Debug, Error, PartialEq, Eq)]
41pub enum ResolvedPluginError {
42 #[error("plugin resource path `{path}` is outside package root `{root}`")]
43 ResourceOutsideRoot { root: PathUri, path: PathUri },
44}
45
46impl ResolvedPlugin {
47 pub fn from_environment(
49 selected_root_id: String,
50 environment_id: String,
51 root: PathUri,
52 manifest_path: PathUri,
53 manifest: PluginManifest<PathUri>,
54 ) -> Result<Self, ResolvedPluginError> {
55 let manifest_path = environment_resource(&environment_id, &root, manifest_path)?;
56 let manifest = manifest
57 .try_map_resources(|path| environment_resource(&environment_id, &root, path))?;
58 Ok(Self {
59 selected_root_id,
60 location: ResolvedPluginLocation::Environment {
61 environment_id,
62 root,
63 },
64 manifest_path,
65 manifest,
66 })
67 }
68
69 pub fn selected_root_id(&self) -> &str {
71 &self.selected_root_id
72 }
73
74 pub fn location(&self) -> &ResolvedPluginLocation {
76 &self.location
77 }
78
79 pub fn manifest_path(&self) -> &PluginResourceLocator {
81 &self.manifest_path
82 }
83
84 pub fn manifest(&self) -> &PluginManifest<PluginResourceLocator> {
86 &self.manifest
87 }
88}
89
90fn environment_resource(
91 environment_id: &str,
92 root: &PathUri,
93 path: PathUri,
94) -> Result<PluginResourceLocator, ResolvedPluginError> {
95 if !path.starts_with(root) {
96 return Err(ResolvedPluginError::ResourceOutsideRoot {
97 root: root.clone(),
98 path,
99 });
100 }
101 Ok(PluginResourceLocator::Environment {
102 environment_id: environment_id.to_string(),
103 path,
104 })
105}
106
107pub trait PluginProvider: Send + Sync {
113 type Error: StdError + Send + Sync + 'static;
115
116 fn resolve(
118 &self,
119 root: &SelectedCapabilityRoot,
120 ) -> impl Future<Output = Result<Option<ResolvedPlugin>, Self::Error>> + Send;
121}
122
123#[cfg(test)]
124#[path = "provider_tests.rs"]
125mod tests;