edifact-mapper 0.8.0

EDIFACT to BO4E bidirectional conversion for the German energy market
Documentation
//! Data directory configuration for locating DataBundle files on disk.

use std::path::{Path, PathBuf};

/// Configures where the [`Mapper`](crate::Mapper) looks for `DataBundle` files.
///
/// Bundle files follow the naming convention `edifact-data-{FV}.bin`
/// (e.g., `edifact-data-FV2504.bin`).
///
/// # Resolution order for [`DataDir::auto`]
///
/// 1. `$EDIFACT_DATA_DIR` environment variable (if set)
/// 2. `$HOME/.edifact/data` (Unix) or `%USERPROFILE%\.edifact\data` (Windows)
/// 3. `./data` (current working directory fallback)
#[derive(Debug, Clone)]
pub struct DataDir {
    path: PathBuf,
    eager_fvs: Vec<String>,
    allow_other_release: bool,
}

impl DataDir {
    /// Auto-detect the data directory from the environment.
    ///
    /// See [struct-level docs](DataDir) for resolution order.
    pub fn auto() -> Self {
        let path = if let Ok(env_dir) = std::env::var("EDIFACT_DATA_DIR") {
            PathBuf::from(env_dir)
        } else if let Some(home) = home_dir() {
            home.join(".edifact").join("data")
        } else {
            PathBuf::from("data")
        };
        Self {
            path,
            eager_fvs: vec![],
            allow_other_release: false,
        }
    }

    /// Use an explicit path for the data directory.
    pub fn path<P: AsRef<Path>>(path: P) -> Self {
        Self {
            path: path.as_ref().to_path_buf(),
            eager_fvs: vec![],
            allow_other_release: false,
        }
    }

    /// Mark format versions to be eagerly loaded when the [`Mapper`](crate::Mapper)
    /// is created (rather than lazy-loaded on first access).
    pub fn eager(mut self, fvs: &[&str]) -> Self {
        self.eager_fvs = fvs.iter().map(|s| s.to_string()).collect();
        self
    }

    /// Load a bundle even when it was produced by a different release.
    ///
    /// Off by default. A crate and a bundle from different releases is the
    /// pairing that goes wrong: the format check passes, the bundle loads, and
    /// the mappings inside are from another era — which shows up as a smaller
    /// message rather than an error (issue #158).
    ///
    /// Turn it on only when the mismatch is deliberate and you have some other
    /// way of knowing the two belong together.
    pub fn allow_bundle_from_other_release(mut self, allow: bool) -> Self {
        self.allow_other_release = allow;
        self
    }

    /// Whether a bundle from another release may be loaded.
    pub fn allows_bundle_from_other_release(&self) -> bool {
        self.allow_other_release
    }

    /// The resolved data directory path.
    pub fn data_path(&self) -> &Path {
        &self.path
    }

    /// Format versions that should be eagerly loaded.
    pub fn eager_fvs(&self) -> &[String] {
        &self.eager_fvs
    }

    /// Path to the bundle file for a specific format version.
    pub fn bundle_path(&self, fv: &str) -> PathBuf {
        self.path.join(format!("edifact-data-{fv}.bin"))
    }
}

fn home_dir() -> Option<PathBuf> {
    std::env::var("HOME")
        .or_else(|_| std::env::var("USERPROFILE"))
        .ok()
        .map(PathBuf::from)
}