opt-in-miner 0.2.2

Opt-in Monero mining library for transparent application monetization
Documentation
use std::{fs, path::PathBuf};

/// Manages persistent mining consent stored on the filesystem.
///
/// Consent is stored as a plain text file under the platform's config directory
/// (e.g. `~/.config/<app>/mining-consent` on Linux).
pub struct Consent {
    path: PathBuf,
}

/// Whether the user has granted or denied mining permission.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Status {
    /// The user has granted mining permission.
    Granted,
    /// The user has explicitly denied mining permission.
    Denied,
}

/// Whether to persist a consent decision across sessions.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Persistence {
    /// Store the decision on disk. The user will not be asked again.
    Save,
    /// Do not store. The user will be asked again next launch.
    Ask,
}

impl Consent {
    /// Creates a new consent manager for the given application name.
    pub fn new(application_name: &str) -> Self {
        let path = dirs_path(application_name);
        Self { path }
    }

    /// Reads the current consent status from disk. Returns [`Status::Denied`] if no decision is stored.
    pub fn status(&self) -> Status {
        match fs::read_to_string(&self.path) {
            Ok(content) => match content.trim() {
                "granted" => Status::Granted,
                _ => Status::Denied,
            },
            Err(_) => Status::Denied,
        }
    }

    pub(crate) fn has_stored(&self) -> bool {
        self.path.exists()
    }

    /// Persists a consent status to disk.
    pub fn store(&self, status: Status) -> Result<(), std::io::Error> {
        if let Some(parent) = self.path.parent() {
            fs::create_dir_all(parent)?;
        }
        let value = match status {
            Status::Granted => "granted",
            Status::Denied => "denied",
        };
        fs::write(&self.path, value)
    }

    /// Removes the stored consent, so the user will be asked again.
    pub fn reset(&self) {
        let _ = fs::remove_file(&self.path);
    }
}

fn dirs_path(application_name: &str) -> PathBuf {
    let base = dirs::config_dir().unwrap_or_else(|| PathBuf::from("."));
    base.join(application_name).join("mining-consent")
}

/// The result of a consent check callback.
///
/// Returned by the closure passed to [`crate::MinerBuilder::consent_check`].
pub struct Reply {
    /// Whether the user accepted or denied mining.
    pub consent: Status,
    /// Whether to persist this decision.
    pub persistence: Persistence,
}