amdgpu 1.0.12

Subcomponent of AMDGPU fan control service
use serde::Serializer;
use tracing::error;

use crate::AmdGpuError;

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct TempInput(pub u16);

impl TempInput {
    pub fn as_string(&self) -> String {
        format!("temp{}_input", self.0)
    }
}

impl std::str::FromStr for TempInput {
    type Err = AmdGpuError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.starts_with("temp") && s.ends_with("_input") {
            let mut buffer = String::with_capacity(4);
            for c in s[4..].chars() {
                if c.is_numeric() {
                    buffer.push(c);
                } else if buffer.is_empty() {
                    return Err(AmdGpuError::InvalidTempInput(s.to_string()));
                }
            }
            buffer
                .parse()
                .map_err(|e| {
                    error!("Temp input error {:?}", e);
                    AmdGpuError::InvalidTempInput(s.to_string())
                })
                .map(Self)
        } else {
            Err(AmdGpuError::InvalidTempInput(s.to_string()))
        }
    }
}

impl serde::Serialize for TempInput {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(&self.as_string())
    }
}

impl<'de> serde::Deserialize<'de> for TempInput {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        use serde::de::{self, Visitor};

        struct TempInputVisitor;

        impl<'de> Visitor<'de> for TempInputVisitor {
            type Value = u16;

            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                formatter.write_str("must have format cardX")
            }

            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
            where
                E: de::Error,
            {
                match value.parse::<TempInput>() {
                    Ok(temp) => Ok(temp.0),
                    _ => unreachable!(),
                }
            }
        }
        deserializer
            .deserialize_str(TempInputVisitor)
            .map(TempInput)
    }
}