pvxs_sys/value.rs
1// Copyright 2026 Tine Zata
2// SPDX-License-Identifier: MPL-2.0
3use cxx::UniquePtr;
4use std::fmt;
5
6use crate::{bridge, Result};
7
8/// A PVAccess value container
9///
10/// Represents a structured data value returned from PVXS operations.
11/// Values have a hierarchical structure with named fields.
12///
13/// # Field Access
14///
15/// Values are accessed by field name. Common fields include:
16/// - `"value"`: The primary data value
17/// - `"alarm.severity"`: Alarm severity level
18/// - `"alarm.status"`: Alarm status code
19/// - `"timeStamp.secondsPastEpoch"`: Timestamp seconds
20///
21/// # Example
22///
23/// ```no_run
24/// # use pvxs_sys::{Context, Value};
25/// # let mut ctx = Context::from_env().unwrap();
26/// let value: Value = ctx.get("my:pv:name", 5.0).unwrap();
27///
28/// // Access different field types
29/// let v = value.get_field_double("value").unwrap();
30/// let severity = value.get_field_int32("alarm.severity").unwrap();
31/// ```
32pub struct Value {
33 pub(crate) inner: UniquePtr<bridge::ValueWrapper>,
34}
35
36impl Value {
37 /// Check if this value is valid
38 ///
39 /// Returns `false` if the value is empty or uninitialized.
40 pub fn is_valid(&self) -> bool {
41 bridge::value_is_valid(&self.inner)
42 }
43
44 /// Get a field value as a double
45 ///
46 /// # Errors
47 ///
48 /// Returns an error if the field doesn't exist or cannot be
49 /// converted to a double.
50 pub fn get_field_double(&self, field_name: &str) -> Result<f64> {
51 Ok(bridge::value_get_field_double(
52 &self.inner,
53 field_name.to_string(),
54 )?)
55 }
56
57 /// Get a field value as an i32
58 ///
59 /// # Errors
60 ///
61 /// Returns an error if the field doesn't exist or cannot be
62 /// converted to an i32.
63 pub fn get_field_int32(&self, field_name: &str) -> Result<i32> {
64 Ok(bridge::value_get_field_int32(
65 &self.inner,
66 field_name.to_string(),
67 )?)
68 }
69
70 /// Get a field value as a String
71 ///
72 /// # Errors
73 ///
74 /// Returns an error if the field doesn't exist or cannot be
75 /// converted to a string.
76 pub fn get_field_string(&self, field_name: &str) -> Result<String> {
77 Ok(bridge::value_get_field_string(
78 &self.inner,
79 field_name.to_string(),
80 )?)
81 }
82
83 /// Get a field value as a enum
84 ///
85 /// # Errors
86 ///
87 /// Returns an error if the field doesn't exist or cannot be
88 /// converted to a enum.
89 pub fn get_field_enum(&self, field_name: &str) -> Result<i16> {
90 Ok(bridge::value_get_field_enum(
91 &self.inner,
92 field_name.to_string(),
93 )?)
94 }
95
96 /// Get a field value as an array of doubles
97 ///
98 /// Extracts a field containing an array of double-precision floating point values.
99 /// Commonly used for waveform data, measurement arrays, or multi-point setpoints.
100 ///
101 /// # Arguments
102 ///
103 /// * `field_name` - The field path (e.g., "value", "waveform.data")
104 ///
105 /// # Errors
106 ///
107 /// Returns an error if the field doesn't exist or cannot be
108 /// converted to an array of doubles.
109 ///
110 /// # Example
111 ///
112 /// ```no_run
113 /// # use pvxs_sys::Context;
114 /// # let mut ctx = Context::from_env().unwrap();
115 /// let value = ctx.get("waveform:double:pv", 5.0).unwrap();
116 /// let array = value.get_field_double_array("value").unwrap();
117 /// println!("Double array length: {}", array.len());
118 /// for (i, val) in array.iter().enumerate().take(5) {
119 /// println!(" [{}] = {}", i, val);
120 /// }
121 /// ```
122 pub fn get_field_double_array(&self, field_name: &str) -> Result<Vec<f64>> {
123 Ok(bridge::value_get_field_double_array(
124 &self.inner,
125 field_name.to_string(),
126 )?)
127 }
128
129 /// Get a field value as an array of int32
130 ///
131 /// Extracts a field containing an array of 32-bit signed integers.
132 /// Often used for status arrays, configuration parameters, or indexed data.
133 ///
134 /// # Arguments
135 ///
136 /// * `field_name` - The field path (e.g., "value", "status.codes")
137 ///
138 /// # Errors
139 ///
140 /// Returns an error if the field doesn't exist or cannot be
141 /// converted to an array of int32.
142 ///
143 /// # Example
144 ///
145 /// ```no_run
146 /// # use pvxs_sys::Context;
147 /// # let mut ctx = Context::from_env().unwrap();
148 /// let value = ctx.get("array:int32:pv", 5.0).unwrap();
149 /// let array = value.get_field_int32_array("value").unwrap();
150 /// println!("Int32 array length: {}", array.len());
151 /// for (i, val) in array.iter().enumerate().take(5) {
152 /// println!(" [{}] = {}", i, val);
153 /// }
154 /// ```
155 pub fn get_field_int32_array(&self, field_name: &str) -> Result<Vec<i32>> {
156 Ok(bridge::value_get_field_int32_array(
157 &self.inner,
158 field_name.to_string(),
159 )?)
160 }
161
162 /// Get a field value as an array of strings
163 ///
164 /// Extracts a field containing an array of string values.
165 /// Commonly used for enum choices, device names, status messages, or text lists.
166 ///
167 /// # Arguments
168 ///
169 /// * `field_name` - The field path (e.g., "value.choices", "devices.names")
170 ///
171 /// # Errors
172 ///
173 /// Returns an error if the field doesn't exist or cannot be
174 /// converted to an array of strings.
175 ///
176 /// # Example
177 ///
178 /// ```no_run
179 /// # use pvxs_sys::Context;
180 /// # let mut ctx = Context::from_env().unwrap();
181 /// // Get enum choices for an NTEnum PV
182 /// let value = ctx.get("enum:pv", 5.0).unwrap();
183 /// let choices = value.get_field_string_array("value.choices").unwrap();
184 /// println!("Available choices:");
185 /// for (i, choice) in choices.iter().enumerate() {
186 /// println!(" [{}] = '{}'", i, choice);
187 /// }
188 /// ```
189 pub fn get_field_string_array(&self, field_name: &str) -> Result<Vec<String>> {
190 Ok(bridge::value_get_field_string_array(
191 &self.inner,
192 field_name.to_string(),
193 )?)
194 }
195}
196
197impl fmt::Display for Value {
198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199 write!(f, "{}", bridge::value_to_string(&self.inner))
200 }
201}
202
203impl fmt::Debug for Value {
204 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
205 f.debug_struct("Value")
206 .field("data", &bridge::value_to_string(&self.inner))
207 .finish()
208 }
209}