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::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            // XXX: Getting non-valid UTF8 paths into CoreFoundation on Windows is going to be unpleasant
53            // CFURLGetWideFileSystemRepresentation might help
54            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        // implementing this on Windows is more complicated because of the different OsStr representation
93        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}