Skip to main content

plot/lib/logic/
fs_anchored_datum.rs

1use std::{
2	env,
3	ffi::OsStr,
4	fs,
5	io,
6	path::{Path, PathBuf},
7};
8
9use anyhow::{Context, Result};
10
11/// Reprezentacja pojedynczego węzła ścieżki (buf + string)
12#[derive(Debug, Clone)]
13pub struct PathNode {
14	pub buf: PathBuf,
15	pub str: String,
16}
17
18fn normalize_path<P: AsRef<Path>>(p: P) -> String {
19	p.as_ref().to_string_lossy().trim_start_matches(r"\\?\").replace('\\', "/").replace("/./", "/")
20}
21
22impl PathNode {
23	pub fn new(buf: PathBuf) -> Self {
24		let str = normalize_path(&buf);
25		Self { buf, str }
26	}
27}
28
29/// Relacja między katalogiem wykonania a katalogiem docelowym
30#[derive(Debug, Clone)]
31pub struct AnchoredPathsDatum {
32	pub execution_dir: PathNode,
33	pub workspace_dir: PathNode,
34}
35
36impl AnchoredPathsDatum {
37	pub fn new<P: AsRef<Path>>(input: P) -> Result<Self> {
38		let input = input.as_ref();
39
40		let execution_dir_buf = env::current_dir().context("Nie można odczytać katalogu roboczego (CWD)")?;
41
42		let workspace_dir_buf = fs::canonicalize(input)
43			.with_context(|| format!("Nie można ustalić ścieżki '{}'", input.to_string_lossy()))?;
44
45		Ok(Self { execution_dir: PathNode::new(execution_dir_buf), workspace_dir: PathNode::new(workspace_dir_buf) })
46	}
47
48	/// Metoda obliczająca względną ścieżkę między execution_dir a workspace_dir.
49	pub fn to_relative_path(&self) -> String {
50		match self.workspace_dir.buf.strip_prefix(&self.execution_dir.buf) {
51			Ok(rel) => {
52				let s = rel.to_string_lossy().replace('\\', "/");
53				if s.is_empty() { "./".to_string() } else { format!("./{}/", s) }
54			}
55			Err(_) => normalize_path(&self.workspace_dir.buf),
56		}
57	}
58	/// Prywatna metoda pomocnicza: Wyznacza fizyczną lokalizację dla pliku raportu i gwarantuje istnienie folderów nadrzędnych.
59	#[allow(dead_code)]
60	fn build_output_file_path(&self, relpath: &str, time_tag: String, suffix: &str) -> io::Result<PathBuf> {
61		// Wszystkie raporty lądują relatywnie do execution_dir
62		let base_dir_path = self.execution_dir.buf.join(relpath);
63
64		let stem = base_dir_path.file_stem().unwrap_or_else(|| OsStr::new("output")).to_string_lossy();
65
66		// Składamy pełną nazwę pliku w jednym kroku
67		let file_name = format!("{}_{}{}.md", stem, time_tag, suffix);
68		let raw_file_path = base_dir_path.with_file_name(file_name);
69		let file_path = PathBuf::from(normalize_path(&raw_file_path));
70
71		if let Some(parent) = file_path.parent() {
72			fs::create_dir_all(parent)?;
73		}
74
75		Ok(file_path)
76	}
77}