drasi_source_mock/lib.rs
1#![allow(unexpected_cfgs)]
2// Copyright 2025 The Drasi Authors.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! Mock Source Plugin for drasi-lib.
17//!
18//! This plugin provides a mock data generator for testing and development purposes.
19//! It generates synthetic data at configurable intervals, allowing you to test
20//! query behavior and reaction processing without connecting to real data sources.
21//!
22//! # Data Types
23//!
24//! The mock source supports three data generation modes:
25//!
26//! ## Counter Mode (`data_type: DataType::Counter`)
27//!
28//! Generates sequentially numbered nodes with the label `Counter`. Each event
29//! is always an INSERT with a unique element ID (`counter_1`, `counter_2`, etc.):
30//!
31//! ```text
32//! Element {
33//! id: "counter_1",
34//! labels: ["Counter"],
35//! properties: {
36//! value: 1, // Sequential integer starting at 1
37//! timestamp: "2024-01-15T10:30:00.123Z" // RFC 3339 string
38//! },
39//! effective_from: 1705318200123 // Milliseconds since Unix epoch
40//! }
41//! ```
42//!
43//! ## Sensor Reading Mode (`data_type: DataType::SensorReading { sensor_count }`)
44//!
45//! Generates simulated IoT sensor readings with randomized temperature and humidity
46//! values from a configurable number of sensors (default: 5). Each node has the
47//! label `SensorReading`.
48//!
49//! **Key Behavior**: The first reading for each sensor generates an INSERT event;
50//! subsequent readings for the same sensor generate UPDATE events. This simulates
51//! real sensor behavior where devices are discovered once then continuously report.
52//!
53//! ```text
54//! Element {
55//! id: "sensor_2",
56//! labels: ["SensorReading"],
57//! properties: {
58//! sensor_id: "sensor_2",
59//! temperature: 25.7, // Random in range [20.0, 30.0)
60//! humidity: 48.3, // Random in range [40.0, 60.0)
61//! timestamp: "2024-01-15T10:30:00.123Z"
62//! },
63//! effective_from: 1705318200123
64//! }
65//! ```
66//!
67//! ## Generic Mode (`data_type: DataType::Generic`) - Default
68//!
69//! Generates generic nodes with random values and the label `Generic`. Each event
70//! is always an INSERT with a unique element ID:
71//!
72//! ```text
73//! Element {
74//! id: "generic_1",
75//! labels: ["Generic"],
76//! properties: {
77//! value: 12345, // Random i32
78//! message: "Generic mock data",
79//! timestamp: "2024-01-15T10:30:00.123Z"
80//! },
81//! effective_from: 1705318200123456789 // Nanoseconds since Unix epoch
82//! }
83//! ```
84//!
85//! # Configuration
86//!
87//! | Field | Type | Default | Description |
88//! |-------|------|---------|-------------|
89//! | `data_type` | [`DataType`] | `Generic` | Type of data to generate |
90//! | `interval_ms` | `u64` | `5000` | Interval between events in milliseconds (must be > 0) |
91//!
92//! # Example Configuration (YAML)
93//!
94//! ```yaml
95//! source_type: mock
96//! properties:
97//! data_type:
98//! type: sensor_reading
99//! sensor_count: 10
100//! interval_ms: 1000
101//! ```
102//!
103//! # Usage Example
104//!
105//! ```rust,ignore
106//! use drasi_source_mock::{MockSource, MockSourceConfig, DataType};
107//!
108//! // Create configuration for sensor data at 1-second intervals
109//! let config = MockSourceConfig {
110//! data_type: DataType::sensor_reading(10),
111//! interval_ms: 1000,
112//! };
113//!
114//! // Create the source
115//! let source = MockSource::new("my-mock", config)?;
116//!
117//! // Add to DrasiLib (ownership is transferred)
118//! drasi.add_source(source).await?;
119//! ```
120//!
121//! # Testing
122//!
123//! The mock source provides methods for unit testing without a full DrasiLib setup:
124//!
125//! - [`MockSource::inject_event()`] - Inject custom [`SourceChange`](drasi_core::models::SourceChange)
126//! events (INSERT, UPDATE, DELETE) for deterministic testing scenarios
127//! - [`MockSource::test_subscribe()`] - Subscribe to receive events directly from the source,
128//! bypassing DrasiLib's subscription mechanism
129
130mod config;
131mod conversion;
132pub mod descriptor;
133mod mock;
134mod time;
135
136#[cfg(test)]
137mod tests;
138
139pub use config::{DataType, MockSourceConfig};
140pub use mock::{MockSource, MockSourceBuilder};
141
142/// Dynamic plugin entry point (legacy dylib).
143///
144
145/// Dynamic plugin entry point.
146#[cfg(feature = "dynamic-plugin")]
147drasi_plugin_sdk::export_plugin!(
148 plugin_id = "mock-source",
149 core_version = env!("CARGO_PKG_VERSION"),
150 lib_version = env!("CARGO_PKG_VERSION"),
151 plugin_version = env!("CARGO_PKG_VERSION"),
152 source_descriptors = [descriptor::MockSourceDescriptor],
153 reaction_descriptors = [],
154 bootstrap_descriptors = [],
155);