2018-12-02 13:19:37 +00:00
|
|
|
use glib;
|
|
|
|
use gst;
|
|
|
|
use url::Url;
|
|
|
|
|
|
|
|
use std::convert::AsRef;
|
|
|
|
use std::fmt;
|
|
|
|
use std::ops::Deref;
|
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
|
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
const WIN_EXT_PATH_PREFIX: &str = "\\\\?\\";
|
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
const WIN_EXT_PATH_PREFIX_LEN: usize = 4;
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub(super) struct FileLocation(PathBuf);
|
|
|
|
|
|
|
|
impl FileLocation {
|
|
|
|
pub(super) fn try_from_path_str(path_str: String) -> Result<Self, glib::Error> {
|
|
|
|
FileLocation::try_from(PathBuf::from(path_str))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(super) fn try_from_uri_str(uri_str: &str) -> Result<Self, glib::Error> {
|
|
|
|
match Url::parse(uri_str) {
|
|
|
|
Ok(url) => {
|
|
|
|
if url.scheme() != "file" {
|
2019-11-11 12:29:41 +00:00
|
|
|
return Err(glib::Error::new(
|
2018-12-02 13:19:37 +00:00
|
|
|
gst::URIError::UnsupportedProtocol,
|
|
|
|
format!("Unsupported URI {}", uri_str).as_str(),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
let path = url.to_file_path().or_else(|_| {
|
2019-11-11 12:29:41 +00:00
|
|
|
Err(glib::Error::new(
|
2018-12-02 13:19:37 +00:00
|
|
|
gst::URIError::BadUri,
|
|
|
|
format!("Unsupported URI {}", uri_str).as_str(),
|
|
|
|
))
|
|
|
|
})?;
|
|
|
|
|
|
|
|
FileLocation::try_from(path)
|
|
|
|
}
|
2019-11-11 12:29:41 +00:00
|
|
|
Err(err) => Err(glib::Error::new(
|
2018-12-02 13:19:37 +00:00
|
|
|
gst::URIError::BadUri,
|
|
|
|
format!("Couldn't parse URI {}: {}", uri_str, err.to_string()).as_str(),
|
|
|
|
)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn try_from(location: PathBuf) -> Result<Self, glib::Error> {
|
|
|
|
let location_str = location.to_str().ok_or_else(|| {
|
2019-11-11 12:29:41 +00:00
|
|
|
glib::Error::new(
|
2018-12-30 10:02:28 +00:00
|
|
|
gst::URIError::BadReference,
|
2018-12-02 13:19:37 +00:00
|
|
|
format!("Invalid path {:?}", location).as_str(),
|
|
|
|
)
|
|
|
|
})?;
|
|
|
|
|
|
|
|
let file_name = location.file_name().ok_or_else(|| {
|
2019-11-11 12:29:41 +00:00
|
|
|
glib::Error::new(
|
2018-12-30 10:02:28 +00:00
|
|
|
gst::URIError::BadReference,
|
2018-12-02 13:19:37 +00:00
|
|
|
format!("Expected a path with a filename, got {}", location_str,).as_str(),
|
|
|
|
)
|
|
|
|
})?;
|
|
|
|
|
|
|
|
// The filename might not exist yet, so check the parent only.
|
|
|
|
// Note: `location` contains a filename, so its parent can't be `None`
|
|
|
|
let mut parent_dir = location
|
|
|
|
.parent()
|
|
|
|
.expect("FileSink::set_location `location` with filename but without a parent")
|
|
|
|
.to_owned();
|
|
|
|
if parent_dir.is_relative() && parent_dir.components().next() == None {
|
|
|
|
// `location` only contains the filename
|
|
|
|
// need to specify "." for `canonicalize` to resolve the actual path
|
|
|
|
parent_dir = PathBuf::from(".");
|
|
|
|
}
|
|
|
|
|
|
|
|
let parent_canonical = parent_dir.canonicalize().map_err(|err| {
|
2019-11-11 12:29:41 +00:00
|
|
|
glib::Error::new(
|
2018-12-30 10:02:28 +00:00
|
|
|
gst::URIError::BadReference,
|
2018-12-02 13:19:37 +00:00
|
|
|
format!(
|
|
|
|
"Could not resolve path {}: {}",
|
|
|
|
location_str,
|
|
|
|
err.to_string(),
|
|
|
|
)
|
|
|
|
.as_str(),
|
|
|
|
)
|
|
|
|
})?;
|
|
|
|
|
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
let parent_canonical = {
|
|
|
|
let has_prefix = parent_canonical
|
|
|
|
.to_str()
|
|
|
|
.unwrap() // already checked above
|
|
|
|
.starts_with(WIN_EXT_PATH_PREFIX);
|
|
|
|
if has_prefix {
|
|
|
|
// Remove the "extended length path" prefix
|
|
|
|
// for compatibility with applications which can't deal with it.
|
|
|
|
// See https://doc.rust-lang.org/std/fs/fn.canonicalize.html
|
|
|
|
let parent_canonical_str = parent_canonical.to_str().unwrap();
|
|
|
|
PathBuf::from(&parent_canonical_str[WIN_EXT_PATH_PREFIX_LEN..])
|
|
|
|
} else {
|
|
|
|
parent_canonical
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
let location_canonical = parent_canonical.join(file_name);
|
|
|
|
Url::from_file_path(&location_canonical)
|
|
|
|
.map_err(|_| {
|
2019-11-11 12:29:41 +00:00
|
|
|
glib::Error::new(
|
2018-12-30 10:02:28 +00:00
|
|
|
gst::URIError::BadReference,
|
2018-12-02 13:19:37 +00:00
|
|
|
format!("Could not resolve path to URL {}", location_str).as_str(),
|
|
|
|
)
|
|
|
|
})
|
|
|
|
.map(|_| FileLocation(location_canonical))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn to_str(&self) -> &str {
|
|
|
|
self.0
|
|
|
|
.to_str()
|
|
|
|
.expect("FileLocation: couldn't get `&str` from internal `PathBuf`")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AsRef<Path> for FileLocation {
|
|
|
|
fn as_ref(&self) -> &Path {
|
|
|
|
self.0.as_ref()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Deref for FileLocation {
|
|
|
|
type Target = Path;
|
|
|
|
|
|
|
|
fn deref(&self) -> &Path {
|
|
|
|
self.0.as_ref()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for FileLocation {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
write!(f, "{}", self.to_str())
|
|
|
|
}
|
|
|
|
}
|