Skip to main content

soapysdr/
args.rs

1use soapysdr_sys::*;
2use std::collections::HashMap;
3use std::ffi::{CStr, CString};
4use std::fmt;
5use std::iter::{FromIterator, IntoIterator};
6use std::os::raw::c_char;
7use std::ptr;
8use std::slice;
9
10/// A list of key=value pairs.
11pub struct Args(SoapySDRKwargs);
12
13impl Drop for Args {
14    fn drop(&mut self) {
15        unsafe { SoapySDRKwargs_clear(self.as_raw()) }
16    }
17}
18
19impl Default for Args {
20    fn default() -> Self {
21        Self::new()
22    }
23}
24
25impl Args {
26    /// Create a new, empty `Args` list
27    pub fn new() -> Args {
28        Args(SoapySDRKwargs {
29            size: 0,
30            keys: ptr::null_mut(),
31            vals: ptr::null_mut(),
32        })
33    }
34
35    /// # Safety
36    ///
37    /// Be careful that [`SoapySDRKwargs`] is either:
38    /// - [`SoapySDRKwargs::keys`] and [`SoapySDRKwargs::vals`] are null and [`SoapySDRKwargs::size`] is 0 or
39    /// - [`SoapySDRKwargs::keys`] and [`SoapySDRKwargs::vals`] both point to valid keys and vals of
40    ///   [`SoapySDRKwargs::size`] length.
41    pub unsafe fn from_raw(a: SoapySDRKwargs) -> Args {
42        Args(a)
43    }
44
45    pub fn as_raw(&mut self) -> *mut SoapySDRKwargs {
46        &mut self.0 as *mut _
47    }
48
49    pub fn as_raw_const(&self) -> *const SoapySDRKwargs {
50        &self.0 as *const _
51    }
52
53    fn keys(&self) -> &[*mut c_char] {
54        unsafe { slice::from_raw_parts(self.0.keys, self.0.size) }
55    }
56
57    fn key(&self, idx: usize) -> &CStr {
58        unsafe { CStr::from_ptr(self.keys()[idx]) }
59    }
60
61    fn values(&self) -> &[*mut c_char] {
62        unsafe { slice::from_raw_parts(self.0.vals, self.0.size) }
63    }
64
65    fn value(&self, idx: usize) -> &CStr {
66        unsafe { CStr::from_ptr(self.values()[idx]) }
67    }
68
69    /// Append a key-value pair to the arguments list
70    ///
71    /// # Example
72    /// ```
73    /// use soapysdr::Args;
74    /// let mut args = Args::new();
75    /// args.set("driver", "lime");
76    /// ```
77    ///
78    /// # Panics
79    ///  * if `key` or `value` contain null bytes
80    pub fn set<K: Into<Vec<u8>>, V: Into<Vec<u8>>>(&mut self, key: K, value: V) {
81        unsafe {
82            let k = CString::new(key).expect("SoapySDR key can't contain null bytes");
83            let v = CString::new(value).expect("SoapySDR value can't contain null bytes");
84            SoapySDRKwargs_set(self.as_raw(), k.as_ptr(), v.as_ptr());
85        }
86    }
87
88    /// Get the value corresponding to a key in the arguments list.
89    ///
90    /// ### Example:
91    /// ```
92    /// use soapysdr::Args;
93    /// let args: Args = "serial=123456".into();
94    /// assert_eq!(args.get("serial"), Some("123456"));
95    /// ```
96    pub fn get<'a>(&'a self, key: &str) -> Option<&'a str> {
97        for i in 0..(self.0.size) {
98            if self.key(i).to_bytes() == key.as_bytes() {
99                return self.value(i).to_str().ok();
100            }
101        }
102        None
103    }
104
105    /// Get an iterator over the (key, value) pairs in the arguments list.
106    ///
107    /// ### Example:
108    /// ```
109    /// use soapysdr::Args;
110    /// let args: Args = "driver=lime, serial=123456".into();
111    /// let mut i = args.iter();
112    /// assert_eq!(i.next(), Some(("driver", "lime")));
113    /// assert_eq!(i.next(), Some(("serial", "123456")));
114    /// assert_eq!(i.next(), None);
115    /// ```
116    pub fn iter(&self) -> ArgsIterator<'_> {
117        ArgsIterator { args: self, pos: 0 }
118    }
119}
120
121impl<K: Into<Vec<u8>>, V: Into<Vec<u8>>> FromIterator<(K, V)> for Args {
122    fn from_iter<T>(i: T) -> Self
123    where
124        T: IntoIterator<Item = (K, V)>,
125    {
126        let mut args = Args::new();
127        for (k, v) in i {
128            args.set(k, v);
129        }
130        args
131    }
132}
133
134impl<'a> IntoIterator for &'a Args {
135    type Item = (&'a str, &'a str);
136    type IntoIter = ArgsIterator<'a>;
137
138    fn into_iter(self) -> Self::IntoIter {
139        self.iter()
140    }
141}
142
143impl<'a> From<&'a str> for Args {
144    fn from(s: &'a str) -> Args {
145        let mut args = Args::new();
146        for i in s.split(',') {
147            if let Some(pos) = i.find('=') {
148                args.set(i[..pos].trim(), i[pos + 1..].trim());
149            }
150        }
151        args
152    }
153}
154
155impl<'a, K: ::std::cmp::Eq + ::std::hash::Hash, V> From<&'a HashMap<K, V>> for Args
156where
157    &'a K: Into<Vec<u8>>,
158    &'a V: Into<Vec<u8>>,
159{
160    fn from(m: &'a HashMap<K, V>) -> Args {
161        let mut args = Args::new();
162        for (k, v) in m {
163            args.set(k, v);
164        }
165        args
166    }
167}
168
169impl<'a, K, V> From<&'a [(K, V)]> for Args
170where
171    &'a K: Into<Vec<u8>>,
172    &'a V: Into<Vec<u8>>,
173{
174    fn from(m: &'a [(K, V)]) -> Args {
175        let mut args = Args::new();
176        for (k, v) in m {
177            args.set(k, v);
178        }
179        args
180    }
181}
182
183impl From<()> for Args {
184    fn from(_: ()) -> Args {
185        Args::new()
186    }
187}
188
189impl<'a> From<&'a Args> for String {
190    fn from(a: &'a Args) -> String {
191        format!("{}", a)
192    }
193}
194
195impl<'a> From<&'a Args> for HashMap<String, String> {
196    fn from(a: &'a Args) -> HashMap<String, String> {
197        a.into_iter()
198            .map(|(k, v)| (k.to_owned(), v.to_owned()))
199            .collect()
200    }
201}
202
203impl fmt::Display for Args {
204    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
205        let mut i = self.iter();
206        if let Some((k, v)) = i.next() {
207            write!(fmt, "{}={}", k, v)?;
208            for (k, v) in i {
209                write!(fmt, ", {}={}", k, v)?;
210            }
211        }
212        Ok(())
213    }
214}
215
216/// An iterator over the `(&key, &value)` pairs in an `Args` list.
217pub struct ArgsIterator<'a> {
218    args: &'a Args,
219    pos: usize,
220}
221
222impl<'a> Iterator for ArgsIterator<'a> {
223    type Item = (&'a str, &'a str);
224    fn next(&mut self) -> Option<Self::Item> {
225        if self.pos < self.args.0.size {
226            let k = self.args.key(self.pos).to_str().unwrap_or("(invalid utf8)");
227            let v = self
228                .args
229                .value(self.pos)
230                .to_str()
231                .unwrap_or("(invalid utf8)");
232            self.pos += 1;
233            Some((k, v))
234        } else {
235            None
236        }
237    }
238}