core_foundation/
url.rs

1// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution.
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10//! A URL type for Core Foundation.
11
12pub use core_foundation_sys::url::*;
13
14use crate::base::{CFIndex, TCFType};
15use crate::string::CFString;
16
17use core_foundation_sys::base::{kCFAllocatorDefault, Boolean};
18use std::fmt;
19use std::path::{Path, PathBuf};
20use std::ptr;
21
22use libc::{c_char, strlen, PATH_MAX};
23
24#[cfg(unix)]
25use std::ffi::OsStr;
26#[cfg(unix)]
27use std::os::unix::ffi::OsStrExt;
28
29declare_TCFType!(CFURL, CFURLRef);
30impl_TCFType!(CFURL, CFURLRef, CFURLGetTypeID);
31
32impl fmt::Debug for CFURL {
33    #[inline]
34    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
35        unsafe {
36            let string: CFString = TCFType::wrap_under_get_rule(CFURLGetString(self.0));
37            write!(f, "{}", string)
38        }
39    }
40}
41
42impl CFURL {
43    pub fn from_path<P: AsRef<Path>>(path: P, isDirectory: bool) -> Option<CFURL> {
44        let path_bytes;
45        #[cfg(unix)]
46        {
47            path_bytes = path.as_ref().as_os_str().as_bytes()
48        }
49        #[cfg(not(unix))]
50        {
51            // XXX: Getting non-valid UTF8 paths into CoreFoundation on Windows is going to be unpleasant
52            // CFURLGetWideFileSystemRepresentation might help
53            path_bytes = match path.as_ref().to_str() {
54                Some(path) => path,
55                None => return None,
56            }
57        }
58
59        unsafe {
60            let url_ref = CFURLCreateFromFileSystemRepresentation(
61                ptr::null_mut(),
62                path_bytes.as_ptr(),
63                path_bytes.len() as CFIndex,
64                isDirectory as u8,
65            );
66            if url_ref.is_null() {
67                return None;
68            }
69            Some(TCFType::wrap_under_create_rule(url_ref))
70        }
71    }
72
73    pub fn from_file_system_path(
74        filePath: CFString,
75        pathStyle: CFURLPathStyle,
76        isDirectory: bool,
77    ) -> CFURL {
78        unsafe {
79            let url_ref = CFURLCreateWithFileSystemPath(
80                kCFAllocatorDefault,
81                filePath.as_concrete_TypeRef(),
82                pathStyle,
83                isDirectory as u8,
84            );
85            TCFType::wrap_under_create_rule(url_ref)
86        }
87    }
88
89    #[cfg(unix)]
90    pub fn to_path(&self) -> Option<PathBuf> {
91        // implementing this on Windows is more complicated because of the different OsStr representation
92        unsafe {
93            let mut buf = [0u8; PATH_MAX as usize];
94            let result = CFURLGetFileSystemRepresentation(
95                self.0,
96                true as Boolean,
97                buf.as_mut_ptr(),
98                buf.len() as CFIndex,
99            );
100            if result == false as Boolean {
101                return None;
102            }
103            let len = strlen(buf.as_ptr() as *const c_char);
104            let path = OsStr::from_bytes(&buf[0..len]);
105            Some(PathBuf::from(path))
106        }
107    }
108
109    pub fn get_string(&self) -> CFString {
110        unsafe { TCFType::wrap_under_get_rule(CFURLGetString(self.0)) }
111    }
112
113    pub fn get_file_system_path(&self, pathStyle: CFURLPathStyle) -> CFString {
114        unsafe {
115            TCFType::wrap_under_create_rule(CFURLCopyFileSystemPath(
116                self.as_concrete_TypeRef(),
117                pathStyle,
118            ))
119        }
120    }
121
122    pub fn absolute(&self) -> CFURL {
123        unsafe { TCFType::wrap_under_create_rule(CFURLCopyAbsoluteURL(self.as_concrete_TypeRef())) }
124    }
125}
126
127#[test]
128fn file_url_from_path() {
129    let path = "/usr/local/foo/";
130    let cfstr_path = CFString::from_static_string(path);
131    let cfurl = CFURL::from_file_system_path(cfstr_path, kCFURLPOSIXPathStyle, true);
132    assert_eq!(cfurl.get_string().to_string(), "file:///usr/local/foo/");
133}
134
135#[cfg(unix)]
136#[test]
137fn non_utf8() {
138    use std::ffi::OsStr;
139    let path = Path::new(OsStr::from_bytes(b"/\xC0/blame"));
140    let cfurl = CFURL::from_path(path, false).unwrap();
141    assert_eq!(cfurl.to_path().unwrap(), path);
142    let len = unsafe { CFURLGetBytes(cfurl.as_concrete_TypeRef(), ptr::null_mut(), 0) };
143    assert_eq!(len, 17);
144}
145
146#[test]
147fn absolute_file_url() {
148    use core_foundation_sys::url::CFURLCreateWithFileSystemPathRelativeToBase;
149    use std::path::PathBuf;
150
151    let path = "/usr/local/foo";
152    let file = "bar";
153
154    let cfstr_path = CFString::from_static_string(path);
155    let cfstr_file = CFString::from_static_string(file);
156    let cfurl_base = CFURL::from_file_system_path(cfstr_path, kCFURLPOSIXPathStyle, true);
157    let cfurl_relative: CFURL = unsafe {
158        let url_ref = CFURLCreateWithFileSystemPathRelativeToBase(
159            kCFAllocatorDefault,
160            cfstr_file.as_concrete_TypeRef(),
161            kCFURLPOSIXPathStyle,
162            false as u8,
163            cfurl_base.as_concrete_TypeRef(),
164        );
165        TCFType::wrap_under_create_rule(url_ref)
166    };
167
168    let mut absolute_path = PathBuf::from(path);
169    absolute_path.push(file);
170
171    assert_eq!(
172        cfurl_relative
173            .get_file_system_path(kCFURLPOSIXPathStyle)
174            .to_string(),
175        file
176    );
177    assert_eq!(
178        cfurl_relative
179            .absolute()
180            .get_file_system_path(kCFURLPOSIXPathStyle)
181            .to_string(),
182        absolute_path.to_str().unwrap()
183    );
184}