nixl_sys/utils/
params.rs

1// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use super::*;
17use std::collections::HashMap;
18
19/// A safe wrapper around NIXL parameters
20pub struct Params {
21    inner: NonNull<bindings::nixl_capi_params_s>,
22}
23
24/// A key-value pair in the parameters
25#[derive(Debug)]
26pub struct ParamPair<'a> {
27    pub key: &'a str,
28    pub value: &'a str,
29}
30
31/// An iterator over parameter key-value pairs
32pub struct ParamIterator<'a> {
33    iter: NonNull<bindings::nixl_capi_param_iter_s>,
34    _phantom: std::marker::PhantomData<&'a ()>,
35}
36
37/// An infallible iterator over parameter key-value pairs (filters out errors)
38pub struct ParamIntoIter<'a> {
39    inner: ParamIterator<'a>,
40}
41
42impl<'a> Iterator for ParamIntoIter<'a> {
43    type Item = (&'a str, &'a str);
44
45    fn next(&mut self) -> Option<Self::Item> {
46        self.inner.find_map(Result::ok)
47    }
48}
49
50impl<'a> Iterator for ParamIterator<'a> {
51    type Item = Result<(&'a str, &'a str), NixlError>;
52
53    fn next(&mut self) -> Option<Self::Item> {
54        let mut key_ptr = ptr::null();
55        let mut value_ptr = ptr::null();
56        let mut has_next = false;
57
58        // SAFETY: self.iter is guaranteed to be valid by NonNull
59        let status = unsafe {
60            nixl_capi_params_iterator_next(
61                self.iter.as_ptr(),
62                &mut key_ptr,
63                &mut value_ptr,
64                &mut has_next,
65            )
66        };
67
68        match status {
69            0 if key_ptr.is_null() => None,
70            0 => {
71                // SAFETY: If status is 0 and key_ptr is not null, both pointers are valid null-terminated strings
72                let result = unsafe {
73                    let key = CStr::from_ptr(key_ptr).to_str().unwrap();
74                    let value = CStr::from_ptr(value_ptr).to_str().unwrap();
75                    Ok((key, value))
76                };
77                Some(result)
78            }
79            -1 => Some(Err(NixlError::InvalidParam)),
80            _ => Some(Err(NixlError::BackendError)),
81        }
82    }
83}
84
85impl Drop for ParamIterator<'_> {
86    fn drop(&mut self) {
87        // SAFETY: self.iter is guaranteed to be valid by NonNull
88        unsafe {
89            nixl_capi_params_destroy_iterator(self.iter.as_ptr());
90        }
91    }
92}
93
94impl From<ParamIterator<'_>> for HashMap<String, String> {
95    fn from(iter: ParamIterator<'_>) -> Self {
96        iter.filter_map(Result::ok)
97            .map(|(k, v)| (k.to_string(), v.to_string()))
98            .collect()
99    }
100}
101
102impl<'a> IntoIterator for &'a Params {
103    type Item = (&'a str, &'a str);
104    type IntoIter = ParamIntoIter<'a>;
105
106    fn into_iter(self) -> Self::IntoIter {
107        ParamIntoIter {
108            inner: self.iter().expect("Failed to create param iterator"),
109        }
110    }
111}
112
113impl Params {
114    pub(crate) fn new(inner: NonNull<bindings::nixl_capi_params_s>) -> Self {
115        Self { inner }
116    }
117
118    /// Creates a new empty Params object
119    pub(crate) fn create() -> Result<Self, NixlError> {
120        let mut params = ptr::null_mut();
121
122        let status = unsafe { nixl_capi_create_params(&mut params) };
123
124        match status {
125            0 => {
126                let inner = unsafe { NonNull::new_unchecked(params) };
127                Ok(Self { inner })
128            }
129            -1 => Err(NixlError::InvalidParam),
130            _ => Err(NixlError::BackendError),
131        }
132    }
133
134    /// Creates a new Params object from an iteratable
135    ///
136    /// # Example
137    /// ```ignore
138    /// use std::collections::HashMap;
139    ///
140    /// let map = HashMap::from([
141    ///     ("access_key", "*********"),
142    ///     ("secret_key", "*********"),
143    ///     ("bucket", "my-bucket"),
144    /// ]);
145    ///
146    /// let params = Params::from(map.iter().map(|(k, v)| (*k, *v)))?;
147    /// ```
148    pub fn from<I, K, V>(iter: I) -> Result<Self, NixlError>
149    where
150        I: IntoIterator<Item = (K, V)>,
151        K: AsRef<str>,
152        V: AsRef<str>,
153    {
154        let mut params = Self::create()?;
155        for (key, value) in iter {
156            params.set(key.as_ref(), value.as_ref())?;
157        }
158        Ok(params)
159    }
160
161    /// Creates a new Params object by copying from another Params
162    ///
163    /// # Example
164    /// ```ignore
165    /// let original_params = agent.get_plugin_params("OBJ")?.1;
166    /// let mut modified_params = original_params.clone()?;
167    /// modified_params.set("bucket", "my-custom-bucket")?;
168    /// ```
169    pub fn clone(&self) -> Result<Self, NixlError> {
170        Params::from(self)
171    }
172
173    /// Sets a key-value pair in the parameters (overwrites if exists)
174    pub fn set(&mut self, key: &str, value: &str) -> Result<(), NixlError> {
175        let c_key = CString::new(key)?;
176        let c_value = CString::new(value)?;
177
178        let status = unsafe {
179            nixl_capi_params_add(self.inner.as_ptr(), c_key.as_ptr(), c_value.as_ptr())
180        };
181
182        match status {
183            0 => Ok(()),
184            -1 => Err(NixlError::InvalidParam),
185            _ => Err(NixlError::BackendError),
186        }
187    }
188
189    /// Returns true if the parameters are empty
190    pub fn is_empty(&self) -> Result<bool, NixlError> {
191        let mut is_empty = false;
192
193        // SAFETY: self.inner is guaranteed to be valid by NonNull
194        let status = unsafe { nixl_capi_params_is_empty(self.inner.as_ptr(), &mut is_empty) };
195
196        match status {
197            0 => Ok(is_empty),
198            -1 => Err(NixlError::InvalidParam),
199            _ => Err(NixlError::BackendError),
200        }
201    }
202
203    /// Returns an iterator over the parameter key-value pairs
204    pub fn iter(&self) -> Result<ParamIterator<'_>, NixlError> {
205        let mut iter = ptr::null_mut();
206
207        // SAFETY: self.inner is guaranteed to be valid by NonNull
208        let status = unsafe { nixl_capi_params_create_iterator(self.inner.as_ptr(), &mut iter) };
209
210        match status {
211            0 => {
212                // SAFETY: If status is 0, iter was successfully created and is non-null
213                let iter = unsafe { NonNull::new_unchecked(iter) };
214                Ok(ParamIterator {
215                    iter,
216                    _phantom: std::marker::PhantomData,
217                })
218            }
219            -1 => Err(NixlError::InvalidParam),
220            _ => Err(NixlError::BackendError),
221        }
222    }
223
224    pub(crate) fn handle(&self) -> *mut bindings::nixl_capi_params_s {
225        self.inner.as_ptr()
226    }
227}
228
229impl Drop for Params {
230    fn drop(&mut self) {
231        // SAFETY: self.inner is guaranteed to be valid by NonNull
232        unsafe {
233            nixl_capi_destroy_params(self.inner.as_ptr());
234        }
235    }
236}