1pub use core_foundation_sys::url::*;
13
14use crate::base::{CFIndex, TCFType};
15use crate::string::CFString;
16
17use core::ffi::c_char;
18use core_foundation_sys::base::{kCFAllocatorDefault, Boolean};
19use std::fmt;
20use std::path::{Path, PathBuf};
21use std::ptr;
22
23use libc::{strlen, PATH_MAX};
24
25#[cfg(unix)]
26use std::ffi::OsStr;
27#[cfg(unix)]
28use std::os::unix::ffi::OsStrExt;
29
30declare_TCFType!(CFURL, CFURLRef);
31impl_TCFType!(CFURL, CFURLRef, CFURLGetTypeID);
32
33impl fmt::Debug for CFURL {
34 #[inline]
35 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
36 unsafe {
37 let string: CFString = TCFType::wrap_under_get_rule(CFURLGetString(self.0));
38 write!(f, "{}", string)
39 }
40 }
41}
42
43impl CFURL {
44 pub fn from_path<P: AsRef<Path>>(path: P, isDirectory: bool) -> Option<CFURL> {
45 let path_bytes;
46 #[cfg(unix)]
47 {
48 path_bytes = path.as_ref().as_os_str().as_bytes()
49 }
50 #[cfg(not(unix))]
51 {
52 path_bytes = match path.as_ref().to_str() {
55 Some(path) => path,
56 None => return None,
57 }
58 }
59
60 unsafe {
61 let url_ref = CFURLCreateFromFileSystemRepresentation(
62 ptr::null_mut(),
63 path_bytes.as_ptr(),
64 path_bytes.len() as CFIndex,
65 isDirectory as u8,
66 );
67 if url_ref.is_null() {
68 return None;
69 }
70 Some(TCFType::wrap_under_create_rule(url_ref))
71 }
72 }
73
74 pub fn from_file_system_path(
75 filePath: CFString,
76 pathStyle: CFURLPathStyle,
77 isDirectory: bool,
78 ) -> CFURL {
79 unsafe {
80 let url_ref = CFURLCreateWithFileSystemPath(
81 kCFAllocatorDefault,
82 filePath.as_concrete_TypeRef(),
83 pathStyle,
84 isDirectory as u8,
85 );
86 TCFType::wrap_under_create_rule(url_ref)
87 }
88 }
89
90 #[cfg(unix)]
91 pub fn to_path(&self) -> Option<PathBuf> {
92 unsafe {
94 let mut buf = [0u8; PATH_MAX as usize];
95 let result = CFURLGetFileSystemRepresentation(
96 self.0,
97 true as Boolean,
98 buf.as_mut_ptr(),
99 buf.len() as CFIndex,
100 );
101 if result == false as Boolean {
102 return None;
103 }
104 let len = strlen(buf.as_ptr() as *const c_char);
105 let path = OsStr::from_bytes(&buf[0..len]);
106 Some(PathBuf::from(path))
107 }
108 }
109
110 pub fn get_string(&self) -> CFString {
111 unsafe { TCFType::wrap_under_get_rule(CFURLGetString(self.0)) }
112 }
113
114 pub fn get_file_system_path(&self, pathStyle: CFURLPathStyle) -> CFString {
115 unsafe {
116 TCFType::wrap_under_create_rule(CFURLCopyFileSystemPath(
117 self.as_concrete_TypeRef(),
118 pathStyle,
119 ))
120 }
121 }
122
123 pub fn absolute(&self) -> CFURL {
124 unsafe { TCFType::wrap_under_create_rule(CFURLCopyAbsoluteURL(self.as_concrete_TypeRef())) }
125 }
126}
127
128#[test]
129fn file_url_from_path() {
130 let path = "/usr/local/foo/";
131 let cfstr_path = CFString::from_static_string(path);
132 let cfurl = CFURL::from_file_system_path(cfstr_path, kCFURLPOSIXPathStyle, true);
133 assert_eq!(cfurl.get_string().to_string(), "file:///usr/local/foo/");
134}
135
136#[cfg(unix)]
137#[test]
138fn non_utf8() {
139 use std::ffi::OsStr;
140 let path = Path::new(OsStr::from_bytes(b"/\xC0/blame"));
141 let cfurl = CFURL::from_path(path, false).unwrap();
142 assert_eq!(cfurl.to_path().unwrap(), path);
143 let len = unsafe { CFURLGetBytes(cfurl.as_concrete_TypeRef(), ptr::null_mut(), 0) };
144 assert_eq!(len, 17);
145}
146
147#[test]
148fn absolute_file_url() {
149 use core_foundation_sys::url::CFURLCreateWithFileSystemPathRelativeToBase;
150 use std::path::PathBuf;
151
152 let path = "/usr/local/foo";
153 let file = "bar";
154
155 let cfstr_path = CFString::from_static_string(path);
156 let cfstr_file = CFString::from_static_string(file);
157 let cfurl_base = CFURL::from_file_system_path(cfstr_path, kCFURLPOSIXPathStyle, true);
158 let cfurl_relative: CFURL = unsafe {
159 let url_ref = CFURLCreateWithFileSystemPathRelativeToBase(
160 kCFAllocatorDefault,
161 cfstr_file.as_concrete_TypeRef(),
162 kCFURLPOSIXPathStyle,
163 false as u8,
164 cfurl_base.as_concrete_TypeRef(),
165 );
166 TCFType::wrap_under_create_rule(url_ref)
167 };
168
169 let mut absolute_path = PathBuf::from(path);
170 absolute_path.push(file);
171
172 assert_eq!(
173 cfurl_relative
174 .get_file_system_path(kCFURLPOSIXPathStyle)
175 .to_string(),
176 file
177 );
178 assert_eq!(
179 cfurl_relative
180 .absolute()
181 .get_file_system_path(kCFURLPOSIXPathStyle)
182 .to_string(),
183 absolute_path.to_str().unwrap()
184 );
185}