unitycatalog_server/services/
location.rs1use object_store::path::Path;
10use olai_storage_credentials::{StorageLocation, StorageProvider, StorageScope};
11use url::Url;
12
13use crate::{Error, Result};
14
15#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct StorageLocationUrl {
18 raw: Url,
19 location: StorageLocation,
20}
21
22impl StorageLocationUrl {
23 pub fn parse(url: impl AsRef<str>) -> Result<Self> {
25 let url = url.as_ref();
26 let raw = Url::parse(url)
27 .map_err(|e| Error::invalid_argument(format!("invalid storage location: {e}")))?;
28 let location =
29 StorageLocation::parse(&raw).map_err(|e| Error::invalid_argument(e.to_string()))?;
30 Ok(Self { raw, location })
31 }
32
33 pub fn raw(&self) -> &Url {
35 &self.raw
36 }
37
38 pub fn location(&self) -> &StorageLocation {
40 &self.location
41 }
42
43 pub fn scope(&self) -> StorageScope {
45 StorageScope::new(self.location.clone())
46 }
47
48 pub fn path(&self) -> &Path {
50 self.location.path()
51 }
52
53 pub fn is_local(&self) -> bool {
55 self.location.provider() == StorageProvider::Local
56 }
57
58 pub fn starts_with(&self, prefix: &StorageLocationUrl) -> bool {
60 self.location.starts_with(&prefix.location)
61 }
62
63 pub fn overlaps(&self, other: &StorageLocationUrl) -> bool {
65 self.starts_with(other) || other.starts_with(self)
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 fn parse(url: &str) -> StorageLocationUrl {
74 StorageLocationUrl::parse(url).unwrap()
75 }
76
77 #[test]
78 fn overlap_is_segment_aware_and_symmetric() {
79 assert!(parse("s3://b/data").overlaps(&parse("s3://b/data/sub")));
80 assert!(parse("s3://b/data/sub").overlaps(&parse("s3://b/data")));
81 assert!(parse("s3://b/data").overlaps(&parse("s3://b/data/")));
82 assert!(!parse("s3://b/data").overlaps(&parse("s3://b/data-secret")));
83 assert!(!parse("s3://b/data").overlaps(&parse("s3://c/data")));
84 }
85
86 #[test]
87 fn azure_spellings_compare_canonically() {
88 let abfss = parse("abfss://container@account.dfs.core.windows.net/data/tab");
89 let https = parse("https://account.blob.core.windows.net/container/data");
90 assert!(abfss.starts_with(&https));
91 assert!(!https.starts_with(&abfss));
92 }
93
94 #[test]
95 fn local_locations_are_detected() {
96 assert!(parse("file:///tmp/data").is_local());
97 assert!(!parse("s3://b/data").is_local());
98 assert!(StorageLocationUrl::parse("not a url").is_err());
99 }
100}