erpc_analysis/
config.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
//! Handles application configuration for erpc-analysis.
//!
//! This module defines structures for parsing the `Config.toml` file and for
//! loading sensitive information like Neo4j credentials from environment
//! variables.

use anyhow::{Context, Result};
use log::info;
use serde::Deserialize;
use std::{env, fs, path::Path};

use crate::args::Args;

/// Represents the `[primary]` table within the `Config.toml` file.
/// It extracts settings relevant to `erpc-analysis` from this section.
#[derive(Debug, Deserialize)]
pub struct PrimarySectionConfig {
    /// Indicates if Neo4j database interaction is enabled.
    pub neo4j_allowed: bool,
}

/// Represents the root structure of the `Config.toml` file.
/// `erpc-analysis` is primarily interested in the settings within the
/// `[primary]` table.
#[derive(Debug, Deserialize)]
pub struct TomlRootConfig {
    /// Configuration settings from the `[primary]` table of the TOML file.
    pub primary: PrimarySectionConfig,
}

/// Holds Neo4j connection parameters.
/// These are loaded from environment variables if `neo4j_allowed` is true.
#[derive(Debug, Clone)]
pub struct Neo4jConfig {
    /// The URI for the Neo4j database (e.g., "bolt://localhost:7687").
    pub uri: String,
    /// The username for Neo4j authentication.
    pub username: String,
    /// The password for Neo4j authentication.
    pub password: String,
}

/// The fully processed and usable configuration for the `erpc-analysis`
/// application. It combines settings from the TOML file and environment
/// variables.
#[derive(Debug)]
pub struct AnalysisConfig {
    /// Optional Neo4j configuration. `Some` if Neo4j is enabled and
    /// credentials are loaded, `None` otherwise.
    pub neo4j: Option<Neo4jConfig>,
}

impl AnalysisConfig {
    /// Loads the application configuration.
    ///
    /// This function performs the following steps:
    /// 1. Reads the TOML configuration file specified by `args.config`.
    /// 2. Parses the TOML into `TomlRootConfig`.
    /// 3. Checks `toml_root.primary.neo4j_allowed`.
    /// 4. If Neo4j is allowed, it attempts to load `NEO4J_DB_ADDR`,
    ///    `NEO4J_DB_USERNAME`, and `NEO4J_DB_PASSWORD` from environment
    ///    variables (expected to be set via `config/primary/.env`).
    /// 5. Constructs and returns the final `AnalysisConfig`.
    ///
    /// # Arguments
    /// * `args` - Parsed command-line arguments containing the path to the
    ///   TOML config file.
    ///
    /// # Errors
    /// Returns an error if the TOML file cannot be read or parsed, or if
    /// Neo4j is allowed but the required environment variables for
    /// credentials are not set.
    pub fn load_from_toml_and_env(args: &Args) -> Result<Self> {
        let config_path = Path::new(&args.config);
        info!("Loading configuration from: {:?}", config_path);

        let config_file_contents = fs::read_to_string(config_path)
            .with_context(|| {
                format!(
                    "Failed to read configuration file at: {:?}",
                    config_path
                )
            })?;

        let toml_root: TomlRootConfig = toml::from_str(&config_file_contents)
            .with_context(|| {
                format!(
                    "Failed to parse TOML from configuration file at: {:?}. \
                     Ensure it matches the expected structure.",
                    config_path
                )
            })?;

        let mut neo4j_details: Option<Neo4jConfig> = None;

        if toml_root.primary.neo4j_allowed {
            // Attempt to load .env file only if neo4j is allowed.
            // The .env file is expected to be at "config/primary/.env"
            // relative to workspace root.
            let env_path = "config/primary/.env";

            match dotenvy::from_path(env_path) {
                Ok(_) => {
                    println!(
                        "Successfully loaded environment variables from: {}",
                        env_path
                    );
                }
                Err(e) => {
                    println!(
                        "Warning: Could not load .env file from {}: {}. \
                         Neo4j credential loading will rely on globally set \
                         environment variables.",
                        env_path, e
                    );
                }
            }

            info!(
                "Attempting to load Neo4j credentials from environment \
                 variables..."
            );
            let raw_uri = env::var("NEO4J_DB_ADDR").context(
                "NEO4J_DB_ADDR not found. Ensure it is set (e.g., in \
                 'config/primary/.env' or globally).",
            )?;
            let uri = format!("bolt://{}", raw_uri);
            let username = env::var("NEO4J_DB_USERNAME").context(
                "NEO4J_DB_USERNAME not found. Ensure it is set (e.g., in \
                 'config/primary/.env' or globally).",
            )?;
            let password = env::var("NEO4J_DB_PASSWORD").context(
                "NEO4J_DB_PASSWORD not found. Ensure it is set (e.g., in \
                 'config/primary/.env' or globally).",
            )?;

            neo4j_details = Some(Neo4jConfig {
                uri,
                username,
                password,
            });
            println!("Successfully loaded Neo4j credentials.");
        } else {
            println!(
                "Neo4j is not allowed in the [primary] section of the \
                 configuration file. Skipping .env loading and credential \
                 retrieval."
            );
        }

        Ok(AnalysisConfig {
            neo4j: neo4j_details,
        })
    }
}