Skip to main content

ember_plus/glow/
root.rs

1//! Glow root element and container types.
2
3use crate::error::{GlowError, Result};
4use crate::ber::{Tag, BerReader, BerWriter, BerValue};
5use super::tags;
6use super::element::EmberValue;
7
8/// A path through the Ember+ tree (sequence of node numbers).
9pub type EmberPath = Vec<i32>;
10
11/// Parse a path string into a path vector.
12pub fn parse_path(path: &str) -> Result<EmberPath> {
13    if path.is_empty() {
14        return Ok(vec![]);
15    }
16    
17    path.split('.')
18        .map(|s| {
19            s.parse::<i32>().map_err(|_| {
20                GlowError::InvalidPath(format!("invalid path component: {}", s)).into()
21            })
22        })
23        .collect()
24}
25
26/// Format a path vector as a string.
27pub fn format_path(path: &EmberPath) -> String {
28    path.iter()
29        .map(|n| n.to_string())
30        .collect::<Vec<_>>()
31        .join(".")
32}
33
34/// Glow root element - the top-level container for Ember+ messages.
35#[derive(Debug, Clone, Default)]
36pub struct GlowRoot {
37    /// Root elements (can be a mix of nodes, parameters, etc.)
38    pub elements: Vec<GlowElement>,
39    /// Invocation results
40    pub invocation_results: Vec<InvocationResult>,
41    /// Stream entries
42    pub streams: Vec<StreamEntry>,
43}
44
45impl GlowRoot {
46    /// Create an empty root.
47    pub fn new() -> Self {
48        GlowRoot::default()
49    }
50
51    /// Create a root with elements.
52    pub fn with_elements(elements: Vec<GlowElement>) -> Self {
53        GlowRoot {
54            elements,
55            ..Default::default()
56        }
57    }
58
59    /// Create a root with a single command for getting the root directory.
60    pub fn get_directory() -> Self {
61        GlowRoot {
62            elements: vec![GlowElement::Command(GlowCommand::GetDirectory)],
63            ..Default::default()
64        }
65    }
66
67    /// Add an element.
68    pub fn add_element(&mut self, element: GlowElement) {
69        self.elements.push(element);
70    }
71
72    /// Check if this root is empty.
73    pub fn is_empty(&self) -> bool {
74        self.elements.is_empty() 
75            && self.invocation_results.is_empty() 
76            && self.streams.is_empty()
77    }
78}
79
80/// A Glow element (can be a node, parameter, function, matrix, or command).
81#[derive(Debug, Clone)]
82pub enum GlowElement {
83    /// A node in the tree
84    Node(GlowNode),
85    /// A parameter
86    Parameter(GlowParameter),
87    /// A function
88    Function(GlowFunction),
89    /// A matrix
90    Matrix(GlowMatrix),
91    /// A command
92    Command(GlowCommand),
93    /// A qualified node (with path)
94    QualifiedNode(EmberPath, GlowNode),
95    /// A qualified parameter (with path)
96    QualifiedParameter(EmberPath, GlowParameter),
97    /// A qualified function (with path)
98    QualifiedFunction(EmberPath, GlowFunction),
99    /// A qualified matrix (with path)
100    QualifiedMatrix(EmberPath, GlowMatrix),
101    /// A template
102    Template(GlowTemplate),
103}
104
105impl GlowElement {
106    /// Get the element number if this is a numbered element.
107    pub fn number(&self) -> Option<i32> {
108        match self {
109            GlowElement::Node(n) => Some(n.number),
110            GlowElement::Parameter(p) => Some(p.number),
111            GlowElement::Function(f) => Some(f.number),
112            GlowElement::Matrix(m) => Some(m.number),
113            _ => None,
114        }
115    }
116
117    /// Get the path if this is a qualified element.
118    pub fn path(&self) -> Option<&EmberPath> {
119        match self {
120            GlowElement::QualifiedNode(path, _) => Some(path),
121            GlowElement::QualifiedParameter(path, _) => Some(path),
122            GlowElement::QualifiedFunction(path, _) => Some(path),
123            GlowElement::QualifiedMatrix(path, _) => Some(path),
124            _ => None,
125        }
126    }
127
128    /// Check if this is a node.
129    pub fn is_node(&self) -> bool {
130        matches!(self, GlowElement::Node(_) | GlowElement::QualifiedNode(_, _))
131    }
132
133    /// Check if this is a parameter.
134    pub fn is_parameter(&self) -> bool {
135        matches!(self, GlowElement::Parameter(_) | GlowElement::QualifiedParameter(_, _))
136    }
137
138    /// Check if this is a function.
139    pub fn is_function(&self) -> bool {
140        matches!(self, GlowElement::Function(_) | GlowElement::QualifiedFunction(_, _))
141    }
142
143    /// Check if this is a matrix.
144    pub fn is_matrix(&self) -> bool {
145        matches!(self, GlowElement::Matrix(_) | GlowElement::QualifiedMatrix(_, _))
146    }
147
148    /// Check if this is a command.
149    pub fn is_command(&self) -> bool {
150        matches!(self, GlowElement::Command(_))
151    }
152}
153
154/// A node in the Ember+ tree.
155#[derive(Debug, Clone, Default)]
156pub struct GlowNode {
157    /// Node number (within parent)
158    pub number: i32,
159    /// Identifier string
160    pub identifier: Option<String>,
161    /// Description string
162    pub description: Option<String>,
163    /// Whether this is the root node
164    pub is_root: Option<bool>,
165    /// Whether this node is online
166    pub is_online: Option<bool>,
167    /// Schema identifiers
168    pub schema_identifiers: Option<String>,
169    /// Template reference
170    pub template_reference: Option<EmberPath>,
171    /// Child elements
172    pub children: Vec<GlowElement>,
173}
174
175impl GlowNode {
176    /// Create a new node with the given number.
177    pub fn new(number: i32) -> Self {
178        GlowNode {
179            number,
180            ..Default::default()
181        }
182    }
183
184    /// Create a node with identifier.
185    pub fn with_identifier(number: i32, identifier: &str) -> Self {
186        GlowNode {
187            number,
188            identifier: Some(identifier.to_string()),
189            ..Default::default()
190        }
191    }
192
193    /// Set the description.
194    pub fn description(mut self, description: &str) -> Self {
195        self.description = Some(description.to_string());
196        self
197    }
198
199    /// Set the is_root flag.
200    pub fn is_root(mut self, is_root: bool) -> Self {
201        self.is_root = Some(is_root);
202        self
203    }
204
205    /// Add a child element.
206    pub fn add_child(&mut self, child: GlowElement) {
207        self.children.push(child);
208    }
209}
210
211/// A parameter in the Ember+ tree.
212#[derive(Debug, Clone, Default)]
213pub struct GlowParameter {
214    /// Parameter number (within parent)
215    pub number: i32,
216    /// Identifier string
217    pub identifier: Option<String>,
218    /// Description string
219    pub description: Option<String>,
220    /// Current value
221    pub value: Option<EmberValue>,
222    /// Minimum value
223    pub minimum: Option<EmberValue>,
224    /// Maximum value
225    pub maximum: Option<EmberValue>,
226    /// Access mode
227    pub access: Option<super::element::ParameterAccess>,
228    /// Format string
229    pub format: Option<String>,
230    /// Enumeration string (for enum types)
231    pub enumeration: Option<String>,
232    /// Factor for value conversion
233    pub factor: Option<i32>,
234    /// Is online
235    pub is_online: Option<bool>,
236    /// Formula for value transformation
237    pub formula: Option<String>,
238    /// Step size
239    pub step: Option<i32>,
240    /// Default value
241    pub default: Option<EmberValue>,
242    /// Parameter type
243    pub parameter_type: Option<super::element::ParameterType>,
244    /// Stream identifier
245    pub stream_identifier: Option<i32>,
246    /// Enumeration map
247    pub enum_map: Vec<super::element::StringIntegerPair>,
248    /// Stream descriptor
249    pub stream_descriptor: Option<super::element::StreamDescriptor>,
250    /// Schema identifiers
251    pub schema_identifiers: Option<String>,
252    /// Template reference
253    pub template_reference: Option<EmberPath>,
254    /// Child elements
255    pub children: Vec<GlowElement>,
256}
257
258impl GlowParameter {
259    /// Create a new parameter with the given number.
260    pub fn new(number: i32) -> Self {
261        GlowParameter {
262            number,
263            ..Default::default()
264        }
265    }
266
267    /// Create a parameter with identifier and value.
268    pub fn with_value(number: i32, identifier: &str, value: EmberValue) -> Self {
269        GlowParameter {
270            number,
271            identifier: Some(identifier.to_string()),
272            value: Some(value),
273            ..Default::default()
274        }
275    }
276
277    /// Set the description.
278    pub fn description(mut self, description: &str) -> Self {
279        self.description = Some(description.to_string());
280        self
281    }
282
283    /// Set the access mode.
284    pub fn access(mut self, access: super::element::ParameterAccess) -> Self {
285        self.access = Some(access);
286        self
287    }
288
289    /// Set the minimum value.
290    pub fn minimum(mut self, min: EmberValue) -> Self {
291        self.minimum = Some(min);
292        self
293    }
294
295    /// Set the maximum value.
296    pub fn maximum(mut self, max: EmberValue) -> Self {
297        self.maximum = Some(max);
298        self
299    }
300}
301
302/// A function in the Ember+ tree.
303#[derive(Debug, Clone, Default)]
304pub struct GlowFunction {
305    /// Function number (within parent)
306    pub number: i32,
307    /// Identifier string
308    pub identifier: Option<String>,
309    /// Description string
310    pub description: Option<String>,
311    /// Arguments description
312    pub arguments: Vec<super::element::TupleItemDescription>,
313    /// Result description
314    pub result: Vec<super::element::TupleItemDescription>,
315    /// Template reference
316    pub template_reference: Option<EmberPath>,
317    /// Child elements
318    pub children: Vec<GlowElement>,
319}
320
321impl GlowFunction {
322    /// Create a new function with the given number.
323    pub fn new(number: i32) -> Self {
324        GlowFunction {
325            number,
326            ..Default::default()
327        }
328    }
329
330    /// Create a function with identifier.
331    pub fn with_identifier(number: i32, identifier: &str) -> Self {
332        GlowFunction {
333            number,
334            identifier: Some(identifier.to_string()),
335            ..Default::default()
336        }
337    }
338}
339
340/// A matrix in the Ember+ tree.
341#[derive(Debug, Clone, Default)]
342pub struct GlowMatrix {
343    /// Matrix number (within parent)
344    pub number: i32,
345    /// Identifier string
346    pub identifier: Option<String>,
347    /// Description string
348    pub description: Option<String>,
349    /// Matrix type
350    pub matrix_type: Option<super::element::MatrixType>,
351    /// Addressing mode
352    pub addressing_mode: Option<super::element::MatrixAddressingMode>,
353    /// Target count
354    pub target_count: Option<i32>,
355    /// Source count
356    pub source_count: Option<i32>,
357    /// Maximum connections per target
358    pub max_connections_per_target: Option<i32>,
359    /// Maximum total connections
360    pub max_total_connections: Option<i32>,
361    /// Connection parameters location
362    pub connection_parameters_location: Option<EmberPath>,
363    /// Gain parameter number
364    pub gain_parameter_number: Option<i32>,
365    /// Labels
366    pub labels: Vec<super::element::Label>,
367    /// Schema identifiers
368    pub schema_identifiers: Option<String>,
369    /// Template reference
370    pub template_reference: Option<EmberPath>,
371    /// Targets
372    pub targets: Vec<i32>,
373    /// Sources
374    pub sources: Vec<i32>,
375    /// Connections
376    pub connections: Vec<GlowConnection>,
377    /// Child elements
378    pub children: Vec<GlowElement>,
379}
380
381impl GlowMatrix {
382    /// Create a new matrix with the given number.
383    pub fn new(number: i32) -> Self {
384        GlowMatrix {
385            number,
386            ..Default::default()
387        }
388    }
389
390    /// Create a matrix with identifier.
391    pub fn with_identifier(number: i32, identifier: &str) -> Self {
392        GlowMatrix {
393            number,
394            identifier: Some(identifier.to_string()),
395            ..Default::default()
396        }
397    }
398}
399
400/// A matrix connection.
401#[derive(Debug, Clone, Default)]
402pub struct GlowConnection {
403    /// Target number
404    pub target: i32,
405    /// Source numbers
406    pub sources: Vec<i32>,
407    /// Operation
408    pub operation: Option<super::element::ConnectionOperation>,
409    /// Disposition
410    pub disposition: Option<super::element::ConnectionDisposition>,
411}
412
413impl GlowConnection {
414    /// Create a new connection.
415    pub fn new(target: i32, sources: Vec<i32>) -> Self {
416        GlowConnection {
417            target,
418            sources,
419            ..Default::default()
420        }
421    }
422}
423
424/// A command in the Ember+ protocol.
425#[derive(Debug, Clone, PartialEq)]
426pub enum GlowCommand {
427    /// Subscribe to changes
428    Subscribe,
429    /// Unsubscribe from changes
430    Unsubscribe,
431    /// Get directory (children)
432    GetDirectory,
433    /// Invoke a function
434    Invoke {
435        /// Invocation ID
436        invocation_id: i32,
437        /// Arguments
438        arguments: Vec<EmberValue>,
439    },
440}
441
442impl GlowCommand {
443    /// Get the command number.
444    pub fn number(&self) -> i32 {
445        match self {
446            GlowCommand::Subscribe => 1,
447            GlowCommand::Unsubscribe => 2,
448            GlowCommand::GetDirectory => 32,
449            GlowCommand::Invoke { .. } => 33,
450        }
451    }
452}
453
454/// A template definition.
455#[derive(Debug, Clone, Default)]
456pub struct GlowTemplate {
457    /// Template number
458    pub number: i32,
459    /// Element definition
460    pub element: Option<Box<GlowElement>>,
461    /// Description
462    pub description: Option<String>,
463}
464
465/// Result of a function invocation.
466#[derive(Debug, Clone)]
467pub struct InvocationResult {
468    /// Invocation ID
469    pub invocation_id: i32,
470    /// Whether the invocation succeeded
471    pub success: bool,
472    /// Result values
473    pub result: Vec<EmberValue>,
474}
475
476impl InvocationResult {
477    /// Create a successful result.
478    pub fn success(invocation_id: i32, result: Vec<EmberValue>) -> Self {
479        InvocationResult {
480            invocation_id,
481            success: true,
482            result,
483        }
484    }
485
486    /// Create a failed result.
487    pub fn failure(invocation_id: i32) -> Self {
488        InvocationResult {
489            invocation_id,
490            success: false,
491            result: vec![],
492        }
493    }
494}
495
496/// A stream entry (for streaming parameter values).
497#[derive(Debug, Clone)]
498pub struct StreamEntry {
499    /// Stream identifier
500    pub stream_identifier: i32,
501    /// Stream value
502    pub value: EmberValue,
503}
504
505impl StreamEntry {
506    /// Create a new stream entry.
507    pub fn new(stream_identifier: i32, value: EmberValue) -> Self {
508        StreamEntry { stream_identifier, value }
509    }
510}