mockforge_plugin_core/
datasource.rs

1//! Data source plugin interface
2//!
3//! This module defines the DataSourcePlugin trait and related types for implementing
4//! external data source integrations in MockForge. Data source plugins enable
5//! connecting to databases, APIs, files, and other data sources for enhanced mocking.
6
7use crate::{PluginCapabilities, PluginContext, PluginResult, Result};
8use serde::{Deserialize, Serialize};
9use serde_json::Value;
10use std::collections::HashMap;
11
12/// Data source plugin trait
13///
14/// Implement this trait to create custom data source integrations.
15/// Data source plugins enable MockForge to connect to external data sources
16/// like databases, REST APIs, files, and other systems for dynamic mocking.
17#[async_trait::async_trait]
18pub trait DataSourcePlugin: Send + Sync {
19    /// Get plugin capabilities (permissions and limits)
20    fn capabilities(&self) -> PluginCapabilities;
21
22    /// Initialize the plugin with configuration
23    async fn initialize(&self, config: &DataSourcePluginConfig) -> Result<()>;
24
25    /// Connect to the data source
26    ///
27    /// This method establishes a connection to the data source using
28    /// the provided configuration.
29    ///
30    /// # Arguments
31    /// * `context` - Plugin execution context
32    /// * `config` - Data source configuration
33    ///
34    /// # Returns
35    /// Connection handle
36    async fn connect(
37        &self,
38        context: &PluginContext,
39        config: &DataSourcePluginConfig,
40    ) -> Result<PluginResult<DataConnection>>;
41
42    /// Execute a query against the data source
43    ///
44    /// This method executes a query using the provided connection.
45    ///
46    /// # Arguments
47    /// * `context` - Plugin execution context
48    /// * `connection` - Active connection
49    /// * `query` - Query to execute
50    /// * `config` - Data source configuration
51    ///
52    /// # Returns
53    /// Query results
54    async fn query(
55        &self,
56        context: &PluginContext,
57        connection: &DataConnection,
58        query: &DataQuery,
59        config: &DataSourcePluginConfig,
60    ) -> Result<PluginResult<DataResult>>;
61
62    /// Get data source schema information
63    ///
64    /// This method retrieves schema information about the data source,
65    /// such as available tables, columns, and relationships.
66    ///
67    /// # Arguments
68    /// * `context` - Plugin execution context
69    /// * `connection` - Active connection
70    /// * `config` - Data source configuration
71    ///
72    /// # Returns
73    /// Schema information
74    async fn get_schema(
75        &self,
76        context: &PluginContext,
77        connection: &DataConnection,
78        config: &DataSourcePluginConfig,
79    ) -> Result<PluginResult<Schema>>;
80
81    /// Test the data source connection
82    ///
83    /// This method tests whether the data source is accessible and
84    /// the configuration is correct.
85    ///
86    /// # Arguments
87    /// * `context` - Plugin execution context
88    /// * `config` - Data source configuration
89    ///
90    /// # Returns
91    /// Connection test result
92    async fn test_connection(
93        &self,
94        context: &PluginContext,
95        config: &DataSourcePluginConfig,
96    ) -> Result<PluginResult<ConnectionTestResult>>;
97
98    /// Validate plugin configuration
99    fn validate_config(&self, config: &DataSourcePluginConfig) -> Result<()>;
100
101    /// Get supported data source types
102    fn supported_types(&self) -> Vec<String>;
103
104    /// Cleanup plugin resources
105    async fn cleanup(&self) -> Result<()>;
106}
107
108/// Data source plugin configuration
109#[derive(Debug, Clone, Serialize, Deserialize)]
110pub struct DataSourcePluginConfig {
111    /// Plugin-specific configuration
112    pub config: HashMap<String, serde_json::Value>,
113    /// Enable/disable the plugin
114    pub enabled: bool,
115    /// Data source type (e.g., "postgresql", "mysql", "api", "file")
116    pub data_source_type: String,
117    /// Connection string or endpoint URL
118    pub connection_string: Option<String>,
119    /// Connection timeout in seconds
120    pub connection_timeout_secs: u64,
121    /// Query timeout in seconds
122    pub query_timeout_secs: u64,
123    /// Maximum connections
124    pub max_connections: u32,
125    /// Authentication credentials
126    pub credentials: Option<DataSourceCredentials>,
127    /// SSL/TLS configuration
128    pub ssl_config: Option<SslConfig>,
129    /// Custom settings
130    pub settings: HashMap<String, serde_json::Value>,
131}
132
133impl Default for DataSourcePluginConfig {
134    fn default() -> Self {
135        Self {
136            config: HashMap::new(),
137            enabled: true,
138            data_source_type: "unknown".to_string(),
139            connection_string: None,
140            connection_timeout_secs: 30,
141            query_timeout_secs: 30,
142            max_connections: 10,
143            credentials: None,
144            ssl_config: None,
145            settings: HashMap::new(),
146        }
147    }
148}
149
150/// Data source credentials
151#[derive(Debug, Clone, Serialize, Deserialize)]
152pub struct DataSourceCredentials {
153    /// Username
154    pub username: Option<String>,
155    /// Password
156    pub password: Option<String>,
157    /// API key
158    pub api_key: Option<String>,
159    /// Bearer token
160    pub bearer_token: Option<String>,
161    /// Custom authentication fields
162    pub custom: HashMap<String, String>,
163}
164
165impl DataSourceCredentials {
166    /// Create with username/password
167    pub fn user_pass<S: Into<String>>(username: S, password: S) -> Self {
168        Self {
169            username: Some(username.into()),
170            password: Some(password.into()),
171            api_key: None,
172            bearer_token: None,
173            custom: HashMap::new(),
174        }
175    }
176
177    /// Create with API key
178    pub fn api_key<S: Into<String>>(api_key: S) -> Self {
179        Self {
180            username: None,
181            password: None,
182            api_key: Some(api_key.into()),
183            bearer_token: None,
184            custom: HashMap::new(),
185        }
186    }
187
188    /// Create with bearer token
189    pub fn bearer_token<S: Into<String>>(token: S) -> Self {
190        Self {
191            username: None,
192            password: None,
193            api_key: None,
194            bearer_token: Some(token.into()),
195            custom: HashMap::new(),
196        }
197    }
198}
199
200/// SSL/TLS configuration
201#[derive(Debug, Clone, Serialize, Deserialize, Default)]
202pub struct SslConfig {
203    /// Enable SSL/TLS
204    pub enabled: bool,
205    /// CA certificate path
206    pub ca_cert_path: Option<String>,
207    /// Client certificate path
208    pub client_cert_path: Option<String>,
209    /// Client key path
210    pub client_key_path: Option<String>,
211    /// Skip certificate verification (for development)
212    pub skip_verify: bool,
213    /// Custom SSL settings
214    pub custom: HashMap<String, serde_json::Value>,
215}
216
217/// Data connection handle
218#[derive(Debug, Clone)]
219pub struct DataConnection {
220    /// Connection ID
221    pub id: String,
222    /// Connection type
223    pub connection_type: String,
224    /// Connection metadata
225    pub metadata: HashMap<String, Value>,
226    /// Connection creation time
227    pub created_at: chrono::DateTime<chrono::Utc>,
228    /// Last used time
229    pub last_used: chrono::DateTime<chrono::Utc>,
230    /// Internal connection handle (plugin-specific)
231    pub handle: Value,
232}
233
234impl DataConnection {
235    /// Create a new connection
236    pub fn new<S: Into<String>>(connection_type: S, handle: Value) -> Self {
237        let now = chrono::Utc::now();
238        Self {
239            id: uuid::Uuid::new_v4().to_string(),
240            connection_type: connection_type.into(),
241            metadata: HashMap::new(),
242            created_at: now,
243            last_used: now,
244            handle,
245        }
246    }
247
248    /// Update last used timestamp
249    pub fn mark_used(&mut self) {
250        self.last_used = chrono::Utc::now();
251    }
252
253    /// Add metadata
254    pub fn with_metadata<S: Into<String>>(mut self, key: S, value: Value) -> Self {
255        self.metadata.insert(key.into(), value);
256        self
257    }
258
259    /// Get metadata value
260    pub fn metadata(&self, key: &str) -> Option<&Value> {
261        self.metadata.get(key)
262    }
263
264    /// Check if connection is stale (older than specified duration)
265    pub fn is_stale(&self, max_age: chrono::Duration) -> bool {
266        chrono::Utc::now().signed_duration_since(self.last_used) > max_age
267    }
268}
269
270/// Data query specification
271#[derive(Debug, Clone, Serialize, Deserialize)]
272pub struct DataQuery {
273    /// Query type
274    pub query_type: QueryType,
275    /// Query string or specification
276    pub query: String,
277    /// Query parameters
278    pub parameters: HashMap<String, Value>,
279    /// Result limit
280    pub limit: Option<usize>,
281    /// Result offset
282    pub offset: Option<usize>,
283    /// Sort specification
284    pub sort: Option<Vec<SortField>>,
285    /// Filter conditions
286    pub filters: Vec<QueryFilter>,
287    /// Custom query options
288    pub options: HashMap<String, Value>,
289}
290
291impl DataQuery {
292    /// Create a simple SELECT query
293    pub fn select<S: Into<String>>(query: S) -> Self {
294        Self {
295            query_type: QueryType::Select,
296            query: query.into(),
297            parameters: HashMap::new(),
298            limit: None,
299            offset: None,
300            sort: None,
301            filters: Vec::new(),
302            options: HashMap::new(),
303        }
304    }
305
306    /// Create an INSERT query
307    pub fn insert<S: Into<String>>(query: S) -> Self {
308        Self {
309            query_type: QueryType::Insert,
310            query: query.into(),
311            parameters: HashMap::new(),
312            limit: None,
313            offset: None,
314            sort: None,
315            filters: Vec::new(),
316            options: HashMap::new(),
317        }
318    }
319
320    /// Create an UPDATE query
321    pub fn update<S: Into<String>>(query: S) -> Self {
322        Self {
323            query_type: QueryType::Update,
324            query: query.into(),
325            parameters: HashMap::new(),
326            limit: None,
327            offset: None,
328            sort: None,
329            filters: Vec::new(),
330            options: HashMap::new(),
331        }
332    }
333
334    /// Create a DELETE query
335    pub fn delete<S: Into<String>>(query: S) -> Self {
336        Self {
337            query_type: QueryType::Delete,
338            query: query.into(),
339            parameters: HashMap::new(),
340            limit: None,
341            offset: None,
342            sort: None,
343            filters: Vec::new(),
344            options: HashMap::new(),
345        }
346    }
347
348    /// Add a parameter
349    pub fn with_parameter<S: Into<String>>(mut self, key: S, value: Value) -> Self {
350        self.parameters.insert(key.into(), value);
351        self
352    }
353
354    /// Set limit
355    pub fn with_limit(mut self, limit: usize) -> Self {
356        self.limit = Some(limit);
357        self
358    }
359
360    /// Set offset
361    pub fn with_offset(mut self, offset: usize) -> Self {
362        self.offset = Some(offset);
363        self
364    }
365
366    /// Add sort field
367    pub fn with_sort(mut self, field: SortField) -> Self {
368        self.sort.get_or_insert_with(Vec::new).push(field);
369        self
370    }
371
372    /// Add filter
373    pub fn with_filter(mut self, filter: QueryFilter) -> Self {
374        self.filters.push(filter);
375        self
376    }
377
378    /// Add option
379    pub fn with_option<S: Into<String>>(mut self, key: S, value: Value) -> Self {
380        self.options.insert(key.into(), value);
381        self
382    }
383}
384
385/// Query type enumeration
386#[derive(Debug, Clone, Serialize, Deserialize)]
387pub enum QueryType {
388    /// SELECT query
389    Select,
390    /// INSERT query
391    Insert,
392    /// UPDATE query
393    Update,
394    /// DELETE query
395    Delete,
396    /// Custom query type
397    Custom(String),
398}
399
400impl fmt::Display for QueryType {
401    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
402        match self {
403            QueryType::Select => write!(f, "SELECT"),
404            QueryType::Insert => write!(f, "INSERT"),
405            QueryType::Update => write!(f, "UPDATE"),
406            QueryType::Delete => write!(f, "DELETE"),
407            QueryType::Custom(custom) => write!(f, "{}", custom),
408        }
409    }
410}
411
412use std::fmt;
413
414/// Sort field specification
415#[derive(Debug, Clone, Serialize, Deserialize)]
416pub struct SortField {
417    /// Field name
418    pub field: String,
419    /// Sort direction
420    pub direction: SortDirection,
421}
422
423impl SortField {
424    /// Create ascending sort
425    pub fn asc<S: Into<String>>(field: S) -> Self {
426        Self {
427            field: field.into(),
428            direction: SortDirection::Ascending,
429        }
430    }
431
432    /// Create descending sort
433    pub fn desc<S: Into<String>>(field: S) -> Self {
434        Self {
435            field: field.into(),
436            direction: SortDirection::Descending,
437        }
438    }
439}
440
441/// Sort direction
442#[derive(Debug, Clone, Serialize, Deserialize)]
443pub enum SortDirection {
444    /// Ascending order
445    Ascending,
446    /// Descending order
447    Descending,
448}
449
450/// Query filter specification
451#[derive(Debug, Clone, Serialize, Deserialize)]
452pub struct QueryFilter {
453    /// Field name
454    pub field: String,
455    /// Filter operator
456    pub operator: FilterOperator,
457    /// Filter value
458    pub value: Value,
459    /// Logical AND/OR with next filter
460    pub logical_op: Option<LogicalOperator>,
461}
462
463impl QueryFilter {
464    /// Create equality filter
465    pub fn equals<S: Into<String>>(field: S, value: Value) -> Self {
466        Self {
467            field: field.into(),
468            operator: FilterOperator::Equals,
469            value,
470            logical_op: None,
471        }
472    }
473
474    /// Create greater than filter
475    pub fn greater_than<S: Into<String>>(field: S, value: Value) -> Self {
476        Self {
477            field: field.into(),
478            operator: FilterOperator::GreaterThan,
479            value,
480            logical_op: None,
481        }
482    }
483
484    /// Create less than filter
485    pub fn less_than<S: Into<String>>(field: S, value: Value) -> Self {
486        Self {
487            field: field.into(),
488            operator: FilterOperator::LessThan,
489            value,
490            logical_op: None,
491        }
492    }
493
494    /// Create contains filter
495    pub fn contains<S: Into<String>>(field: S, value: Value) -> Self {
496        Self {
497            field: field.into(),
498            operator: FilterOperator::Contains,
499            value,
500            logical_op: None,
501        }
502    }
503
504    /// Add logical AND
505    pub fn and(mut self) -> Self {
506        self.logical_op = Some(LogicalOperator::And);
507        self
508    }
509
510    /// Add logical OR
511    pub fn or(mut self) -> Self {
512        self.logical_op = Some(LogicalOperator::Or);
513        self
514    }
515}
516
517/// Filter operator
518#[derive(Debug, Clone, Serialize, Deserialize)]
519pub enum FilterOperator {
520    /// Equal to
521    Equals,
522    /// Not equal to
523    NotEquals,
524    /// Greater than
525    GreaterThan,
526    /// Greater than or equal
527    GreaterThanOrEqual,
528    /// Less than
529    LessThan,
530    /// Less than or equal
531    LessThanOrEqual,
532    /// Contains (for strings/arrays)
533    Contains,
534    /// Starts with (for strings)
535    StartsWith,
536    /// Ends with (for strings)
537    EndsWith,
538    /// In array
539    In,
540    /// Not in array
541    NotIn,
542    /// Is null
543    IsNull,
544    /// Is not null
545    IsNotNull,
546}
547
548/// Logical operator for combining filters
549#[derive(Debug, Clone, Serialize, Deserialize)]
550pub enum LogicalOperator {
551    /// Logical AND
552    And,
553    /// Logical OR
554    Or,
555}
556
557/// Query result data
558#[derive(Debug, Clone, Serialize, Deserialize)]
559pub struct DataResult {
560    /// Result rows
561    pub rows: Vec<DataRow>,
562    /// Column metadata
563    pub columns: Vec<ColumnInfo>,
564    /// Total number of rows (for pagination)
565    pub total_count: Option<usize>,
566    /// Query execution time
567    pub execution_time_ms: u64,
568    /// Custom result metadata
569    pub metadata: HashMap<String, Value>,
570}
571
572impl DataResult {
573    /// Create empty result
574    pub fn empty() -> Self {
575        Self {
576            rows: Vec::new(),
577            columns: Vec::new(),
578            total_count: Some(0),
579            execution_time_ms: 0,
580            metadata: HashMap::new(),
581        }
582    }
583
584    /// Create result with rows
585    pub fn with_rows(rows: Vec<DataRow>, columns: Vec<ColumnInfo>) -> Self {
586        let row_count = rows.len();
587        Self {
588            rows,
589            columns,
590            total_count: Some(row_count),
591            execution_time_ms: 0,
592            metadata: HashMap::new(),
593        }
594    }
595
596    /// Add metadata
597    pub fn with_metadata<S: Into<String>>(mut self, key: S, value: Value) -> Self {
598        self.metadata.insert(key.into(), value);
599        self
600    }
601
602    /// Set execution time
603    pub fn with_execution_time(mut self, time_ms: u64) -> Self {
604        self.execution_time_ms = time_ms;
605        self
606    }
607
608    /// Get row count
609    pub fn row_count(&self) -> usize {
610        self.rows.len()
611    }
612
613    /// Get column count
614    pub fn column_count(&self) -> usize {
615        self.columns.len()
616    }
617
618    /// Convert to JSON array
619    pub fn to_json_array(&self) -> Result<Value> {
620        let mut json_rows = Vec::new();
621        for row in &self.rows {
622            json_rows.push(row.to_json(&self.columns)?);
623        }
624        Ok(Value::Array(json_rows))
625    }
626}
627
628/// Data row
629#[derive(Debug, Clone, Serialize, Deserialize)]
630pub struct DataRow {
631    /// Row values (indexed by column position)
632    pub values: Vec<Value>,
633    /// Row metadata
634    pub metadata: HashMap<String, Value>,
635}
636
637impl DataRow {
638    /// Create new row
639    pub fn new(values: Vec<Value>) -> Self {
640        Self {
641            values,
642            metadata: HashMap::new(),
643        }
644    }
645
646    /// Get value by column index
647    pub fn get(&self, index: usize) -> Option<&Value> {
648        self.values.get(index)
649    }
650
651    /// Get value by column name
652    pub fn get_by_name(&self, name: &str, columns: &[ColumnInfo]) -> Option<&Value> {
653        columns
654            .iter()
655            .position(|col| col.name == name)
656            .and_then(|index| self.get(index))
657    }
658
659    /// Convert row to JSON object
660    pub fn to_json(&self, columns: &[ColumnInfo]) -> Result<Value> {
661        let mut obj = serde_json::Map::new();
662        for (i, value) in self.values.iter().enumerate() {
663            if let Some(column) = columns.get(i) {
664                obj.insert(column.name.clone(), value.clone());
665            }
666        }
667        Ok(Value::Object(obj))
668    }
669}
670
671/// Column information
672#[derive(Debug, Clone, Serialize, Deserialize)]
673pub struct ColumnInfo {
674    /// Column name
675    pub name: String,
676    /// Column data type
677    pub data_type: DataType,
678    /// Whether column is nullable
679    pub nullable: bool,
680    /// Column description
681    pub description: Option<String>,
682    /// Column metadata
683    pub metadata: HashMap<String, Value>,
684}
685
686impl ColumnInfo {
687    /// Create new column info
688    pub fn new<S: Into<String>>(name: S, data_type: DataType) -> Self {
689        Self {
690            name: name.into(),
691            data_type,
692            nullable: true,
693            description: None,
694            metadata: HashMap::new(),
695        }
696    }
697
698    /// Set nullable
699    pub fn nullable(mut self, nullable: bool) -> Self {
700        self.nullable = nullable;
701        self
702    }
703
704    /// Set description
705    pub fn description<S: Into<String>>(mut self, description: S) -> Self {
706        self.description = Some(description.into());
707        self
708    }
709
710    /// Add metadata
711    pub fn with_metadata<S: Into<String>>(mut self, key: S, value: Value) -> Self {
712        self.metadata.insert(key.into(), value);
713        self
714    }
715}
716
717/// Data type enumeration
718#[derive(Debug, Clone, Serialize, Deserialize)]
719pub enum DataType {
720    /// Text/string data
721    Text,
722    /// Integer number
723    Integer,
724    /// Floating point number
725    Float,
726    /// Boolean value
727    Boolean,
728    /// Date/time value
729    DateTime,
730    /// Binary data
731    Binary,
732    /// JSON data
733    Json,
734    /// UUID
735    Uuid,
736    /// Custom data type
737    Custom(String),
738}
739
740impl fmt::Display for DataType {
741    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
742        match self {
743            DataType::Text => write!(f, "TEXT"),
744            DataType::Integer => write!(f, "INTEGER"),
745            DataType::Float => write!(f, "FLOAT"),
746            DataType::Boolean => write!(f, "BOOLEAN"),
747            DataType::DateTime => write!(f, "DATETIME"),
748            DataType::Binary => write!(f, "BINARY"),
749            DataType::Json => write!(f, "JSON"),
750            DataType::Uuid => write!(f, "UUID"),
751            DataType::Custom(custom) => write!(f, "{}", custom),
752        }
753    }
754}
755
756/// Data source schema information
757#[derive(Debug, Clone, Serialize, Deserialize)]
758pub struct Schema {
759    /// Schema name
760    pub name: Option<String>,
761    /// Tables in the schema
762    pub tables: Vec<TableInfo>,
763    /// Custom schema metadata
764    pub metadata: HashMap<String, Value>,
765}
766
767impl Default for Schema {
768    fn default() -> Self {
769        Self::new()
770    }
771}
772
773impl Schema {
774    /// Create new schema
775    pub fn new() -> Self {
776        Self {
777            name: None,
778            tables: Vec::new(),
779            metadata: HashMap::new(),
780        }
781    }
782
783    /// Add table
784    pub fn with_table(mut self, table: TableInfo) -> Self {
785        self.tables.push(table);
786        self
787    }
788
789    /// Get table by name
790    pub fn get_table(&self, name: &str) -> Option<&TableInfo> {
791        self.tables.iter().find(|t| t.name == name)
792    }
793
794    /// Get all table names
795    pub fn table_names(&self) -> Vec<&str> {
796        self.tables.iter().map(|t| t.name.as_str()).collect()
797    }
798}
799
800/// Table information
801#[derive(Debug, Clone, Serialize, Deserialize)]
802pub struct TableInfo {
803    /// Table name
804    pub name: String,
805    /// Columns in the table
806    pub columns: Vec<ColumnInfo>,
807    /// Primary key columns
808    pub primary_keys: Vec<String>,
809    /// Foreign key relationships
810    pub foreign_keys: Vec<ForeignKey>,
811    /// Table description
812    pub description: Option<String>,
813    /// Row count (if available)
814    pub row_count: Option<usize>,
815    /// Custom table metadata
816    pub metadata: HashMap<String, Value>,
817}
818
819impl TableInfo {
820    /// Create new table info
821    pub fn new<S: Into<String>>(name: S) -> Self {
822        Self {
823            name: name.into(),
824            columns: Vec::new(),
825            primary_keys: Vec::new(),
826            foreign_keys: Vec::new(),
827            description: None,
828            row_count: None,
829            metadata: HashMap::new(),
830        }
831    }
832
833    /// Add column
834    pub fn with_column(mut self, column: ColumnInfo) -> Self {
835        self.columns.push(column);
836        self
837    }
838
839    /// Add primary key
840    pub fn with_primary_key<S: Into<String>>(mut self, column: S) -> Self {
841        self.primary_keys.push(column.into());
842        self
843    }
844
845    /// Add foreign key
846    pub fn with_foreign_key(mut self, fk: ForeignKey) -> Self {
847        self.foreign_keys.push(fk);
848        self
849    }
850
851    /// Set description
852    pub fn description<S: Into<String>>(mut self, description: S) -> Self {
853        self.description = Some(description.into());
854        self
855    }
856
857    /// Set row count
858    pub fn row_count(mut self, count: usize) -> Self {
859        self.row_count = Some(count);
860        self
861    }
862
863    /// Get column by name
864    pub fn get_column(&self, name: &str) -> Option<&ColumnInfo> {
865        self.columns.iter().find(|c| c.name == name)
866    }
867
868    /// Check if column is primary key
869    pub fn is_primary_key(&self, column: &str) -> bool {
870        self.primary_keys.contains(&column.to_string())
871    }
872}
873
874/// Foreign key relationship
875#[derive(Debug, Clone, Serialize, Deserialize)]
876pub struct ForeignKey {
877    /// Local column name
878    pub column: String,
879    /// Referenced table name
880    pub referenced_table: String,
881    /// Referenced column name
882    pub referenced_column: String,
883    /// Relationship name
884    pub name: Option<String>,
885}
886
887/// Connection test result
888#[derive(Debug, Clone, Serialize, Deserialize)]
889pub struct ConnectionTestResult {
890    /// Test successful
891    pub success: bool,
892    /// Test message
893    pub message: String,
894    /// Connection latency in milliseconds
895    pub latency_ms: Option<u64>,
896    /// Test metadata
897    pub metadata: HashMap<String, Value>,
898}
899
900impl ConnectionTestResult {
901    /// Create successful test result
902    pub fn success<S: Into<String>>(message: S) -> Self {
903        Self {
904            success: true,
905            message: message.into(),
906            latency_ms: None,
907            metadata: HashMap::new(),
908        }
909    }
910
911    /// Create failed test result
912    pub fn failure<S: Into<String>>(message: S) -> Self {
913        Self {
914            success: false,
915            message: message.into(),
916            latency_ms: None,
917            metadata: HashMap::new(),
918        }
919    }
920
921    /// Set latency
922    pub fn with_latency(mut self, latency_ms: u64) -> Self {
923        self.latency_ms = Some(latency_ms);
924        self
925    }
926
927    /// Add metadata
928    pub fn with_metadata<S: Into<String>>(mut self, key: S, value: Value) -> Self {
929        self.metadata.insert(key.into(), value);
930        self
931    }
932}
933
934/// Data source plugin registry entry
935pub struct DataSourcePluginEntry {
936    /// Plugin ID
937    pub plugin_id: crate::PluginId,
938    /// Plugin instance
939    pub plugin: std::sync::Arc<dyn DataSourcePlugin>,
940    /// Plugin configuration
941    pub config: DataSourcePluginConfig,
942    /// Plugin capabilities
943    pub capabilities: PluginCapabilities,
944}
945
946impl DataSourcePluginEntry {
947    /// Create new plugin entry
948    pub fn new(
949        plugin_id: crate::PluginId,
950        plugin: std::sync::Arc<dyn DataSourcePlugin>,
951        config: DataSourcePluginConfig,
952    ) -> Self {
953        let capabilities = plugin.capabilities();
954        Self {
955            plugin_id,
956            plugin,
957            config,
958            capabilities,
959        }
960    }
961
962    /// Check if plugin is enabled
963    pub fn is_enabled(&self) -> bool {
964        self.config.enabled
965    }
966
967    /// Check if plugin supports a data source type
968    pub fn supports_type(&self, data_type: &str) -> bool {
969        self.config.data_source_type == data_type
970    }
971}
972
973/// Helper trait for creating data source plugins
974pub trait DataSourcePluginFactory: Send + Sync {
975    /// Create a new data source plugin instance
976    fn create_plugin(&self) -> Result<Box<dyn DataSourcePlugin>>;
977}
978
979/// Built-in data source helpers
980pub mod helpers {
981    use super::*;
982
983    /// Create a simple in-memory data source for testing
984    pub fn create_memory_data_source() -> Vec<DataRow> {
985        vec![
986            DataRow::new(vec![
987                Value::String("John".to_string()),
988                Value::String("Doe".to_string()),
989                Value::Number(30.into()),
990            ]),
991            DataRow::new(vec![
992                Value::String("Jane".to_string()),
993                Value::String("Smith".to_string()),
994                Value::Number(25.into()),
995            ]),
996        ]
997    }
998
999    /// Create sample column info for testing
1000    pub fn create_sample_columns() -> Vec<ColumnInfo> {
1001        vec![
1002            ColumnInfo::new("first_name", DataType::Text).nullable(false),
1003            ColumnInfo::new("last_name", DataType::Text).nullable(false),
1004            ColumnInfo::new("age", DataType::Integer).nullable(false),
1005        ]
1006    }
1007
1008    /// Create sample schema for testing
1009    pub fn create_sample_schema() -> Schema {
1010        let table = TableInfo::new("users")
1011            .with_column(ColumnInfo::new("id", DataType::Integer).nullable(false))
1012            .with_column(ColumnInfo::new("first_name", DataType::Text).nullable(false))
1013            .with_column(ColumnInfo::new("last_name", DataType::Text).nullable(false))
1014            .with_column(ColumnInfo::new("email", DataType::Text).nullable(false))
1015            .with_primary_key("id");
1016
1017        Schema::new().with_table(table)
1018    }
1019}
1020
1021#[cfg(test)]
1022mod tests {
1023
1024    #[test]
1025    fn test_module_compiles() {
1026        // Basic compilation test
1027    }
1028}