Viewing: nid_large.rs

// SPDX-License-Identifier: MIT

// Copyright (c) 2025. DDN. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

use crate::{Error, error::cvt_null_mut_m};
use cstrbuf::CStrBuf;
use lnetconfig_sys::{__kernel_size_t, libcfs_nidstr_r, libcfs_strnid, lnet_nid};
use nix::errno::Errno;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt::Display;

#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct LargeNid(lnet_nid);

impl LargeNid {
    pub fn new(nid: lnet_nid) -> Self {
        LargeNid(nid)
    }
}

impl Display for LargeNid {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match nidstr(&self.0) {
            Ok(s) => write!(f, "{}", s),
            Err(_) => write!(f, "<invalid extended nid>"),
        }
    }
}

impl Serialize for LargeNid {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        match nidstr(&self.0) {
            Ok(s) => serializer.serialize_str(&s),
            Err(_) => serializer.serialize_str("<invalid extended nid>"),
        }
    }
}

impl<'de> Deserialize<'de> for LargeNid {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        match strnid(&s) {
            Ok(nid) => Ok(LargeNid(nid)),
            Err(_) => Err(serde::de::Error::custom("invalid extended nid string")),
        }
    }
}

fn nidstr(nid: &lnet_nid) -> crate::Result<String> {
    let mut buf = CStrBuf::new(64);
    let _res = unsafe {
        cvt_null_mut_m(
            libcfs_nidstr_r(nid, buf.as_mut_ptr(), buf.buffer_len() as __kernel_size_t),
            "libcfs_nidstr".to_string(),
        )
    }?;

    buf.into_string().map_err(Error::IntoString)
}

fn strnid(s: &str) -> crate::Result<lnet_nid> {
    let cstr = std::ffi::CString::new(s).map_err(Error::StringConversion)?;
    let mut nid = lnet_nid::default();
    let res = unsafe { libcfs_strnid(&mut nid, cstr.as_ptr()) };
    if res < 0 {
        return Err(Error::NidConversion(Errno::from_raw(-res)));
    }
    Ok(nid)
}

#[cfg(test)]
mod test {
    use super::nidstr;
    use lnetconfig_sys::lnet_nid;

    #[test]
    fn test_nidstr_invalid() {
        let nid = lnet_nid::default();
        let nid_str = nidstr(&nid).unwrap();
        insta::assert_debug_snapshot!(nid_str, @r#""0@<0:0>""#);
    }
}