Skip to main content

systemprompt_extension/traits/
extension.rs

1use std::sync::Arc;
2
3use serde_json::Value as JsonValue;
4use systemprompt_provider_contracts::{
5    ComponentRenderer, ContentDataProvider, FrontmatterProcessor, Job, LlmProvider,
6    PageDataProvider, PagePrerenderer, RssFeedProvider, SitemapProvider, TemplateDataExtender,
7    TemplateProvider, ToolProvider,
8};
9
10use crate::asset::{AssetDefinition, AssetPaths};
11use crate::context::ExtensionContext;
12use crate::error::ConfigError;
13use crate::metadata::{ExtensionMetadata, ExtensionRole, SchemaDefinition};
14use crate::migration::Migration;
15use crate::router::{ExtensionRouter, ExtensionRouterConfig, SiteAuthConfig};
16use crate::seed::Seed;
17
18pub trait Extension: Send + Sync + 'static {
19    fn metadata(&self) -> ExtensionMetadata;
20
21    fn schemas(&self) -> Vec<SchemaDefinition> {
22        vec![]
23    }
24
25    fn router(&self, _ctx: &dyn ExtensionContext) -> Option<ExtensionRouter> {
26        None
27    }
28
29    fn router_config(&self) -> Option<ExtensionRouterConfig> {
30        None
31    }
32
33    fn site_auth(&self) -> Option<SiteAuthConfig> {
34        None
35    }
36
37    /// Per-extension job manifest, for CLI/plugin attribution only.
38    ///
39    /// The scheduler does **not** consult this method: it discovers runnable
40    /// jobs from the `inventory` catalog populated by `submit_job!`. This list
41    /// is used by `jobs list` and plugin-capability commands to attribute a job
42    /// to the extension that owns it; an entry here that is never
43    /// `submit_job!`d is invisible to scheduling.
44    fn jobs(&self) -> Vec<Arc<dyn Job>> {
45        vec![]
46    }
47
48    fn config_prefix(&self) -> Option<&str> {
49        None
50    }
51
52    fn config_schema(&self) -> Option<JsonValue> {
53        None
54    }
55
56    fn validate_config(&self, _config: &JsonValue) -> Result<(), ConfigError> {
57        Ok(())
58    }
59
60    fn llm_providers(&self) -> Vec<Arc<dyn LlmProvider>> {
61        vec![]
62    }
63
64    fn tool_providers(&self) -> Vec<Arc<dyn ToolProvider>> {
65        vec![]
66    }
67
68    fn template_providers(&self) -> Vec<Arc<dyn TemplateProvider>> {
69        vec![]
70    }
71
72    fn component_renderers(&self) -> Vec<Arc<dyn ComponentRenderer>> {
73        vec![]
74    }
75
76    fn template_data_extenders(&self) -> Vec<Arc<dyn TemplateDataExtender>> {
77        vec![]
78    }
79
80    fn page_data_providers(&self) -> Vec<Arc<dyn PageDataProvider>> {
81        vec![]
82    }
83
84    fn page_prerenderers(&self) -> Vec<Arc<dyn PagePrerenderer>> {
85        vec![]
86    }
87
88    fn frontmatter_processors(&self) -> Vec<Arc<dyn FrontmatterProcessor>> {
89        vec![]
90    }
91
92    fn content_data_providers(&self) -> Vec<Arc<dyn ContentDataProvider>> {
93        vec![]
94    }
95
96    fn rss_feed_providers(&self) -> Vec<Arc<dyn RssFeedProvider>> {
97        vec![]
98    }
99
100    fn sitemap_providers(&self) -> Vec<Arc<dyn SitemapProvider>> {
101        vec![]
102    }
103
104    fn required_storage_paths(&self) -> Vec<&'static str> {
105        vec![]
106    }
107
108    fn dependencies(&self) -> Vec<&'static str> {
109        vec![]
110    }
111
112    fn is_required(&self) -> bool {
113        false
114    }
115
116    fn migrations(&self) -> Vec<Migration> {
117        vec![]
118    }
119
120    fn seeds(&self) -> Vec<Seed> {
121        Vec::new()
122    }
123
124    /// Tables this extension is permitted to mutate with a cross-extension
125    /// `ALTER` even though another extension creates them. The tables an
126    /// extension *owns* are derived from the `CREATE TABLE` statements in its
127    /// [`Extension::schemas`] and must not be repeated here.
128    fn cross_extension_tables(&self) -> Vec<&'static str> {
129        Vec::new()
130    }
131
132    fn roles(&self) -> Vec<ExtensionRole> {
133        vec![]
134    }
135
136    fn priority(&self) -> u32 {
137        100
138    }
139
140    fn id(&self) -> &'static str {
141        self.metadata().id
142    }
143
144    fn name(&self) -> &'static str {
145        self.metadata().name
146    }
147
148    fn version(&self) -> &'static str {
149        self.metadata().version
150    }
151
152    fn has_schemas(&self) -> bool {
153        !self.schemas().is_empty()
154    }
155
156    fn has_router(&self, ctx: &dyn ExtensionContext) -> bool {
157        self.router(ctx).is_some()
158    }
159
160    fn has_jobs(&self) -> bool {
161        !self.jobs().is_empty()
162    }
163
164    fn has_config(&self) -> bool {
165        self.config_prefix().is_some()
166    }
167
168    fn has_llm_providers(&self) -> bool {
169        !self.llm_providers().is_empty()
170    }
171
172    fn has_tool_providers(&self) -> bool {
173        !self.tool_providers().is_empty()
174    }
175
176    fn has_template_providers(&self) -> bool {
177        !self.template_providers().is_empty()
178    }
179
180    fn has_component_renderers(&self) -> bool {
181        !self.component_renderers().is_empty()
182    }
183
184    fn has_template_data_extenders(&self) -> bool {
185        !self.template_data_extenders().is_empty()
186    }
187
188    fn has_page_data_providers(&self) -> bool {
189        !self.page_data_providers().is_empty()
190    }
191
192    fn has_page_prerenderers(&self) -> bool {
193        !self.page_prerenderers().is_empty()
194    }
195
196    fn has_frontmatter_processors(&self) -> bool {
197        !self.frontmatter_processors().is_empty()
198    }
199
200    fn has_content_data_providers(&self) -> bool {
201        !self.content_data_providers().is_empty()
202    }
203
204    fn has_rss_feed_providers(&self) -> bool {
205        !self.rss_feed_providers().is_empty()
206    }
207
208    fn has_sitemap_providers(&self) -> bool {
209        !self.sitemap_providers().is_empty()
210    }
211
212    fn has_site_auth(&self) -> bool {
213        self.site_auth().is_some()
214    }
215
216    fn has_storage_paths(&self) -> bool {
217        !self.required_storage_paths().is_empty()
218    }
219
220    fn has_roles(&self) -> bool {
221        !self.roles().is_empty()
222    }
223
224    fn has_migrations(&self) -> bool {
225        !self.migrations().is_empty()
226    }
227
228    fn declares_assets(&self) -> bool {
229        false
230    }
231
232    fn required_assets(&self, _paths: &dyn AssetPaths) -> Vec<AssetDefinition> {
233        vec![]
234    }
235}