msr_plugin_csv_register_recorder/
lib.rs

1// FIXME: Enable `deny(missing_docs)` before release
2//#![deny(missing_docs)]
3
4#![warn(rust_2018_idioms)]
5#![warn(rust_2021_compatibility)]
6#![warn(missing_debug_implementations)]
7#![warn(unreachable_pub)]
8#![warn(unsafe_code)]
9#![warn(rustdoc::broken_intra_doc_links)]
10#![warn(clippy::pedantic)]
11// Additional restrictions
12#![warn(clippy::clone_on_ref_ptr)]
13#![warn(clippy::self_named_module_files)]
14// Exceptions
15#![allow(clippy::default_trait_access)]
16#![allow(clippy::module_name_repetitions)]
17#![allow(clippy::missing_errors_doc)] // TODO
18#![allow(clippy::missing_panics_doc)] // TODO
19#![allow(clippy::unnecessary_wraps)] // TODO
20
21use std::{
22    io::Error as IoError,
23    num::{NonZeroU32, NonZeroU64},
24    path::PathBuf,
25};
26
27use thiserror::Error;
28
29use msr_core::{
30    register::recorder::Error as MsrRecordError,
31    storage::{
32        Error as MsrStorageError, MemorySize, StorageConfig, StorageSegmentConfig, TimeInterval,
33    },
34};
35
36use msr_plugin::EventPublisherIndex;
37
38pub mod api;
39use self::api::Config;
40
41mod internal;
42use self::internal::message_loop::create_message_loop;
43
44#[derive(Debug, Clone)]
45pub struct Environment {
46    pub event_publisher_index: EventPublisherIndex,
47
48    /// Directory for storing CSV data
49    pub data_dir: PathBuf,
50
51    pub custom_file_name_prefix: Option<String>,
52}
53
54#[must_use]
55pub fn default_storage_config() -> StorageConfig {
56    StorageConfig {
57        retention_time: TimeInterval::Days(NonZeroU32::new(180).unwrap()), // 180 days
58        segmentation: StorageSegmentConfig {
59            time_interval: TimeInterval::Days(NonZeroU32::new(1).unwrap()), // daily
60            size_limit: MemorySize::Bytes(NonZeroU64::new(1_048_576).unwrap()), // 1 MiB
61        },
62    }
63}
64
65#[must_use]
66pub fn default_config() -> Config {
67    Config {
68        default_storage: default_storage_config(),
69        register_groups: Default::default(),
70    }
71}
72
73#[derive(Debug, Clone, PartialEq, Eq)]
74pub struct PluginSetup {
75    pub initial_config: api::Config,
76    pub initial_state: api::State,
77}
78
79impl Default for PluginSetup {
80    fn default() -> Self {
81        Self {
82            initial_config: default_config(),
83            initial_state: api::State::Inactive,
84        }
85    }
86}
87
88#[derive(Error, Debug)]
89pub enum Error {
90    #[error("register group not configured")]
91    RegisterGroupUnknown,
92
93    #[error("invalid data format")]
94    DataFormatInvalid,
95
96    #[error(transparent)]
97    Io(#[from] IoError),
98
99    #[error(transparent)]
100    MsrRecord(#[from] MsrRecordError),
101
102    #[error(transparent)]
103    MsrStorage(#[from] MsrStorageError),
104
105    #[error(transparent)]
106    Other(#[from] anyhow::Error),
107}
108
109pub type Result<T> = std::result::Result<T, Error>;
110
111pub type PluginError = msr_plugin::PluginError<Error>;
112pub type PluginResult<T> = msr_plugin::PluginResult<T, Error>;
113
114pub type MessageSender = msr_plugin::MessageSender<api::Message>;
115pub type MessageReceiver = msr_plugin::MessageReceiver<api::Message>;
116
117pub type ResultSender<T> = msr_plugin::ResultSender<T, Error>;
118pub type ResultReceiver<T> = msr_plugin::ResultReceiver<T, Error>;
119
120pub type PublishedEvent = msr_plugin::PublishedEvent<api::Event>;
121pub type EventReceiver = msr_plugin::EventReceiver<api::Event>;
122type EventPubSub = msr_plugin::EventPubSub<api::Event>;
123
124pub type Plugin = msr_plugin::PluginContainer<api::Message, api::Event>;
125pub type PluginPorts = msr_plugin::PluginPorts<api::Message, api::Event>;
126
127pub const DEFAULT_FILE_NAME_PREFIX: &str = "register_group_records_";
128
129pub fn create_plugin(
130    environment: Environment,
131    plugin_setup: PluginSetup,
132    event_channel_capacity: usize,
133) -> Result<Plugin> {
134    let Environment {
135        event_publisher_index,
136        data_dir,
137        custom_file_name_prefix,
138    } = environment;
139    let PluginSetup {
140        initial_config,
141        initial_state,
142    } = plugin_setup;
143    let (event_pubsub, event_subscriber) =
144        EventPubSub::new(event_publisher_index, event_channel_capacity);
145    let file_name_prefix =
146        custom_file_name_prefix.unwrap_or_else(|| DEFAULT_FILE_NAME_PREFIX.to_owned());
147    let (message_loop, message_tx) = create_message_loop(
148        data_dir,
149        file_name_prefix,
150        event_pubsub,
151        initial_config,
152        initial_state,
153    )?;
154    Ok(Plugin {
155        ports: PluginPorts {
156            message_tx,
157            event_subscriber,
158        },
159        message_loop,
160    })
161}