floe_core/io/storage/
planner.rs1use std::path::Path;
2
3use crate::FloeResult;
4
5#[derive(Debug, Clone)]
6pub struct ObjectRef {
7 pub uri: String,
8 pub key: String,
9 pub last_modified: Option<String>,
10 pub size: Option<u64>,
11}
12
13pub fn join_prefix(prefix: &str, name: &str) -> String {
14 let left = prefix.trim_matches('/');
15 let right = name.trim_matches('/');
16 if left.is_empty() {
17 right.to_string()
18 } else if right.is_empty() {
19 left.to_string()
20 } else {
21 format!("{left}/{right}")
22 }
23}
24
25pub fn normalize_separators(value: &str) -> String {
26 value.replace('\\', "/").trim_matches('/').to_string()
27}
28
29pub fn stable_sort_refs(mut refs: Vec<ObjectRef>) -> Vec<ObjectRef> {
30 refs.sort_by(|a, b| a.uri.cmp(&b.uri));
31 refs
32}
33
34pub fn filter_by_suffixes(refs: Vec<ObjectRef>, suffixes: &[String]) -> Vec<ObjectRef> {
35 let suffixes = suffixes
36 .iter()
37 .map(|suffix| suffix.to_ascii_lowercase())
38 .collect::<Vec<_>>();
39 refs.into_iter()
40 .filter(|obj| {
41 let lower = obj.uri.to_ascii_lowercase();
42 !lower.ends_with('/') && suffixes.iter().any(|suffix| lower.ends_with(suffix))
43 })
44 .collect()
45}
46
47pub fn ensure_parent_dir(path: &Path) -> FloeResult<()> {
48 if let Some(parent) = path.parent() {
49 std::fs::create_dir_all(parent)?;
50 }
51 Ok(())
52}