1
- use std:: collections:: BTreeMap ;
1
+ use std:: collections:: { BTreeMap , BTreeSet } ;
2
2
3
3
use anyhow:: Context ;
4
4
5
+ use super :: calculate_hash;
6
+
5
7
// TODO 1: Create access method that returns enum indicating value or not
6
8
// TODO 2: Create an iterator that allows for selection of first fields to show if present
7
9
@@ -15,7 +17,13 @@ pub struct Data {
15
17
pub struct LogRow {
16
18
data : BTreeMap < String , serde_json:: Value > ,
17
19
#[ serde( skip) ]
18
- cached_display_list : Option < Vec < ( String , String ) > > ,
20
+ cached_display_list : Option < CachedDisplayInfo > ,
21
+ }
22
+
23
+ #[ derive( Default , Debug , PartialEq , Eq ) ]
24
+ struct CachedDisplayInfo {
25
+ data : Vec < ( String , String ) > ,
26
+ common_fields_hash : u64 ,
19
27
}
20
28
21
29
#[ derive( Debug , PartialEq , Eq , Clone , Copy ) ]
@@ -25,7 +33,7 @@ pub enum FieldContent<'a> {
25
33
}
26
34
27
35
impl < ' a > FieldContent < ' a > {
28
- const TEXT_FOR_EMPTY : & ' static str = "[ --- ]" ;
36
+ pub const TEXT_FOR_EMPTY : & ' static str = "[ --- ]" ;
29
37
30
38
pub fn display ( & self ) -> String {
31
39
// TODO 4: Revisit implementation to see if a more efficient way can be found (should be benchmarked to see if it's worth it)
@@ -50,25 +58,60 @@ impl LogRow {
50
58
}
51
59
}
52
60
53
- pub fn as_slice ( & mut self ) -> & [ ( String , String ) ] {
54
- // TODO 1: Return FieldContent
61
+ pub fn as_slice ( & mut self , common_fields : & BTreeSet < String > ) -> & [ ( String , String ) ] {
62
+ self . ensure_cache_is_populated ( common_fields) ;
63
+
64
+ & self
65
+ . cached_display_list
66
+ . get_or_insert_with ( || {
67
+ unreachable ! ( "should have been initialized above if it was empty" )
68
+ } )
69
+ . data
70
+ }
71
+
72
+ fn ensure_cache_is_populated ( & mut self , common_fields : & BTreeSet < String > ) {
73
+ let common_fields_hash = calculate_hash ( common_fields) ;
74
+
75
+ if let Some ( cache) = self . cached_display_list . as_ref ( ) {
76
+ if cache. common_fields_hash != common_fields_hash {
77
+ // Hash changed cache no longer valid
78
+ self . cached_display_list = None ;
79
+ }
80
+ }
81
+
55
82
if self . cached_display_list . is_none ( ) {
56
- let value = self
83
+ // Build data for sorting
84
+ let mut data: Vec < ( bool , ( String , String ) ) > = self
85
+ . data
57
86
. iter ( )
58
- . map ( |( k, v) | ( k. clone ( ) , FieldContent :: Present ( v) . display ( ) ) ) // Use display to keep formatting consistent
87
+ . map ( |( k, v) | {
88
+ (
89
+ common_fields. contains ( k) ,
90
+ ( k. clone ( ) , FieldContent :: Present ( v) . display ( ) ) ,
91
+ )
92
+ } ) // Use display to keep formatting consistent
59
93
. collect ( ) ;
60
- self . cached_display_list = Some ( value) ;
61
- }
62
94
63
- self . cached_display_list . get_or_insert_with ( || {
64
- unreachable ! ( "should have been initialized above if it was empty" )
65
- } )
66
- }
67
-
68
- fn iter ( & self ) -> impl Iterator < Item = ( & String , & serde_json:: Value ) > {
69
- // TODO 4: Determine if here is actually value in making the "main_fields" show first
70
- // TODO 4: Determine if memory wasted here is worth trying to figure out how to use references instead
71
- self . data . iter ( )
95
+ // Add separator for common fields
96
+ data. push ( (
97
+ true ,
98
+ (
99
+ format ! ( " {}" , FieldContent :: TEXT_FOR_EMPTY ) , // prefixed with a leading space so it should end up at top of the common section
100
+ FieldContent :: TEXT_FOR_EMPTY . to_string ( ) ,
101
+ ) ,
102
+ ) ) ;
103
+
104
+ // Sort data based on common fields (to group them at the bottom)
105
+ data. sort_unstable ( ) ;
106
+
107
+ // Remove extra info for sorting
108
+ let data = data. into_iter ( ) . map ( |x| x. 1 ) . collect ( ) ;
109
+
110
+ self . cached_display_list = Some ( CachedDisplayInfo {
111
+ data,
112
+ common_fields_hash,
113
+ } ) ;
114
+ }
72
115
}
73
116
}
74
117
@@ -77,9 +120,12 @@ impl Data {
77
120
& self . rows
78
121
}
79
122
80
- pub fn selected_row_data_as_slice ( & mut self ) -> Option < & [ ( String , String ) ] > {
123
+ pub fn selected_row_data_as_slice (
124
+ & mut self ,
125
+ common_fields : & BTreeSet < String > ,
126
+ ) -> Option < & [ ( String , String ) ] > {
81
127
let selected_row_index = self . selected_row ?;
82
- Some ( self . rows [ selected_row_index] . as_slice ( ) )
128
+ Some ( self . rows [ selected_row_index] . as_slice ( common_fields ) )
83
129
}
84
130
}
85
131
0 commit comments