1use super::funcs::*;
4use crate::ctl_error::SysctlError;
5use crate::ctl_flags::CtlFlags;
6use crate::ctl_info::CtlInfo;
7use crate::ctl_type::CtlType;
8use crate::ctl_value::CtlValue;
9use crate::traits::Sysctl;
10use std::str::FromStr;
11
12#[derive(Debug, Clone, PartialEq)]
14pub enum Ctl {
15 Oid(Vec<libc::c_int>),
16 Name(String, CtlType, String),
17}
18
19impl Ctl {
20 pub fn oid(&self) -> Option<&Vec<libc::c_int>> {
21 match self {
22 Ctl::Oid(oid) => Some(oid),
23 _ => None,
24 }
25 }
26}
27
28impl std::str::FromStr for Ctl {
29 type Err = SysctlError;
30
31 #[allow(clippy::redundant_field_names)]
32 fn from_str(s: &str) -> Result<Self, Self::Err> {
33 let oid = name2oid(s)?;
34
35 Ok(Ctl::Oid(oid))
36 }
37}
38
39impl Sysctl for Ctl {
40 fn new(name: &str) -> Result<Self, SysctlError> {
41 Ctl::from_str(name)
42 }
43
44 #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos")))]
45 fn new_with_type(name: &str, _ctl_type: CtlType, _fmt: &str) -> Result<Self, SysctlError> {
46 Ctl::from_str(name)
47 }
48
49 #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos"))]
50 fn new_with_type(name: &str, ctl_type: CtlType, fmt: &str) -> Result<Self, SysctlError> {
51 let _ = name2oid(name)?;
52
53 Ok(Ctl::Name(name.to_string(), ctl_type, fmt.to_string()))
54 }
55
56 fn name(&self) -> Result<String, SysctlError> {
57 match self {
58 Ctl::Oid(oid) => oid2name(&oid),
59 Ctl::Name(name, ..) => Ok(name.clone()),
60 }
61 }
62
63 #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos")))]
64 fn value_type(&self) -> Result<CtlType, SysctlError> {
65 match self {
66 Ctl::Oid(oid) => {
67 let info = oidfmt(oid)?;
68 Ok(info.ctl_type)
69 }
70 Ctl::Name(_, ctl_type, _) => {
71 Ok(*ctl_type)
72 }
73 }
74 }
75
76 #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos"))]
77 fn value_type(&self) -> Result<CtlType, SysctlError> {
78 match self {
79 Ctl::Oid(oid) => {
80 let info = oidfmt(oid)?;
81
82 Ok(match info.ctl_type {
83 CtlType::Int => match info.fmt.as_str() {
84 "I" => CtlType::Int,
85 "IU" => CtlType::Uint,
86 "L" => CtlType::Long,
87 "LU" => CtlType::Ulong,
88 _ => return Err(SysctlError::MissingImplementation),
89 },
90 ctl_type => ctl_type,
91 })
92 }
93 Ctl::Name(_, ctl_type, fmt) => {
94 Ok(match ctl_type {
95 CtlType::Int => match fmt.as_str() {
96 "I" => CtlType::Int,
97 "IU" => CtlType::Uint,
98 "L" => CtlType::Long,
99 "LU" => CtlType::Ulong,
100 _ => return Err(SysctlError::MissingImplementation),
101 },
102 ctl_type => *ctl_type,
103 })
104 }
105 }
106 }
107
108 #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos")))]
109 fn description(&self) -> Result<String, SysctlError> {
110 let oid = self.oid().ok_or(SysctlError::MissingImplementation)?;
111 oid2description(oid)
112 }
113
114 #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos"))]
115 fn description(&self) -> Result<String, SysctlError> {
116 Ok("[N/A]".to_string())
117 }
118
119 #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos")))]
120 fn value(&self) -> Result<CtlValue, SysctlError> {
121 let oid = self.oid().ok_or(SysctlError::MissingImplementation)?;
122 value_oid(oid)
123 }
124
125 #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos"))]
126 fn value(&self) -> Result<CtlValue, SysctlError> {
127 match self {
128 Ctl::Oid(oid) => {
129 let mut oid = oid.clone();
130 value_oid(&mut oid)
131 }
132 Ctl::Name(name, ctl_type, fmt) => {
133 value_name(name.as_str(), *ctl_type, fmt.as_str())
134 }
135 }
136 }
137
138 #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos")))]
139 fn value_as<T>(&self) -> Result<Box<T>, SysctlError> {
140 let oid = self.oid().ok_or(SysctlError::MissingImplementation)?;
141 value_oid_as::<T>(oid)
142 }
143
144 fn value_string(&self) -> Result<String, SysctlError> {
145 self.value().map(|v| format!("{}", v))
146 }
147
148 #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos"))]
149 fn value_as<T>(&self) -> Result<Box<T>, SysctlError> {
150 match self {
151 Ctl::Oid(oid) => {
152 let mut oid = oid.clone();
153 value_oid_as::<T>(&mut oid)
154 }
155 Ctl::Name(name, ctl_type, fmt) => {
156 value_name_as::<T>(name.as_str(), *ctl_type, fmt.as_str())
157 }
158 }
159 }
160
161 #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos")))]
162 fn set_value(&self, value: CtlValue) -> Result<CtlValue, SysctlError> {
163 let oid = self.oid().ok_or(SysctlError::MissingImplementation)?;
164 set_oid_value(&oid, value)
165 }
166
167 #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos"))]
168 fn set_value(&self, value: CtlValue) -> Result<CtlValue, SysctlError> {
169 match self {
170 Ctl::Oid(oid) => {
171 let mut oid = oid.clone();
172 set_oid_value(&mut oid, value)
173 }
174 Ctl::Name(name, ctl_type, fmt) => {
175 set_name_value(name.as_str(), *ctl_type, fmt.as_str(), value)
176 }
177 }
178 }
179
180 #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos")))]
181 fn set_value_string(&self, value: &str) -> Result<String, SysctlError> {
182 let oid = self.oid().ok_or(SysctlError::MissingImplementation)?;
183 let ctl_type = self.value_type()?;
184 let _ = match ctl_type {
185 CtlType::String => set_oid_value(oid, CtlValue::String(value.to_owned())),
186 CtlType::Uint => set_oid_value(
187 oid,
188 CtlValue::Uint(value.parse::<u32>().map_err(|_| SysctlError::ParseError)?),
189 ),
190 CtlType::Int => set_oid_value(
191 oid,
192 CtlValue::Int(value.parse::<i32>().map_err(|_| SysctlError::ParseError)?),
193 ),
194 CtlType::Ulong => set_oid_value(
195 oid,
196 CtlValue::Ulong(value.parse::<u64>().map_err(|_| SysctlError::ParseError)?),
197 ),
198 CtlType::U8 => set_oid_value(
199 oid,
200 CtlValue::U8(value.parse::<u8>().map_err(|_| SysctlError::ParseError)?),
201 ),
202 _ => Err(SysctlError::MissingImplementation),
203 }?;
204 self.value_string()
205 }
206
207 #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos"))]
208 fn set_value_string(&self, value: &str) -> Result<String, SysctlError> {
209 let ctl_type = self.value_type()?;
210
211 match self {
212 Ctl::Oid(oid) => {
213 let mut oid = oid.clone();
214 let _ = match ctl_type {
215 CtlType::String => set_oid_value(&mut oid, CtlValue::String(value.to_owned())),
216 CtlType::Uint => set_oid_value(
217 &mut oid,
218 CtlValue::Uint(value.parse::<u32>().map_err(|_| SysctlError::ParseError)?),
219 ),
220 CtlType::Int => set_oid_value(
221 &mut oid,
222 CtlValue::Int(value.parse::<i32>().map_err(|_| SysctlError::ParseError)?),
223 ),
224 CtlType::Ulong => set_oid_value(
225 &mut oid,
226 CtlValue::Ulong(value.parse::<u64>().map_err(|_| SysctlError::ParseError)?),
227 ),
228 CtlType::U8 => set_oid_value(
229 &mut oid,
230 CtlValue::U8(value.parse::<u8>().map_err(|_| SysctlError::ParseError)?),
231 ),
232 _ => Err(SysctlError::MissingImplementation),
233 }?;
234 }
235 Ctl::Name(name, ctl_type, fmt) => {
236 let _ = match ctl_type {
237 CtlType::String => set_name_value(
238 name.as_str(),
239 *ctl_type,
240 fmt.as_str(),
241 CtlValue::String(value.to_owned()),
242 ),
243 CtlType::Uint => set_name_value(
244 name.as_str(),
245 *ctl_type,
246 fmt.as_str(),
247 CtlValue::Uint(value.parse::<u32>().map_err(|_| SysctlError::ParseError)?),
248 ),
249 CtlType::Int => set_name_value(
250 name.as_str(),
251 *ctl_type,
252 fmt.as_str(),
253 CtlValue::Int(value.parse::<i32>().map_err(|_| SysctlError::ParseError)?),
254 ),
255 CtlType::Ulong => set_name_value(
256 name.as_str(),
257 *ctl_type,
258 fmt.as_str(),
259 CtlValue::Ulong(value.parse::<u64>().map_err(|_| SysctlError::ParseError)?),
260 ),
261 CtlType::U8 => set_name_value(
262 name.as_str(),
263 *ctl_type,
264 fmt.as_str(),
265 CtlValue::U8(value.parse::<u8>().map_err(|_| SysctlError::ParseError)?),
266 ),
267 _ => Err(SysctlError::MissingImplementation),
268 }?;
269 }
270 }
271
272 self.value_string()
273 }
274
275 fn flags(&self) -> Result<CtlFlags, SysctlError> {
276 Ok(self.info()?.flags())
277 }
278
279 fn info(&self) -> Result<CtlInfo, SysctlError> {
280 let oid = self.oid().ok_or(SysctlError::MissingImplementation)?;
281 oidfmt(oid)
282 }
283}
284
285#[cfg(test)]
286mod tests {
287 use crate::Sysctl;
288
289 #[test]
290 fn ctl_new() {
291 let _ = super::Ctl::new("kern.ostype").expect("Ctl::new");
292 }
293
294 #[test]
295 fn ctl_description() {
296 let ctl = super::Ctl::new("kern.ostype").expect("Ctl::new");
297
298 let descp = ctl.description();
299 assert!(descp.is_ok());
300
301 let descp = descp.unwrap();
302
303 #[cfg(target_os = "freebsd")]
304 assert_eq!(descp, "Operating system type");
305
306 #[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "visionos", target_os = "watchos", target_os = "linux"))]
307 assert_eq!(descp, "[N/A]");
308 }
309}
310
311#[cfg(all(test, target_os = "freebsd"))]
312mod tests_freebsd {}