Skip to main content

qubit_fs/path/
fs_uri.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2026 Haixing Hu.
4 *
5 *    SPDX-License-Identifier: Apache-2.0
6 *
7 *    Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10//! Full filesystem URI model.
11
12use qubit_metadata::Metadata;
13use url::Url;
14
15use crate::{
16    FsAuthority,
17    FsError,
18    FsOperation,
19    FsPath,
20    FsResult,
21};
22
23/// Full filesystem URI.
24#[derive(Clone, Debug, PartialEq)]
25pub struct FsUri {
26    /// Provider scheme.
27    pub scheme: String,
28    /// Optional URI authority.
29    pub authority: Option<FsAuthority>,
30    /// Provider-local path.
31    pub path: FsPath,
32    /// Non-sensitive query options.
33    pub query: Metadata,
34}
35
36impl FsUri {
37    /// Parses a full filesystem URI.
38    ///
39    /// # Parameters
40    /// - `uri`: URI string to parse.
41    ///
42    /// # Returns
43    /// Parsed filesystem URI.
44    ///
45    /// # Errors
46    /// Returns [`FsError`] when the URI or its path is invalid.
47    pub fn parse(uri: &str) -> FsResult<Self> {
48        let parsed = Url::parse(uri).map_err(|error| {
49            FsError::with_source(
50                crate::FsErrorKind::InvalidPath,
51                FsOperation::ParseUri,
52                "invalid filesystem URI",
53                error,
54            )
55        })?;
56        let scheme = parsed.scheme().to_ascii_lowercase();
57        let authority = parsed.host_str().map(|host| {
58            let mut authority = FsAuthority::new(host);
59            if let Some(port) = parsed.port() {
60                authority = authority.with_port(port);
61            }
62            let username = parsed.username();
63            if !username.is_empty() {
64                authority = authority.with_username(username);
65            }
66            authority
67        });
68        let path = FsPath::parse(parsed.path()).map_err(|error| {
69            FsError::new(
70                error.kind(),
71                FsOperation::ParseUri,
72                "URI path is not a valid filesystem path",
73            )
74        })?;
75        let mut query = Metadata::new();
76        for (key, value) in parsed.query_pairs() {
77            query.set(key.as_ref(), value.into_owned());
78        }
79        Ok(Self {
80            scheme,
81            authority,
82            path,
83            query,
84        })
85    }
86}