Skip to main content

apple_cf/cf/
url.rs

1use std::ptr;
2
3use super::CFURL;
4use crate::raw;
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7#[repr(i64)]
8pub enum CFURLComponent {
9    Scheme = 1,
10    Authority = 2,
11    Path = 3,
12    ResourceSpecifier = 4,
13    User = 5,
14    Password = 6,
15    Host = 8,
16    Port = 9,
17    Parameters = 10,
18    Query = 11,
19    Fragment = 12,
20}
21
22impl CFURL {
23    #[must_use]
24    pub fn parse(value: &str, base: Option<&Self>) -> Option<Self> {
25        let length = i64::try_from(value.len()).ok()?;
26        let reference = unsafe {
27            raw::CFURLCreateWithBytes(
28                ptr::null(),
29                value.as_ptr(),
30                length,
31                0x0800_0100,
32                base.map_or(ptr::null(), |base| base.as_ptr().cast()),
33            )
34        };
35        let reference = unsafe { Self::from_raw(reference.cast_mut().cast()) }?;
36        if base.is_none() {
37            return Some(reference);
38        }
39        let absolute = unsafe { raw::CFURLCopyAbsoluteURL(reference.as_ptr().cast()) };
40        unsafe { Self::from_raw(absolute.cast_mut().cast()) }
41    }
42
43    #[must_use]
44    pub fn component(&self, component: CFURLComponent) -> Option<String> {
45        let url = self.as_ptr().cast();
46        let range =
47            unsafe { raw::CFURLGetByteRangeForComponent(url, component as i64, ptr::null_mut()) };
48        let start = usize::try_from(range.location).ok()?;
49        let length = usize::try_from(range.length).ok()?;
50        let byte_count = unsafe { raw::CFURLGetBytes(url, ptr::null_mut(), 0) };
51        let mut bytes = vec![0; usize::try_from(byte_count).ok()?];
52        let copied = unsafe { raw::CFURLGetBytes(url, bytes.as_mut_ptr(), byte_count) };
53        if copied != byte_count {
54            return None;
55        }
56        let end = start.checked_add(length)?;
57        String::from_utf8(bytes.get(start..end)?.to_vec()).ok()
58    }
59}