@@ -9,88 +9,117 @@ fn caw_tmp_dir() -> PathBuf {
99 tmp_dir. join ( "caw" )
1010}
1111
12- pub trait PersistentData : Serialize + for < ' a > Deserialize < ' a > {
13- const NAME : & ' static str ;
12+ fn containing_dir ( name : & str ) -> PathBuf {
13+ caw_tmp_dir ( ) . join ( name)
14+ }
15+
16+ fn file_path ( name : & str , title : impl AsRef < str > ) -> PathBuf {
17+ let title = title. as_ref ( ) ;
18+ // Hex the title so we can safely use it as part of a file path (ie. so it contains no
19+ // slashes or other characters that have meaning within a file path).
20+ let hexxed_title = hex:: encode ( title) ;
21+ let prefix = title
22+ . chars ( )
23+ . take_while ( |& c| {
24+ c. is_ascii_alphanumeric ( ) || c == '_' || c == '-' || c == ' '
25+ } )
26+ . map ( |c| if c == ' ' { '-' } else { c } )
27+ . collect :: < String > ( ) ;
28+ containing_dir ( name) . join ( format ! ( "{}-{}.json" , prefix, hexxed_title) )
29+ }
30+
31+ fn save (
32+ name : & str ,
33+ data : & ( impl Serialize + for <' a > Deserialize < ' a > ) ,
34+ title : impl AsRef < str > ,
35+ ) -> anyhow:: Result < ( ) > {
36+ use std:: io:: Write ;
37+ fs:: create_dir_all ( & containing_dir ( name) ) ?;
38+ let json_string = serde_json:: to_string ( data) ?;
39+ let mut file = File :: create ( file_path ( name, title) ) ?;
40+ write ! ( file, "{}" , json_string) ?;
41+ Ok ( ( ) )
42+ }
1443
15- fn containing_dir ( ) -> PathBuf {
16- caw_tmp_dir ( ) . join ( Self :: NAME )
44+ /// Like `save` but prints a warning on failure rather than returning an error value.
45+ fn save_ (
46+ name : & str ,
47+ data : & ( impl Serialize + for <' a > Deserialize < ' a > ) ,
48+ title : impl AsRef < str > ,
49+ ) {
50+ if let Err ( e) = save ( name, data, & title) {
51+ log:: warn!( "Failed to save {} for {}: {}" , name, title. as_ref( ) , e) ;
1752 }
53+ }
54+
55+ fn load < T > ( name : & str , title : impl AsRef < str > ) -> anyhow:: Result < T >
56+ where
57+ T : Serialize + for < ' a > Deserialize < ' a > ,
58+ {
59+ let json_string = fs:: read_to_string ( file_path ( name, title) ) ?;
60+ let t = serde_json:: from_str ( json_string. as_str ( ) ) ?;
61+ Ok ( t)
62+ }
1863
19- fn file_path ( title : impl AsRef < str > ) -> PathBuf {
20- let title = title. as_ref ( ) ;
21- // Hex the title so we can safely use it as part of a file path (ie. so it contains no
22- // slashes or other characters that have meaning within a file path).
23- let hexxed_title = hex:: encode ( title) ;
24- let prefix = title
25- . chars ( )
26- . take_while ( |& c| {
27- c. is_ascii_alphanumeric ( ) || c == '_' || c == '-' || c == ' '
28- } )
29- . map ( |c| if c == ' ' { '-' } else { c } )
30- . collect :: < String > ( ) ;
31- Self :: containing_dir ( ) . join ( format ! ( "{}-{}.json" , prefix, hexxed_title) )
64+ /// Like `load` but prints a warning on failure rather than returning an error value.
65+ fn load_ < T > ( name : & str , title : impl AsRef < str > ) -> Option < T >
66+ where
67+ T : Serialize + for < ' a > Deserialize < ' a > ,
68+ {
69+ match load ( name, & title) {
70+ Ok ( t) => Some ( t) ,
71+ Err ( e) => {
72+ log:: warn!( "Failed to load {} for {}: {}" , name, title. as_ref( ) , e) ;
73+ None
74+ }
3275 }
76+ }
77+
78+ /// The type itself knows how to save and load itself
79+ pub trait PersistentData : Serialize + for < ' a > Deserialize < ' a > {
80+ const NAME : & ' static str ;
3381
3482 fn save ( & self , title : impl AsRef < str > ) -> anyhow:: Result < ( ) > {
35- use std:: io:: Write ;
36- fs:: create_dir_all ( & Self :: containing_dir ( ) ) ?;
37- let json_string = serde_json:: to_string ( self ) ?;
38- let mut file = File :: create ( Self :: file_path ( title) ) ?;
39- write ! ( file, "{}" , json_string) ?;
40- Ok ( ( ) )
83+ save ( Self :: NAME , self , title)
4184 }
4285
4386 /// Like `save` but prints a warning on failure rather than returning an error value.
4487 fn save_ ( & self , title : impl AsRef < str > ) {
45- if let Err ( e) = self . save ( & title) {
46- log:: warn!(
47- "Failed to save {} for {}: {}" ,
48- Self :: NAME ,
49- title. as_ref( ) ,
50- e
51- ) ;
52- }
88+ save_ ( Self :: NAME , self , title)
5389 }
5490
5591 fn load ( title : impl AsRef < str > ) -> anyhow:: Result < Self > {
56- let json_string = fs:: read_to_string ( Self :: file_path ( title) ) ?;
57- let t = serde_json:: from_str ( json_string. as_str ( ) ) ?;
58- Ok ( t)
92+ load ( Self :: NAME , title)
5993 }
6094
6195 /// Like `load` but prints a warning on failure rather than returning an error value.
6296 fn load_ ( title : impl AsRef < str > ) -> Option < Self > {
63- match Self :: load ( & title) {
64- Ok ( t) => Some ( t) ,
65- Err ( e) => {
66- log:: warn!(
67- "Failed to load {} for {}: {}" ,
68- Self :: NAME ,
69- title. as_ref( ) ,
70- e
71- ) ;
72- None
73- }
74- }
97+ load_ ( Self :: NAME , title)
7598 }
7699}
77100
78- #[ derive( Serialize , Deserialize , Clone , Copy , Debug ) ]
79- pub struct WindowPosition {
80- pub x : i32 ,
81- pub y : i32 ,
82- }
101+ /// Knows how to save and load some type `T`
102+ pub trait PersistentWitness < T >
103+ where
104+ T : Serialize + for < ' a > Deserialize < ' a > ,
105+ {
106+ const NAME : & ' static str ;
83107
84- impl PersistentData for WindowPosition {
85- const NAME : & ' static str = "window_position" ;
86- }
108+ fn save ( & self , data : & T , title : impl AsRef < str > ) -> anyhow :: Result < ( ) > {
109+ save ( Self :: NAME , data , title )
110+ }
87111
88- #[ derive( Serialize , Deserialize , Clone , Copy , Debug ) ]
89- pub struct WindowSize {
90- pub width : u32 ,
91- pub height : u32 ,
92- }
112+ /// Like `save` but prints a warning on failure rather than returning an error value.
113+ fn save_ ( & self , data : & T , title : impl AsRef < str > ) {
114+ save_ ( Self :: NAME , data, title)
115+ }
93116
94- impl PersistentData for WindowSize {
95- const NAME : & ' static str = "window_size" ;
117+ fn load ( & self , title : impl AsRef < str > ) -> anyhow:: Result < T > {
118+ load ( Self :: NAME , title)
119+ }
120+
121+ /// Like `load` but prints a warning on failure rather than returning an error value.
122+ fn load_ ( & self , title : impl AsRef < str > ) -> Option < T > {
123+ load_ ( Self :: NAME , title)
124+ }
96125}
0 commit comments