hedl_stream/
event.rs

1// Dweve HEDL - Hierarchical Entity Data Language
2//
3// Copyright (c) 2025 Dweve IP B.V. and individual contributors.
4//
5// SPDX-License-Identifier: Apache-2.0
6//
7// Licensed under the Apache License, Version 2.0 (the "License");
8// you may not use this file except in compliance with the License.
9// You may obtain a copy of the License in the LICENSE file at the
10// root of this repository or at: http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Event types for streaming parser.
19//!
20//! This module defines the events yielded by [`StreamingParser`](crate::StreamingParser)
21//! and associated metadata structures.
22//!
23//! # Event Flow
24//!
25//! A typical HEDL document produces events in this order:
26//!
27//! 1. `ListStart` or `ObjectStart` - Container begins
28//! 2. `Node` or `Scalar` - Data items
29//! 3. `ListEnd` or `ObjectEnd` - Container ends
30//! 4. (Repeat for additional containers)
31//!
32//! # Example Event Sequence
33//!
34//! For this HEDL document:
35//!
36//! ```text
37//! %VERSION: 1.0
38//! %STRUCT: User: [id, name]
39//! ---
40//! users: @User
41//!   | alice, Alice
42//!   | bob, Bob
43//! ```
44//!
45//! The parser yields:
46//!
47//! ```text
48//! ListStart { key: "users", type_name: "User", ... }
49//! Node(NodeInfo { id: "alice", ... })
50//! Node(NodeInfo { id: "bob", ... })
51//! ListEnd { key: "users", count: 2, ... }
52//! ```
53
54use hedl_core::Value;
55use std::collections::BTreeMap;
56
57/// Header information parsed from the HEDL document.
58///
59/// Contains metadata extracted from header directives:
60/// - `%VERSION`: Document format version
61/// - `%STRUCT`: Schema definitions mapping type names to column lists
62/// - `%ALIAS`: Variable substitutions
63/// - `%NEST`: Parent-child relationship rules
64///
65/// # Examples
66///
67/// ```rust
68/// use hedl_stream::{StreamingParser, HeaderInfo};
69/// use std::io::Cursor;
70///
71/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
72/// let input = r#"
73/// %VERSION: 1.0
74/// %STRUCT: User: [id, name, email]
75/// %ALIAS: admin = "Administrator"
76/// %NEST: User > Order
77/// ---
78/// "#;
79///
80/// let parser = StreamingParser::new(Cursor::new(input))?;
81/// let header = parser.header().unwrap();
82///
83/// // Access version
84/// let (major, minor) = header.version;
85/// println!("HEDL version {}.{}", major, minor);
86///
87/// // Look up schema
88/// if let Some(schema) = header.get_schema("User") {
89///     println!("User has {} fields", schema.len());
90/// }
91///
92/// // Check for alias
93/// if let Some(value) = header.aliases.get("admin") {
94///     println!("Alias 'admin' expands to '{}'", value);
95/// }
96///
97/// // Check nesting rule
98/// if let Some(child) = header.get_child_type("User") {
99///     println!("User can contain {}", child);
100/// }
101/// # Ok(())
102/// # }
103/// ```
104#[derive(Debug, Clone)]
105pub struct HeaderInfo {
106    /// HEDL version (major, minor).
107    pub version: (u32, u32),
108    /// Schema definitions: type -> columns.
109    pub structs: BTreeMap<String, Vec<String>>,
110    /// Alias definitions.
111    pub aliases: BTreeMap<String, String>,
112    /// Nest relationships: parent -> child.
113    pub nests: BTreeMap<String, String>,
114}
115
116impl HeaderInfo {
117    /// Create a new empty header.
118    #[must_use]
119    pub fn new() -> Self {
120        Self {
121            version: (1, 0),
122            structs: BTreeMap::new(),
123            aliases: BTreeMap::new(),
124            nests: BTreeMap::new(),
125        }
126    }
127
128    /// Get schema columns for a type.
129    #[inline]
130    #[must_use]
131    pub fn get_schema(&self, type_name: &str) -> Option<&Vec<String>> {
132        self.structs.get(type_name)
133    }
134
135    /// Get child type for a parent type (from NEST).
136    #[inline]
137    #[must_use]
138    pub fn get_child_type(&self, parent_type: &str) -> Option<&String> {
139        self.nests.get(parent_type)
140    }
141}
142
143impl Default for HeaderInfo {
144    fn default() -> Self {
145        Self::new()
146    }
147}
148
149/// Information about a parsed node (entity/row).
150///
151/// Represents a single entity parsed from a HEDL matrix row. Contains the
152/// entity's type, ID, field values, and parent relationship information.
153///
154/// # Field Access
155///
156/// Fields can be accessed by index using [`get_field()`](Self::get_field).
157/// The first field (index 0) is always the ID.
158///
159/// # Examples
160///
161/// ## Accessing Fields
162///
163/// ```rust
164/// use hedl_stream::{StreamingParser, NodeEvent};
165/// use hedl_core::Value;
166/// use std::io::Cursor;
167///
168/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
169/// let input = r#"
170/// %VERSION: 1.0
171/// %STRUCT: User: [id, name, email, active]
172/// ---
173/// users: @User
174///   | alice, Alice Smith, alice@example.com, true
175/// "#;
176///
177/// let parser = StreamingParser::new(Cursor::new(input))?;
178///
179/// for event in parser {
180///     if let Ok(NodeEvent::Node(node)) = event {
181///         // Access by index
182///         assert_eq!(node.get_field(0), Some(&Value::String("alice".to_string().into())));
183///         assert_eq!(node.get_field(1), Some(&Value::String("Alice Smith".to_string().into())));
184///         assert_eq!(node.get_field(3), Some(&Value::Bool(true)));
185///
186///         // Or use the id field directly
187///         assert_eq!(node.id, "alice");
188///     }
189/// }
190/// # Ok(())
191/// # }
192/// ```
193///
194/// ## Checking Parent Relationships
195///
196/// ```rust
197/// use hedl_stream::{StreamingParser, NodeEvent};
198/// use std::io::Cursor;
199///
200/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
201/// let input = r#"
202/// %VERSION: 1.0
203/// %STRUCT: User: [id, name]
204/// %STRUCT: Order: [id, amount]
205/// %NEST: User > Order
206/// ---
207/// users: @User
208///   | alice, Alice
209///     | order1, 100.00
210/// "#;
211///
212/// let parser = StreamingParser::new(Cursor::new(input))?;
213///
214/// for event in parser.filter_map(|e| e.ok()) {
215///     if let NodeEvent::Node(node) = event {
216///         if node.is_nested() {
217///             println!("{} is a child of {:?}",
218///                 node.id, node.parent_id.as_ref().unwrap());
219///         }
220///     }
221/// }
222/// # Ok(())
223/// # }
224/// ```
225#[derive(Debug, Clone)]
226pub struct NodeInfo {
227    /// The entity type name.
228    pub type_name: String,
229    /// The entity ID.
230    pub id: String,
231    /// Field values aligned with schema.
232    pub fields: Vec<Value>,
233    /// Nesting depth (0 = top-level).
234    pub depth: usize,
235    /// Parent node ID (if nested).
236    pub parent_id: Option<String>,
237    /// Parent type name (if nested).
238    pub parent_type: Option<String>,
239    /// Line number in source.
240    pub line: usize,
241    /// Expected child count from `|[N]` syntax.
242    pub child_count: Option<usize>,
243}
244
245impl NodeInfo {
246    /// Create a new node info.
247    #[must_use]
248    pub fn new(
249        type_name: String,
250        id: String,
251        fields: Vec<Value>,
252        depth: usize,
253        line: usize,
254    ) -> Self {
255        Self {
256            type_name,
257            id,
258            fields,
259            depth,
260            parent_id: None,
261            parent_type: None,
262            line,
263            child_count: None,
264        }
265    }
266
267    /// Set parent information.
268    #[must_use]
269    pub fn with_parent(mut self, parent_type: String, parent_id: String) -> Self {
270        self.parent_type = Some(parent_type);
271        self.parent_id = Some(parent_id);
272        self
273    }
274
275    /// Set expected child count from `|[N]` syntax.
276    #[must_use]
277    pub fn with_child_count(mut self, count: usize) -> Self {
278        self.child_count = Some(count);
279        self
280    }
281
282    /// Get a field value by column index.
283    #[inline]
284    #[must_use]
285    pub fn get_field(&self, index: usize) -> Option<&Value> {
286        self.fields.get(index)
287    }
288
289    /// Check if this is a nested (child) node.
290    #[inline]
291    #[must_use]
292    pub fn is_nested(&self) -> bool {
293        self.depth > 0 || self.parent_id.is_some()
294    }
295}
296
297/// Event emitted by the streaming parser.
298#[derive(Debug, Clone)]
299pub enum NodeEvent {
300    /// Header has been parsed.
301    Header(HeaderInfo),
302
303    /// Start of a new list.
304    ListStart {
305        /// Key name for the list.
306        key: String,
307        /// Type name.
308        type_name: String,
309        /// Schema columns.
310        schema: Vec<String>,
311        /// Line number.
312        line: usize,
313    },
314
315    /// A node/row has been parsed.
316    Node(NodeInfo),
317
318    /// End of a list.
319    ListEnd {
320        /// Key name for the list.
321        key: String,
322        /// Type name.
323        type_name: String,
324        /// Number of nodes in the list.
325        count: usize,
326    },
327
328    /// A scalar key-value pair.
329    Scalar {
330        /// Key name.
331        key: String,
332        /// Value.
333        value: Value,
334        /// Line number.
335        line: usize,
336    },
337
338    /// Start of an object.
339    ObjectStart {
340        /// Key name.
341        key: String,
342        /// Line number.
343        line: usize,
344    },
345
346    /// End of an object.
347    ObjectEnd {
348        /// Key name.
349        key: String,
350    },
351
352    /// End of document.
353    EndOfDocument,
354}
355
356impl NodeEvent {
357    /// Check if this is a node event.
358    #[inline]
359    #[must_use]
360    pub fn is_node(&self) -> bool {
361        matches!(self, Self::Node(_))
362    }
363
364    /// Get the node info if this is a node event.
365    #[inline]
366    #[must_use]
367    pub fn as_node(&self) -> Option<&NodeInfo> {
368        match self {
369            Self::Node(info) => Some(info),
370            _ => None,
371        }
372    }
373
374    /// Get the line number for this event.
375    #[inline]
376    #[must_use]
377    pub fn line(&self) -> Option<usize> {
378        match self {
379            Self::Header(_) => Some(1),
380            Self::ListStart { line, .. } => Some(*line),
381            Self::Node(info) => Some(info.line),
382            Self::Scalar { line, .. } => Some(*line),
383            Self::ObjectStart { line, .. } => Some(*line),
384            _ => None,
385        }
386    }
387}
388
389#[cfg(test)]
390mod tests {
391    use super::*;
392
393    // ==================== HeaderInfo tests ====================
394
395    #[test]
396    fn test_header_info_new() {
397        let header = HeaderInfo::new();
398        assert_eq!(header.version, (1, 0));
399        assert!(header.structs.is_empty());
400        assert!(header.aliases.is_empty());
401        assert!(header.nests.is_empty());
402    }
403
404    #[test]
405    fn test_header_info_default() {
406        let header = HeaderInfo::default();
407        assert_eq!(header.version, (1, 0));
408        assert!(header.structs.is_empty());
409    }
410
411    #[test]
412    fn test_header_info_get_schema() {
413        let mut header = HeaderInfo::new();
414        header.structs.insert(
415            "User".to_string(),
416            vec!["id".to_string(), "name".to_string()],
417        );
418
419        assert_eq!(
420            header.get_schema("User"),
421            Some(&vec!["id".to_string(), "name".to_string()])
422        );
423        assert_eq!(header.get_schema("Unknown"), None);
424    }
425
426    #[test]
427    fn test_header_info_get_child_type() {
428        let mut header = HeaderInfo::new();
429        header.nests.insert("User".to_string(), "Order".to_string());
430
431        assert_eq!(header.get_child_type("User"), Some(&"Order".to_string()));
432        assert_eq!(header.get_child_type("Product"), None);
433    }
434
435    #[test]
436    fn test_header_info_multiple_structs() {
437        let mut header = HeaderInfo::new();
438        header
439            .structs
440            .insert("User".to_string(), vec!["id".to_string()]);
441        header.structs.insert(
442            "Product".to_string(),
443            vec!["id".to_string(), "price".to_string()],
444        );
445        header.structs.insert(
446            "Order".to_string(),
447            vec!["id".to_string(), "user".to_string(), "product".to_string()],
448        );
449
450        assert_eq!(header.structs.len(), 3);
451        assert_eq!(header.get_schema("User").unwrap().len(), 1);
452        assert_eq!(header.get_schema("Product").unwrap().len(), 2);
453        assert_eq!(header.get_schema("Order").unwrap().len(), 3);
454    }
455
456    #[test]
457    fn test_header_info_multiple_aliases() {
458        let mut header = HeaderInfo::new();
459        header
460            .aliases
461            .insert("active".to_string(), "Active".to_string());
462        header
463            .aliases
464            .insert("inactive".to_string(), "Inactive".to_string());
465
466        assert_eq!(header.aliases.len(), 2);
467        assert_eq!(header.aliases.get("active"), Some(&"Active".to_string()));
468    }
469
470    #[test]
471    fn test_header_info_multiple_nests() {
472        let mut header = HeaderInfo::new();
473        header.nests.insert("User".to_string(), "Order".to_string());
474        header
475            .nests
476            .insert("Order".to_string(), "LineItem".to_string());
477
478        assert_eq!(header.nests.len(), 2);
479        assert_eq!(header.get_child_type("User"), Some(&"Order".to_string()));
480        assert_eq!(
481            header.get_child_type("Order"),
482            Some(&"LineItem".to_string())
483        );
484    }
485
486    #[test]
487    fn test_header_info_clone() {
488        let mut header = HeaderInfo::new();
489        header.version = (2, 1);
490        header
491            .structs
492            .insert("Test".to_string(), vec!["col".to_string()]);
493
494        let cloned = header.clone();
495        assert_eq!(cloned.version, (2, 1));
496        assert_eq!(cloned.structs.get("Test"), Some(&vec!["col".to_string()]));
497    }
498
499    #[test]
500    fn test_header_info_debug() {
501        let header = HeaderInfo::new();
502        let debug = format!("{header:?}");
503        assert!(debug.contains("HeaderInfo"));
504        assert!(debug.contains("version"));
505    }
506
507    // ==================== NodeInfo tests ====================
508
509    #[test]
510    fn test_node_info_new() {
511        let node = NodeInfo::new(
512            "User".to_string(),
513            "alice".to_string(),
514            vec![
515                Value::String("alice".to_string().into()),
516                Value::String("Alice".to_string().into()),
517            ],
518            0,
519            10,
520        );
521
522        assert_eq!(node.type_name, "User");
523        assert_eq!(node.id, "alice");
524        assert_eq!(node.fields.len(), 2);
525        assert_eq!(node.depth, 0);
526        assert_eq!(node.line, 10);
527        assert_eq!(node.parent_id, None);
528        assert_eq!(node.parent_type, None);
529    }
530
531    #[test]
532    fn test_node_info_with_parent() {
533        let node = NodeInfo::new(
534            "Order".to_string(),
535            "order1".to_string(),
536            vec![Value::String("order1".to_string().into())],
537            1,
538            15,
539        )
540        .with_parent("User".to_string(), "alice".to_string());
541
542        assert_eq!(node.parent_type, Some("User".to_string()));
543        assert_eq!(node.parent_id, Some("alice".to_string()));
544    }
545
546    #[test]
547    fn test_node_info_get_field() {
548        let node = NodeInfo::new(
549            "Data".to_string(),
550            "row1".to_string(),
551            vec![
552                Value::String("row1".to_string().into()),
553                Value::Int(42),
554                Value::Bool(true),
555            ],
556            0,
557            5,
558        );
559
560        assert_eq!(
561            node.get_field(0),
562            Some(&Value::String("row1".to_string().into()))
563        );
564        assert_eq!(node.get_field(1), Some(&Value::Int(42)));
565        assert_eq!(node.get_field(2), Some(&Value::Bool(true)));
566        assert_eq!(node.get_field(3), None);
567        assert_eq!(node.get_field(100), None);
568    }
569
570    #[test]
571    fn test_node_info_is_nested_by_depth() {
572        let nested = NodeInfo::new("Child".to_string(), "c1".to_string(), vec![], 1, 10);
573        assert!(nested.is_nested());
574
575        let top_level = NodeInfo::new("Parent".to_string(), "p1".to_string(), vec![], 0, 5);
576        assert!(!top_level.is_nested());
577    }
578
579    #[test]
580    fn test_node_info_is_nested_by_parent() {
581        let with_parent = NodeInfo::new("Child".to_string(), "c1".to_string(), vec![], 0, 10)
582            .with_parent("Parent".to_string(), "p1".to_string());
583        assert!(with_parent.is_nested());
584    }
585
586    #[test]
587    fn test_node_info_clone() {
588        let node = NodeInfo::new(
589            "User".to_string(),
590            "alice".to_string(),
591            vec![Value::String("alice".to_string().into())],
592            0,
593            1,
594        );
595        let cloned = node.clone();
596
597        assert_eq!(cloned.type_name, "User");
598        assert_eq!(cloned.id, "alice");
599    }
600
601    #[test]
602    fn test_node_info_debug() {
603        let node = NodeInfo::new("User".to_string(), "alice".to_string(), vec![], 0, 1);
604        let debug = format!("{node:?}");
605        assert!(debug.contains("NodeInfo"));
606        assert!(debug.contains("User"));
607        assert!(debug.contains("alice"));
608    }
609
610    #[test]
611    fn test_node_info_empty_fields() {
612        let node = NodeInfo::new("Empty".to_string(), "e1".to_string(), vec![], 0, 1);
613        assert!(node.fields.is_empty());
614        assert_eq!(node.get_field(0), None);
615    }
616
617    #[test]
618    fn test_node_info_all_value_types() {
619        let node = NodeInfo::new(
620            "AllTypes".to_string(),
621            "test".to_string(),
622            vec![
623                Value::Null,
624                Value::Bool(true),
625                Value::Int(-42),
626                Value::Float(3.5),
627                Value::String("hello".to_string().into()),
628            ],
629            0,
630            1,
631        );
632
633        assert_eq!(node.get_field(0), Some(&Value::Null));
634        assert_eq!(node.get_field(1), Some(&Value::Bool(true)));
635        assert_eq!(node.get_field(2), Some(&Value::Int(-42)));
636        assert_eq!(node.get_field(3), Some(&Value::Float(3.5)));
637        assert_eq!(
638            node.get_field(4),
639            Some(&Value::String("hello".to_string().into()))
640        );
641    }
642
643    // ==================== NodeEvent tests ====================
644
645    #[test]
646    fn test_node_event_is_node() {
647        let node_info = NodeInfo::new("User".to_string(), "a".to_string(), vec![], 0, 1);
648        let node_event = NodeEvent::Node(node_info);
649        assert!(node_event.is_node());
650
651        let header_event = NodeEvent::Header(HeaderInfo::new());
652        assert!(!header_event.is_node());
653
654        let list_start = NodeEvent::ListStart {
655            key: "users".to_string(),
656            type_name: "User".to_string(),
657            schema: vec![],
658            line: 1,
659        };
660        assert!(!list_start.is_node());
661    }
662
663    #[test]
664    fn test_node_event_as_node() {
665        let node_info = NodeInfo::new("User".to_string(), "alice".to_string(), vec![], 0, 5);
666        let node_event = NodeEvent::Node(node_info);
667
668        let extracted = node_event.as_node().unwrap();
669        assert_eq!(extracted.id, "alice");
670
671        let header_event = NodeEvent::Header(HeaderInfo::new());
672        assert!(header_event.as_node().is_none());
673    }
674
675    #[test]
676    fn test_node_event_line_header() {
677        let event = NodeEvent::Header(HeaderInfo::new());
678        assert_eq!(event.line(), Some(1));
679    }
680
681    #[test]
682    fn test_node_event_line_list_start() {
683        let event = NodeEvent::ListStart {
684            key: "users".to_string(),
685            type_name: "User".to_string(),
686            schema: vec![],
687            line: 42,
688        };
689        assert_eq!(event.line(), Some(42));
690    }
691
692    #[test]
693    fn test_node_event_line_node() {
694        let node = NodeInfo::new("User".to_string(), "a".to_string(), vec![], 0, 100);
695        let event = NodeEvent::Node(node);
696        assert_eq!(event.line(), Some(100));
697    }
698
699    #[test]
700    fn test_node_event_line_scalar() {
701        let event = NodeEvent::Scalar {
702            key: "name".to_string(),
703            value: Value::String("test".to_string().into()),
704            line: 25,
705        };
706        assert_eq!(event.line(), Some(25));
707    }
708
709    #[test]
710    fn test_node_event_line_object_start() {
711        let event = NodeEvent::ObjectStart {
712            key: "config".to_string(),
713            line: 50,
714        };
715        assert_eq!(event.line(), Some(50));
716    }
717
718    #[test]
719    fn test_node_event_line_list_end() {
720        let event = NodeEvent::ListEnd {
721            key: "users".to_string(),
722            type_name: "User".to_string(),
723            count: 10,
724        };
725        assert_eq!(event.line(), None);
726    }
727
728    #[test]
729    fn test_node_event_line_object_end() {
730        let event = NodeEvent::ObjectEnd {
731            key: "config".to_string(),
732        };
733        assert_eq!(event.line(), None);
734    }
735
736    #[test]
737    fn test_node_event_line_end_of_document() {
738        let event = NodeEvent::EndOfDocument;
739        assert_eq!(event.line(), None);
740    }
741
742    #[test]
743    fn test_node_event_clone() {
744        let event = NodeEvent::Scalar {
745            key: "key".to_string(),
746            value: Value::Int(42),
747            line: 10,
748        };
749        let cloned = event.clone();
750
751        if let NodeEvent::Scalar { key, value, line } = cloned {
752            assert_eq!(key, "key");
753            assert_eq!(value, Value::Int(42));
754            assert_eq!(line, 10);
755        } else {
756            panic!("Expected Scalar");
757        }
758    }
759
760    #[test]
761    fn test_node_event_debug() {
762        let event = NodeEvent::EndOfDocument;
763        let debug = format!("{event:?}");
764        assert!(debug.contains("EndOfDocument"));
765    }
766
767    #[test]
768    fn test_node_event_list_start_fields() {
769        let event = NodeEvent::ListStart {
770            key: "users".to_string(),
771            type_name: "User".to_string(),
772            schema: vec!["id".to_string(), "name".to_string()],
773            line: 5,
774        };
775
776        if let NodeEvent::ListStart {
777            key,
778            type_name,
779            schema,
780            line,
781        } = event
782        {
783            assert_eq!(key, "users");
784            assert_eq!(type_name, "User");
785            assert_eq!(schema, vec!["id".to_string(), "name".to_string()]);
786            assert_eq!(line, 5);
787        }
788    }
789
790    #[test]
791    fn test_node_event_list_end_fields() {
792        let event = NodeEvent::ListEnd {
793            key: "products".to_string(),
794            type_name: "Product".to_string(),
795            count: 100,
796        };
797
798        if let NodeEvent::ListEnd {
799            key,
800            type_name,
801            count,
802        } = event
803        {
804            assert_eq!(key, "products");
805            assert_eq!(type_name, "Product");
806            assert_eq!(count, 100);
807        }
808    }
809
810    #[test]
811    fn test_node_event_scalar_all_value_types() {
812        let events = [
813            NodeEvent::Scalar {
814                key: "null".to_string(),
815                value: Value::Null,
816                line: 1,
817            },
818            NodeEvent::Scalar {
819                key: "bool".to_string(),
820                value: Value::Bool(true),
821                line: 2,
822            },
823            NodeEvent::Scalar {
824                key: "int".to_string(),
825                value: Value::Int(-100),
826                line: 3,
827            },
828            NodeEvent::Scalar {
829                key: "float".to_string(),
830                value: Value::Float(2.75),
831                line: 4,
832            },
833            NodeEvent::Scalar {
834                key: "string".to_string(),
835                value: Value::String("text".to_string().into()),
836                line: 5,
837            },
838        ];
839
840        for (i, event) in events.iter().enumerate() {
841            assert_eq!(event.line(), Some(i + 1));
842            assert!(!event.is_node());
843        }
844    }
845}