1use std::fs::{self, File};
4use std::io::{BufRead, Seek};
5use std::path::PathBuf;
6
7#[doc(inline)]
8use crate::error::PathError;
9
10pub fn append_open(filename: &str) -> Result<File, PathError> {
16 fs::OpenOptions::new()
17 .create(true)
18 .append(true)
19 .open(filename)
20 .map_err(|e| PathError::FileAccess(filename.to_string(), e.to_string()))
21}
22
23pub fn rw_open(filename: &str) -> Result<File, PathError> {
29 fs::OpenOptions::new()
30 .read(true)
31 .write(true)
32 .open(filename)
33 .map_err(|e| PathError::FileAccess(filename.to_string(), e.to_string()))
34}
35
36pub(crate) enum FileKind {
38 LogFile,
39 StackFile
40}
41
42impl From<FileKind> for PathError {
43 fn from(kind: FileKind) -> PathError {
45 match kind {
46 FileKind::LogFile => PathError::InvalidTimelogPath,
47 FileKind::StackFile => PathError::InvalidStackPath
48 }
49 }
50}
51
52pub(crate) fn canonical_filename(file: &str, kind: FileKind) -> Result<String, PathError> {
60 if file.is_empty() {
61 return Err(PathError::FilenameMissing);
62 }
63 let mut dir = PathBuf::from(file);
64 let filename = dir
65 .file_name()
66 .ok_or(PathError::FilenameMissing)?
67 .to_os_string();
68 dir.pop();
69
70 let mut candir = fs::canonicalize(dir)
71 .map_err(|e| PathError::InvalidPath(file.to_string(), e.to_string()))?;
72 candir.push(filename);
73
74 candir.into_os_string().into_string().map_err(|_| kind.into())
75}
76
77pub fn pop_last_line(file: &mut File) -> Option<String> {
79 let (line, pos) = find_last_line(file)?;
80
81 file.set_len(pos).ok()?;
82 Some(line)
83}
84
85pub fn find_last_line(file: &mut File) -> Option<(String, u64)> {
88 let mut last_pos = 0u64;
89 let mut last_line = String::new();
90
91 if file.metadata().ok()?.len() == 0u64 {
92 return None;
93 }
94
95 let mut reader = std::io::BufReader::new(file);
96 while let Some((line, pos)) = last_line_and_pos(&mut reader) {
97 last_pos = pos;
98 last_line = line.trim_end().to_string();
99 }
100
101 Some((last_line, last_pos))
102}
103
104fn last_line_and_pos<R>(reader: &mut R) -> Option<(String, u64)>
106where
107 R: BufRead + Seek
108{
109 let pos = reader.stream_position().ok()?;
110 let mut line = String::new();
111 (0 != reader.read_line(&mut line).ok()?).then_some((line, pos))
112}