perf: make StoragePath::join and File builders consume self to avoid clones

StoragePath::join deep-cloned the whole Vec<String> (every segment String)
just to append one element. Take self by value and push in place. All callers
pass owned values except PathService::create_file_path, which holds a borrow
and now clones explicitly — the same copy the old &self join already made.

File::with_name / with_folder / with_size took &self and rebuilt the struct,
cloning every carried-over field (id, mime_type, folder_id, blob_hash, ...).
Consume self and mutate only the fields that change. Behaviour is identical;
the fallible builders now drop the input on Err, which is fine for these
rename/move/resize transforms (all current callers replace the file).

Impact is small in practice — with_folder/with_size have no callers and
with_name is test-only, while the one hot join caller (create_file_path)
must copy segments regardless — but the consuming form is the idiomatic one.

Verified: cargo fmt + clippy --all-features --all-targets -D warnings clean;
domain tests (path_service::, entities::file::) pass — 30 + 6.

https://claude.ai/code/session_01UtfkS3nZF1vrF5jNAps6wV
This commit is contained in:
Claude
2026-06-09 13:45:16 +00:00
parent ec8ddebc30
commit bf46c1ca10
3 changed files with 31 additions and 59 deletions
+5 -7
View File
@@ -103,18 +103,16 @@ impl StoragePath {
Self { segments }
}
/// Appends a segment to the path.
/// Appends a segment to the path, consuming `self` so the existing
/// segment buffer is reused instead of deep-cloned.
///
/// Traversal segments (`.`, `..`) and segments containing `/` are
/// silently ignored to prevent path-traversal attacks.
pub fn join(&self, segment: &str) -> Self {
let mut new_segments = self.segments.clone();
pub fn join(mut self, segment: &str) -> Self {
if Self::is_safe_segment(segment) {
new_segments.push(segment.to_string());
}
Self {
segments: new_segments,
self.segments.push(segment.to_string());
}
self
}
/// Gets the file name (last segment)