1- use std:: { fs:: { File , OpenOptions } , sync:: mpsc:: { Receiver , TryRecvError } , io:: { BufReader , Seek , SeekFrom , Read } , time:: Duration , path:: Path } ;
1+ use std:: { fs:: { File , OpenOptions } , sync:: mpsc:: { Receiver , TryRecvError } , io:: { BufReader , Seek , SeekFrom , Read } , time:: Duration , path:: { Path , PathBuf } } ;
22use directories:: UserDirs ;
33
44use crate :: constants;
@@ -8,9 +8,66 @@ use crate::constants;
88/// In case of error will return Some with that error message, otherwise will return None.
99/// For `printer` parameter you can pass any function or closure that you want to print log text with,
1010/// the string passed to said printer will consist of one or more lines of text.
11- pub fn tail_scriptslog < P > ( printer : P , refresh_time_millis : u64 , cancel_token : Receiver < ( ) > ) -> Option < String >
11+ pub fn tail_scriptslog < P > ( printer : P , refresh_time_millis : u64 , cancel_token : Receiver < ( ) > , custom_path : Option < String > ) -> Option < String >
1212where P : Fn ( & String ) -> ( ) {
13- match scriptslog_file ( ) {
13+ if let Some ( p) = custom_path {
14+ tail_scriptslog_loop ( Path :: new ( & p) . to_path_buf ( ) , printer, refresh_time_millis, cancel_token)
15+ } else {
16+ match scriptslog_file_path ( ) {
17+ Ok ( p) => {
18+ tail_scriptslog_loop ( p, printer, refresh_time_millis, cancel_token)
19+ }
20+ Err ( e) => {
21+ Some ( e)
22+ }
23+ }
24+ }
25+ }
26+
27+ fn scriptslog_file_path ( ) -> Result < PathBuf , String > {
28+ let mut docs = None ;
29+ if let Some ( ud) = UserDirs :: new ( ) {
30+ if cfg ! ( windows) {
31+ if let Some ( path) = ud. document_dir ( ) {
32+ docs = Some ( path. to_owned ( ) ) ;
33+ }
34+ }
35+ else if cfg ! ( unix) {
36+ if let Some ( path) = Some ( ud. home_dir ( ) ) {
37+ docs = Some ( path. join ( constants:: LINUX_STEAM_PFX_PATH ) . to_owned ( ) ) ;
38+ }
39+ }
40+ else {
41+ unimplemented ! ( ) ;
42+ }
43+ }
44+
45+ if let Some ( docs) = docs {
46+ return Ok ( docs. join ( Path :: new ( "The Witcher 3" ) . join ( constants:: SCRIPTSLOG_FILE_NAME ) ) ) ;
47+ } else {
48+ return Err ( "Documents directory could not be found." . to_owned ( ) ) ;
49+ }
50+ }
51+
52+ fn open_scriptslog ( path : & PathBuf ) -> Result < File , String > {
53+ let file = OpenOptions :: new ( )
54+ . read ( true )
55+ . write ( true ) // so that it can be created if doesn't exist
56+ . create ( true )
57+ . open ( path) ;
58+
59+ if let Err ( e) = file {
60+ println ! ( "{:?}" , e. kind( ) ) ;
61+ return Err ( "File open error: " . to_owned ( ) + & e. to_string ( ) ) ;
62+ } else {
63+ return Ok ( file. unwrap ( ) ) ;
64+ }
65+ }
66+
67+ #[ cfg( target_os = "windows" ) ]
68+ fn tail_scriptslog_loop < P > ( scriptslog_path : PathBuf , printer : P , refresh_time_millis : u64 , cancel_token : Receiver < ( ) > ) -> Option < String >
69+ where P : Fn ( & String ) -> ( ) {
70+ match open_scriptslog ( & scriptslog_path) {
1471 Ok ( file) => {
1572 let mut reader = BufReader :: new ( & file) ;
1673 // start from the end of the file
@@ -53,33 +110,61 @@ where P: Fn(&String) -> () {
53110 Err ( e) => {
54111 Some ( e)
55112 }
56- }
113+ }
57114}
58115
59- fn scriptslog_file ( ) -> Result < File , String > {
60- let mut docs = None ;
61- if let Some ( ud) = UserDirs :: new ( ) {
62- if let Some ( path) = ud. document_dir ( ) {
63- docs = Some ( path. to_owned ( ) ) ;
116+ #[ cfg( target_os = "linux" ) ]
117+ fn tail_scriptslog_loop < P > ( scriptslog_path : PathBuf , printer : P , refresh_time_millis : u64 , cancel_token : Receiver < ( ) > ) -> Option < String >
118+ where P : Fn ( & String ) -> ( ) {
119+ let mut last_pos: u64 ;
120+ match open_scriptslog ( & scriptslog_path) {
121+ Ok ( file) => last_pos = file. metadata ( ) . unwrap ( ) . len ( ) ,
122+ Err ( e) => {
123+ return Some ( e)
64124 }
65125 }
126+
127+ let mut text = String :: new ( ) ;
128+ loop {
129+ match cancel_token. try_recv ( ) {
130+ Ok ( _) | Err ( TryRecvError :: Disconnected ) => {
131+ break ;
132+ }
133+ Err ( _) => { }
134+ }
135+
136+ // on linux file system is different in a sense that we have to reopen the file to see the changes made to it
137+ match open_scriptslog ( & scriptslog_path) {
138+ Ok ( file) => {
139+ let mut reader = BufReader :: new ( & file) ;
140+ let filesize = file. metadata ( ) . unwrap ( ) . len ( ) ;
66141
67- if let Some ( docs) = docs {
68- let scriptslog_path = docs. join ( Path :: new ( "The Witcher 3" ) . join ( constants:: SCRIPTSLOG_FILE_NAME ) ) ;
69-
70- let file = OpenOptions :: new ( )
71- . read ( true )
72- . write ( true ) // so that it can be created if doesn't exist
73- . create ( true )
74- . open ( scriptslog_path ) ;
75-
76- if let Err ( e) = file {
77- println ! ( "{:?}" , e. kind( ) ) ;
78- return Err ( "File open error: " . to_owned ( ) + & e. to_string ( ) ) ;
79- } else {
80- return Ok ( file. unwrap ( ) ) ;
142+ if last_pos > filesize {
143+ last_pos = reader. seek ( SeekFrom :: Start ( 0 ) ) . unwrap ( ) ;
144+ } else {
145+ reader. seek ( SeekFrom :: Start ( last_pos) ) . unwrap ( ) ;
146+ }
147+
148+ text. clear ( ) ;
149+ match reader. read_to_string ( & mut text) {
150+ Ok ( size) => {
151+ if size > 0 {
152+ last_pos += size as u64 ;
153+ printer ( & text) ;
154+ }
155+ }
156+ Err ( e) => {
157+ return Some ( "File read error: " . to_owned ( ) + & e. to_string ( ) )
158+ }
159+ }
160+ }
161+ Err ( e) => {
162+ return Some ( e)
163+ }
81164 }
82- } else {
83- Err ( "Documents directory could not be found." . to_owned ( ) )
165+
166+ std :: thread :: sleep ( Duration :: from_millis ( refresh_time_millis ) ) ;
84167 }
168+
169+ None
85170}
0 commit comments