hoicko_lib 0.1.16

Hoicko library
Documentation
use std::str::FromStr;
use serde::{Serialize, Deserialize};
use tracing::{error, instrument};
use std::fmt;

#[derive(Debug, Clone, PartialEq)]
pub struct ResourceTypeError {
    message: String
}

impl fmt::Display for ResourceTypeError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.message)
    }
}

impl std::error::Error for ResourceTypeError {}

#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize,Default)]
#[serde(rename_all = "camelCase")]
#[serde(into = "i8", from = "i8")]
pub enum ResourceType {
    Workspace = 1,
    #[default]
    Board = 2,
    Folder = 3,
}

impl ResourceType {
    pub fn as_str(&self) -> &'static str {
        match self {
            ResourceType::Workspace => "workspace",
            ResourceType::Board => "board",
            ResourceType::Folder => "folder",
        }
    }

    #[instrument]
    pub fn to_i8(&self) -> i8 {
        match self {
            ResourceType::Workspace => 1,
            ResourceType::Board => 2,
            ResourceType::Folder => 3,
        }
    }

    #[instrument]
    pub fn try_from_i8(value: i8) -> Result<Self, ResourceTypeError> {
        match value {
            1 => Ok(ResourceType::Workspace),
            2 => Ok(ResourceType::Board),
            3 => Ok(ResourceType::Folder),
            invalid => {
                error!(value = invalid, "Invalid ResourceType value");
                Err(ResourceTypeError {
                    message: format!("Invalid ResourceType value: {}", invalid)
                })
            }
        }
    }
}

// From implementations
impl From<ResourceType> for i8 {
    fn from(resource_type: ResourceType) -> Self {
        resource_type.to_i8()
    }
}

impl From<ResourceType> for String {
    fn from(resource_type: ResourceType) -> Self {
        resource_type.as_str().to_string()
    }
}

impl From<ResourceType> for i32 {
    fn from(resource_type: ResourceType) -> Self {
        resource_type.to_i8() as i32
    }
}

impl From<i8> for ResourceType {
    fn from(value: i8) -> Self {
        Self::try_from_i8(value).unwrap_or_else(|e| {
            error!(error = %e, "Failed to convert i8 to ResourceType");
            panic!("Invalid ResourceType value: {}", value)
        })
    }
}

impl FromStr for ResourceType {
    type Err = ResourceTypeError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_lowercase().as_str() {
            "workspace" => Ok(ResourceType::Workspace),
            "board" => Ok(ResourceType::Board),
            "folder" => Ok(ResourceType::Folder),
            invalid => {
                error!(value = invalid, "Invalid ResourceType string");
                Err(ResourceTypeError {
                    message: format!("Invalid ResourceType string: {}", invalid)
                })
            }
        }
    }
}

impl ToString for ResourceType {
    #[instrument]
    fn to_string(&self) -> String {
        self.as_str().to_string()
    }
}

impl TryFrom<i32> for ResourceType {
    type Error = ResourceTypeError;

    #[instrument]
    fn try_from(value: i32) -> Result<Self, Self::Error> {
        if value < i8::MIN as i32 || value > i8::MAX as i32 {
            error!(value = value, "ResourceType value out of i8 range");
            return Err(ResourceTypeError {
                message: format!("ResourceType value out of range: {}", value)
            });
        }
        Self::try_from_i8(value as i8)
    }
}

impl TryFrom<String> for ResourceType {
    type Error = ResourceTypeError;

    #[instrument]
    fn try_from(value: String) -> Result<Self, Self::Error> {
        Self::from_str(&value)
    }
}

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

    #[test]
    fn test_from_i8() {
        assert_eq!(ResourceType::from(1), ResourceType::Workspace);
        assert_eq!(ResourceType::from(2), ResourceType::Board);
        assert_eq!(ResourceType::from(3), ResourceType::Folder);
    }

    #[test]
    fn test_to_i8() {
        assert_eq!(ResourceType::Workspace.to_i8(), 1);
        assert_eq!(ResourceType::Board.to_i8(), 2);
        assert_eq!(ResourceType::Folder.to_i8(), 3);
    }

    #[test]
    fn test_from_str() {
        assert_eq!("workspace".parse::<ResourceType>().unwrap(), ResourceType::Workspace);
        assert_eq!("board".parse::<ResourceType>().unwrap(), ResourceType::Board);
        assert_eq!("folder".parse::<ResourceType>().unwrap(), ResourceType::Folder);
    }

    #[test]
    fn test_to_string() {
        assert_eq!(ResourceType::Workspace.to_string(), "workspace");
        assert_eq!(ResourceType::Board.to_string(), "board");
        assert_eq!(ResourceType::Folder.to_string(), "folder");
    }

    #[test]
    fn test_try_from_i32() {
        assert_eq!(ResourceType::try_from(1_i32).unwrap(), ResourceType::Workspace);
        assert!(ResourceType::try_from(1000_i32).is_err());
    }

    #[test]
    fn test_serde() {
        let resource = ResourceType::Board;
        let serialized = serde_json::to_string(&resource).unwrap();
        let deserialized: ResourceType = serde_json::from_str(&serialized).unwrap();
        assert_eq!(resource, deserialized);
    }

    #[test]
    #[should_panic(expected = "Invalid ResourceType value: 4")]
    fn test_invalid_from_i8() {
        let _ = ResourceType::from(4);
    }
}