seru 0.1.0

simple image renderer
Documentation
pub mod assets;
pub mod core;
pub mod font;
pub mod language;
pub mod layout;
pub mod render;
pub mod runtime;

use std::{collections::HashMap, path::PathBuf, rc::Rc};

use anyhow::Context;
use skia_safe::{Data, Image};

use crate::{
    assets::DefaultAssetProvider,
    core::{ImageWrap, Value},
    font::{FontFile, FontManager},
    language::parser::Parser,
    layout::build_layout_tree,
    render::{RenderOptions, context::RenderContext},
    runtime::Runtime,
};

#[derive(Debug)]
pub struct SeruOption {
    pub allow_network_asset: bool,
    pub asset_root: Option<PathBuf>,
    pub load_system_fonts: bool,
    pub fonts: Vec<FontFile>,
}

pub struct Seru {
    runtime: Runtime,
    font_manager: Rc<FontManager>,
}

impl Seru {
    pub fn new() -> anyhow::Result<Self> {
        Ok(Self::new_with_options(&SeruOption {
            allow_network_asset: true,
            asset_root: None,
            load_system_fonts: true,
            fonts: vec![],
        })?)
    }

    pub fn new_with_options(options: &SeruOption) -> anyhow::Result<Self> {
        let font_manager = Rc::new(FontManager::new(options.load_system_fonts, &options.fonts)?);

        let asset_provider = Rc::new(DefaultAssetProvider {
            allow_network_asset: options.allow_network_asset,
            asset_root: options.asset_root.clone(),
        });

        let mut runtime = Runtime::new(RenderContext {
            font_manager: font_manager.clone(),
            asset_provider,
        });
        runtime.register_builtin_functions();

        Ok(Self {
            runtime,
            font_manager,
        })
    }

    pub fn load_str(&mut self, src: &str) -> anyhow::Result<()> {
        let prog = Parser::from_src(src)?.parse()?;
        self.runtime.evaluate(&prog)?;
        Ok(())
    }

    pub fn load_image(&self, image: &[u8]) -> Option<Value> {
        let image = Image::from_encoded(Data::new_copy(image))?;
        Some(Value::Image(ImageWrap(image)))
    }

    pub fn render(
        &mut self,
        component: &str,
        args: HashMap<String, Value>,
        options: RenderOptions,
    ) -> anyhow::Result<Vec<u8>> {
        let node = self
            .runtime
            .build_render_tree(component, args)?
            .context("component returned no render node")?;

        let layout = build_layout_tree(
            &node,
            options.width as usize,
            options.height as usize,
            &self.font_manager,
        )?;

        render::render(&options, &layout, &self.font_manager)
    }
}