organicomplex 0.7.0

Interactive complex-valued cellular automaton on 2D and 3D grids in search of that stuff - emergence, open-endedness, organicity etc.
use sdl2::{
    ttf::Sdl2TtfContext,
    ttf::Font as sdl_Font
};

use std::fs;

use serde::Deserialize;

use crate::base::{
    BASE_DIR,
    JEXT,
    PrependErrorString
};

const FONTS_DIR: &str = "fonts";
const FONTS_FILEPATH_NOEXT: &str = "fonts";

pub struct FontContext {
    sdl_context: Sdl2TtfContext
}

pub struct Font<'a> {
    pub sdl_font: sdl_Font<'a, 'static>
}

pub struct Fontset<'a> {
    fonts: Vec<Font<'a>>
}

#[derive(Deserialize)]
struct FontDesc {
    file: String,
    size: i32
}

#[derive(Deserialize)]
struct FontsDesc {
    system: FontDesc,
    meta: FontDesc,
    info: FontDesc
}

#[derive(Clone, Copy, Hash, Eq, PartialEq, Debug)]
// #[allow(non_camel_case_types)]
pub enum CFont {
    System,
    Meta, // this should be mono (constant width and height of chars) for Meta master to work properly
    Info
}

impl FontContext {
    pub fn init() -> Result<FontContext, String> {
        let sdl_context = sdl2::ttf::init().map_err(|e| e.to_string())?;
        Ok(FontContext{sdl_context})
    }
}

impl Font<'_> {
    pub fn height(&self) -> i32 {
        self.sdl_font.height()
    }
}

fn load_font<'a>(font_context: &'a FontContext, filepath: impl ToString, size: i32) -> Result<Font<'a>, String> {
    let filepath = filepath.to_string();

    Ok(Font{sdl_font: font_context.sdl_context.load_font(format!("{}/{}/{}", BASE_DIR, FONTS_DIR, filepath), size as u16)?})
}

impl<'a> Fontset<'a> {
    pub fn load(font_context: &'a FontContext) -> Result<Self, String> {
        let b = fs::read(format!("{}/{}/{}.{}", BASE_DIR, FONTS_DIR, FONTS_FILEPATH_NOEXT, JEXT)).pre_err("cannot read fonts file")?;
        let fsd: FontsDesc = serde_json::from_slice(&b).pre_err("cannot parse fonts json")?;

        let mut fonts = Vec::<Font<'a>>::new();

        // Must be in the same order as CFont variants, for indexing to be correct
        fonts.push(load_font(font_context, &fsd.system.file, fsd.system.size).pre_err("cannot load System font")?);
        fonts.push(load_font(font_context, &fsd.meta.file, fsd.meta.size).pre_err("cannot load Meta font")?);
        fonts.push(load_font(font_context, &fsd.info.file, fsd.info.size).pre_err("cannot load Parameters font")?);

        Ok(Self{fonts})
    }

    pub fn get(&self, cfont: CFont) -> &Font<'a> {
        & self.fonts[cfont as usize]
    }

}