perfume 0.3.2

Impromptu conversion of sensitive metadata to persistent random names.
Documentation
//! Impromptu conversion of sensitive metadata to persistent random names.
//! Given the same identifier, always returns the same random name.
//! Provides a portable and secure hash-based storage mechanism so that sensitive information
//! does not need to be stored.
//!
//! # Examples
//!
//! examples/remote_store_ureq.rs
//! ```
#![cfg_attr(docsrs, doc = include_str!("../examples/remote_store_ureq.rs"))]
//! ```
//!
//! Cargo.toml
//! ```no_compile
//! [build-dependencies]
//! perfume = { version = "0.1", features = ["codegen"] }
//!
//! [dependencies]
//! perfume = "0.1"
//! phf = { version = "0.12", default-features = false }
//! ```
//!
//! build.rs
//! ```no_run
//! # #[cfg(feature = "codegen")]
//! use perfume::codegen;
//!
//! let out_dir = std::env::var_os("OUT_DIR").unwrap();
//! let out_path = std::path::Path::new(&out_dir).join("perfume.rs");
//! # #[cfg(feature = "codegen")]
//! codegen::ingredients(
//!     "PERFUME_INGREDIENTS",
//!     codegen::PopulationSize::Bhutan, // chosen only once
//!     "data/gerunds.txt",
//!     "data/colors.txt",
//!     "data/animals.txt",
//!     out_path,
//! ).unwrap_or_else(|e| panic!("{e}"));
//! ```
//! Include the generated code in a module using `include!(concat!(env!("OUT_DIR"), "/perfume.rs"));`
//!
//! The word lists such as `gerunds.txt` can be found in the git repository.

#![warn(unused_lifetimes, missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(feature = "nightly", feature(ascii_char))]
#![cfg_attr(feature = "nightly", feature(ascii_char_variants))]

#[cfg(feature = "codegen")]
#[cfg_attr(docsrs, doc(cfg(feature = "codegen")))]
pub mod codegen;

pub mod hex_string;
pub mod identity;

mod random;

use std::fs::{File, OpenOptions};
use std::io::{self, BufRead, Write};
use std::path::Path;

/// All errors generated by this crate.
#[derive(thiserror::Error, Debug)]
pub enum Error {
    /// Generated during code generation. See [`crate::codegen::ingredients`].
    #[error("perfume codegen error: {0}")]
    Codegen(String),
    /// IO errors resulting from calls to [`crate::identity::Population::identity`].
    #[error("perfume io error: {0}")]
    Io(#[from] io::Error),
}

/// The number of hex characters to use to use in each [`crate::identity::Storage`] object key, 3.
/// 4096 possible storage keys.
pub const STORAGE_KEY_LENGTH: usize = 3;
/// The number of hex characters to use to use in each [`crate::identity::Storage`] object digest, 61.
pub const STORAGE_DIGEST_LENGTH: usize = 64 - STORAGE_KEY_LENGTH;

#[allow(dead_code)]
fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
where
    P: AsRef<Path>,
{
    let file = File::open(filename)?;
    Ok(io::BufReader::new(file).lines())
}

#[allow(dead_code)]
fn write_lines<P>(filename: P, lines: &Vec<String>, overwrite: bool) -> io::Result<()>
where
    P: AsRef<Path>,
{
    let mut file = OpenOptions::new()
        .write(true)
        .create_new(!overwrite)
        .append(false)
        .open(filename)?;
    for line in lines {
        writeln!(file, "{line}")?;
    }

    Ok(())
}