1pub 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 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 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}