godoru 0.1.0

UI Framework for Rust using Godot
use crate::{Color, ImageHandle};
use std::path::PathBuf;

#[derive(Clone, Debug, PartialEq)]
pub struct Shader {
    pub source: ShaderSource,
    pub params: Vec<ShaderParam>,
}

impl Shader {
    pub fn fromFile(path: impl Into<PathBuf>) -> Self {
        Self {
            source: ShaderSource::File(path.into()),
            params: Vec::new(),
        }
    }

    pub fn fromCode(code: impl Into<String>) -> Self {
        Self {
            source: ShaderSource::Code(code.into()),
            params: Vec::new(),
        }
    }

    pub fn param(mut self, name: impl Into<String>, value: impl IntoShaderValue) -> Self {
        self.params.push(ShaderParam {
            name: name.into(),
            value: value.intoShaderValue(),
        });
        self
    }
}

#[derive(Clone, Debug, PartialEq)]
pub enum ShaderSource {
    File(PathBuf),
    Code(String),
}

#[derive(Clone, Debug, PartialEq)]
pub struct ShaderParam {
    pub name: String,
    pub value: ShaderValue,
}

#[derive(Clone, Debug, PartialEq)]
pub enum ShaderValue {
    Bool(bool),
    Int(i64),
    Float(f64),
    Vector2(f32, f32),
    Color(Color),
    Texture(ImageHandle),
}

pub trait IntoShaderValue {
    fn intoShaderValue(self) -> ShaderValue;
}

impl IntoShaderValue for bool {
    fn intoShaderValue(self) -> ShaderValue {
        ShaderValue::Bool(self)
    }
}

impl IntoShaderValue for i64 {
    fn intoShaderValue(self) -> ShaderValue {
        ShaderValue::Int(self)
    }
}

impl IntoShaderValue for i32 {
    fn intoShaderValue(self) -> ShaderValue {
        ShaderValue::Int(self as i64)
    }
}

impl IntoShaderValue for u32 {
    fn intoShaderValue(self) -> ShaderValue {
        ShaderValue::Int(self as i64)
    }
}

impl IntoShaderValue for f64 {
    fn intoShaderValue(self) -> ShaderValue {
        ShaderValue::Float(self)
    }
}

impl IntoShaderValue for f32 {
    fn intoShaderValue(self) -> ShaderValue {
        ShaderValue::Float(self as f64)
    }
}

impl IntoShaderValue for Color {
    fn intoShaderValue(self) -> ShaderValue {
        ShaderValue::Color(self)
    }
}

impl IntoShaderValue for ImageHandle {
    fn intoShaderValue(self) -> ShaderValue {
        ShaderValue::Texture(self)
    }
}

impl IntoShaderValue for &ImageHandle {
    fn intoShaderValue(self) -> ShaderValue {
        ShaderValue::Texture(self.clone())
    }
}

impl IntoShaderValue for (f32, f32) {
    fn intoShaderValue(self) -> ShaderValue {
        ShaderValue::Vector2(self.0, self.1)
    }
}

impl IntoShaderValue for (f64, f64) {
    fn intoShaderValue(self) -> ShaderValue {
        ShaderValue::Vector2(self.0 as f32, self.1 as f32)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn shaderStoresParamsInOrder() {
        let shader = Shader::fromCode("shader_type canvas_item;")
            .param("opacity", 0.8)
            .param("enabled", true);

        assert_eq!(shader.params.len(), 2);
        assert_eq!(shader.params[0].name, "opacity");
        assert_eq!(shader.params[1].name, "enabled");
    }
}