piper-phoneme-streaming 0.1.1

A high-performance Rust library for streaming Text-to-Phoneme (G2P) conversion.
Documentation
use std::path::{Path, PathBuf};
use std::sync::OnceLock;

use crate::error::{Error, Result};

const PHONTAB: &[u8] = include_bytes!("../vendor/espeak-ng-data/phontab");
const PHONINDEX: &[u8] = include_bytes!("../vendor/espeak-ng-data/phonindex");
const PHONDATA: &[u8] = include_bytes!("../vendor/espeak-ng-data/phondata");
const INTONATIONS: &[u8] = include_bytes!("../vendor/espeak-ng-data/intonations");
const EN_DICT: &[u8] = include_bytes!("../vendor/espeak-ng-data/en_dict");
const VI_DICT: &[u8] = include_bytes!("../vendor/espeak-ng-data/vi_dict");

static DATA_DIR: OnceLock<PathBuf> = OnceLock::new();

pub(crate) fn materialized_data_dir() -> Result<&'static Path> {
    if let Some(path) = DATA_DIR.get() {
        return Ok(path.as_path());
    }

    let path = write_embedded_data()?;
    let _ = DATA_DIR.set(path);
    Ok(DATA_DIR
        .get()
        .expect("embedded data dir initialized")
        .as_path())
}

fn write_embedded_data() -> Result<PathBuf> {
    let root = std::env::temp_dir().join("piper-phoneme-streaming-espeak-data-v1");
    std::fs::create_dir_all(&root)?;

    write_if_needed(&root.join("phontab"), PHONTAB)?;
    write_if_needed(&root.join("phonindex"), PHONINDEX)?;
    write_if_needed(&root.join("phondata"), PHONDATA)?;
    write_if_needed(&root.join("intonations"), INTONATIONS)?;
    write_if_needed(&root.join("en_dict"), EN_DICT)?;
    write_if_needed(&root.join("vi_dict"), VI_DICT)?;

    Ok(root)
}

fn write_if_needed(path: &Path, expected: &[u8]) -> Result<()> {
    match std::fs::read(path) {
        Ok(existing) if existing == expected => return Ok(()),
        Ok(_) | Err(_) => {}
    }

    std::fs::write(path, expected).map_err(Error::Io)
}