Skip to main content

loonfs_core/path/
helpers.rs

1//! Path parsing helpers shared by the read and write paths.
2
3use crate::error::{CoreError, Result};
4use loonfs_api::{AbsolutePath, DisplayName, PathError};
5
6pub(crate) fn parse_absolute_path_for_core(absolute_path: &str) -> Result<AbsolutePath> {
7    AbsolutePath::parse(absolute_path).map_err(map_path_error_to_core)
8}
9
10/// Parses a raw string into the validated, normalized path a
11/// [`CommitRequest`](crate::path::write::CommitRequest) carries:
12/// absolute, normalized, and not the root.
13///
14/// This is the one named home for the invariant. Every surface that accepts
15/// a raw mutation path parses through it exactly once; planning re-asserts
16/// only the root guard ([`ensure_mutation_path`]) and never re-parses.
17pub fn parse_mutation_path(absolute_path: &str) -> Result<AbsolutePath> {
18    let path = parse_absolute_path_for_core(absolute_path)?;
19    ensure_mutation_path(&path)?;
20    Ok(path)
21}
22
23/// The root-mutation guard on an already-parsed path.
24///
25/// The absolute-path grammar is carried by the type; this rejects the root,
26/// which is readable but cannot be mutated. Intents can be built from parsed
27/// paths directly, so planning is where the invariant is enforced, and
28/// planning is the only place: a caller that rejects the root ahead of the
29/// planners answers for the request rather than for the operation that
30/// named it.
31pub(crate) fn ensure_mutation_path(path: &AbsolutePath) -> Result<()> {
32    if path.is_root() {
33        return Err(CoreError::RootMutationForbidden);
34    }
35    Ok(())
36}
37
38pub(crate) fn map_path_error_to_core(error: PathError) -> CoreError {
39    CoreError::InvalidPath(error.invalid_path_input().to_owned())
40}
41
42pub(crate) fn final_component(absolute_path: &AbsolutePath) -> Result<DisplayName> {
43    absolute_path
44        .final_component()
45        .map(|component| component.to_display_name())
46        .ok_or_else(|| CoreError::InvalidPath(absolute_path.as_str().to_owned()))
47}