123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336 |
- use crate::ResultExt;
- use std;
- use std::ffi::OsStr;
- use std::fs::{self, File};
- use std::io::{self, BufWriter, Write};
- use std::path::{Component, Path, PathBuf};
- use term;
- use walkdir;
- /// Returns true if the path has a filename indicating that it is a high-desity
- /// "retina" icon. Specifically, returns true the the file stem ends with
- /// "@2x" (a convention specified by the [Apple developer docs](
- /// https://developer.apple.com/library/mac/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Optimizing/Optimizing.html)).
- pub fn is_retina<P: AsRef<Path>>(path: P) -> bool {
- path
- .as_ref()
- .file_stem()
- .and_then(OsStr::to_str)
- .map(|stem| stem.ends_with("@2x"))
- .unwrap_or(false)
- }
- /// Creates a new file at the given path, creating any parent directories as
- /// needed.
- pub fn create_file(path: &Path) -> crate::Result<BufWriter<File>> {
- if let Some(parent) = path.parent() {
- fs::create_dir_all(&parent).chain_err(|| format!("Failed to create directory {:?}", parent))?;
- }
- let file = File::create(path).chain_err(|| format!("Failed to create file {:?}", path))?;
- Ok(BufWriter::new(file))
- }
- #[cfg(unix)]
- fn symlink_dir(src: &Path, dst: &Path) -> io::Result<()> {
- std::os::unix::fs::symlink(src, dst)
- }
- #[cfg(windows)]
- fn symlink_dir(src: &Path, dst: &Path) -> io::Result<()> {
- std::os::windows::fs::symlink_dir(src, dst)
- }
- #[cfg(unix)]
- fn symlink_file(src: &Path, dst: &Path) -> io::Result<()> {
- std::os::unix::fs::symlink(src, dst)
- }
- #[cfg(windows)]
- fn symlink_file(src: &Path, dst: &Path) -> io::Result<()> {
- std::os::windows::fs::symlink_file(src, dst)
- }
- /// Copies a regular file from one path to another, creating any parent
- /// directories of the destination path as necessary. Fails if the source path
- /// is a directory or doesn't exist.
- pub fn copy_file(from: &Path, to: &Path) -> crate::Result<()> {
- if !from.exists() {
- bail!("{:?} does not exist", from);
- }
- if !from.is_file() {
- bail!("{:?} is not a file", from);
- }
- let dest_dir = to.parent().expect("No data in parent");
- fs::create_dir_all(dest_dir).chain_err(|| format!("Failed to create {:?}", dest_dir))?;
- fs::copy(from, to).chain_err(|| format!("Failed to copy {:?} to {:?}", from, to))?;
- Ok(())
- }
- /// Recursively copies a directory file from one path to another, creating any
- /// parent directories of the destination path as necessary. Fails if the
- /// source path is not a directory or doesn't exist, or if the destination path
- /// already exists.
- pub fn copy_dir(from: &Path, to: &Path) -> crate::Result<()> {
- if !from.exists() {
- bail!("{:?} does not exist", from);
- }
- if !from.is_dir() {
- bail!("{:?} is not a directory", from);
- }
- if to.exists() {
- bail!("{:?} already exists", to);
- }
- let parent = to.parent().expect("No data in parent");
- fs::create_dir_all(parent).chain_err(|| format!("Failed to create {:?}", parent))?;
- for entry in walkdir::WalkDir::new(from) {
- let entry = entry?;
- debug_assert!(entry.path().starts_with(from));
- let rel_path = entry.path().strip_prefix(from)?;
- let dest_path = to.join(rel_path);
- if entry.file_type().is_symlink() {
- let target = fs::read_link(entry.path())?;
- if entry.path().is_dir() {
- symlink_dir(&target, &dest_path)?;
- } else {
- symlink_file(&target, &dest_path)?;
- }
- } else if entry.file_type().is_dir() {
- fs::create_dir(dest_path)?;
- } else {
- fs::copy(entry.path(), dest_path)?;
- }
- }
- Ok(())
- }
- /// Given a path (absolute or relative) to a resource file, returns the
- /// relative path from the bundle resources directory where that resource
- /// should be stored.
- pub fn resource_relpath(path: &Path) -> PathBuf {
- let mut dest = PathBuf::new();
- for component in path.components() {
- match component {
- Component::Prefix(_) => {}
- Component::RootDir => dest.push("_root_"),
- Component::CurDir => {}
- Component::ParentDir => dest.push("_up_"),
- Component::Normal(string) => dest.push(string),
- }
- }
- dest
- }
- /// Prints a message to stderr, in the same format that `cargo` uses,
- /// indicating that we are creating a bundle with the given filename.
- pub fn print_bundling(filename: &str) -> crate::Result<()> {
- print_progress("Bundling", filename)
- }
- /// Prints a message to stderr, in the same format that `cargo` uses,
- /// indicating that we have finished the the given bundles.
- pub fn print_finished(output_paths: &Vec<PathBuf>) -> crate::Result<()> {
- let pluralised = if output_paths.len() == 1 {
- "bundle"
- } else {
- "bundles"
- };
- let msg = format!("{} {} at:", output_paths.len(), pluralised);
- print_progress("Finished", &msg)?;
- for path in output_paths {
- println!(" {}", path.display());
- }
- Ok(())
- }
- fn safe_term_attr<T: term::Terminal + ?Sized>(
- output: &mut Box<T>,
- attr: term::Attr,
- ) -> term::Result<()> {
- match output.supports_attr(attr) {
- true => output.attr(attr),
- false => Ok(()),
- }
- }
- fn print_progress(step: &str, msg: &str) -> crate::Result<()> {
- if let Some(mut output) = term::stderr() {
- safe_term_attr(&mut output, term::Attr::Bold)?;
- output.fg(term::color::GREEN)?;
- write!(output, " {}", step)?;
- output.reset()?;
- write!(output, " {}\n", msg)?;
- output.flush()?;
- Ok(())
- } else {
- let mut output = io::stderr();
- write!(output, " {}", step)?;
- write!(output, " {}\n", msg)?;
- output.flush()?;
- Ok(())
- }
- }
- /// Prints a warning message to stderr, in the same format that `cargo` uses.
- pub fn print_warning(message: &str) -> crate::Result<()> {
- if let Some(mut output) = term::stderr() {
- safe_term_attr(&mut output, term::Attr::Bold)?;
- output.fg(term::color::YELLOW)?;
- write!(output, "warning:")?;
- output.reset()?;
- write!(output, " {}\n", message)?;
- output.flush()?;
- Ok(())
- } else {
- let mut output = io::stderr();
- write!(output, "warning:")?;
- write!(output, " {}\n", message)?;
- output.flush()?;
- Ok(())
- }
- }
- /// Prints a Info message to stderr.
- pub fn print_info(message: &str) -> crate::Result<()> {
- if let Some(mut output) = term::stderr() {
- safe_term_attr(&mut output, term::Attr::Bold)?;
- output.fg(term::color::GREEN)?;
- write!(output, "info:")?;
- output.reset()?;
- write!(output, " {}\n", message)?;
- output.flush()?;
- Ok(())
- } else {
- let mut output = io::stderr();
- write!(output, "info:")?;
- write!(output, " {}\n", message)?;
- output.flush()?;
- Ok(())
- }
- }
- /// Prints an error to stderr, in the same format that `cargo` uses.
- pub fn print_error(error: &crate::Error) -> crate::Result<()> {
- if let Some(mut output) = term::stderr() {
- safe_term_attr(&mut output, term::Attr::Bold)?;
- output.fg(term::color::RED)?;
- write!(output, "error:")?;
- output.reset()?;
- safe_term_attr(&mut output, term::Attr::Bold)?;
- writeln!(output, " {}", error)?;
- output.reset()?;
- for cause in error.iter().skip(1) {
- writeln!(output, " Caused by: {}", cause)?;
- }
- if let Some(backtrace) = error.backtrace() {
- writeln!(output, "{:?}", backtrace)?;
- }
- output.flush()?;
- std::process::exit(1)
- } else {
- let mut output = io::stderr();
- write!(output, "error:")?;
- writeln!(output, " {}", error)?;
- for cause in error.iter().skip(1) {
- writeln!(output, " Caused by: {}", cause)?;
- }
- if let Some(backtrace) = error.backtrace() {
- writeln!(output, "{:?}", backtrace)?;
- }
- output.flush()?;
- std::process::exit(1)
- }
- }
- #[cfg(test)]
- mod tests {
- use super::{copy_dir, create_file, is_retina, resource_relpath, symlink_file};
- use std;
- use std::io::Write;
- use std::path::PathBuf;
- use tempfile;
- #[test]
- fn create_file_with_parent_dirs() {
- let tmp = tempfile::tempdir().expect("Unable to create temp dir");
- assert!(!tmp.path().join("parent").exists());
- {
- let mut file =
- create_file(&tmp.path().join("parent/file.txt")).expect("Failed to create file");
- write!(file, "Hello, world!\n").expect("unable to write file");
- }
- assert!(tmp.path().join("parent").is_dir());
- assert!(tmp.path().join("parent/file.txt").is_file());
- }
- #[test]
- fn copy_dir_with_symlinks() {
- // Create a directory structure that looks like this:
- // ${TMP}/orig/
- // sub/
- // file.txt
- // link -> sub/file.txt
- let tmp = tempfile::tempdir().expect("unable to create tempdir");
- {
- let mut file =
- create_file(&tmp.path().join("orig/sub/file.txt")).expect("Unable to create file");
- write!(file, "Hello, world!\n").expect("Unable to write to file");
- }
- symlink_file(
- &PathBuf::from("sub/file.txt"),
- &tmp.path().join("orig/link"),
- )
- .expect("Failed to create symlink");
- assert_eq!(
- std::fs::read(tmp.path().join("orig/link"))
- .expect("Failed to read file")
- .as_slice(),
- b"Hello, world!\n"
- );
- // Copy ${TMP}/orig to ${TMP}/parent/copy, and make sure that the
- // directory structure, file, and symlink got copied correctly.
- copy_dir(&tmp.path().join("orig"), &tmp.path().join("parent/copy"))
- .expect("Failed to copy dir");
- assert!(tmp.path().join("parent/copy").is_dir());
- assert!(tmp.path().join("parent/copy/sub").is_dir());
- assert!(tmp.path().join("parent/copy/sub/file.txt").is_file());
- assert_eq!(
- std::fs::read(tmp.path().join("parent/copy/sub/file.txt"))
- .expect("Failed to read file")
- .as_slice(),
- b"Hello, world!\n"
- );
- assert!(tmp.path().join("parent/copy/link").exists());
- assert_eq!(
- std::fs::read_link(tmp.path().join("parent/copy/link")).expect("Failed to read from symlink"),
- PathBuf::from("sub/file.txt")
- );
- assert_eq!(
- std::fs::read(tmp.path().join("parent/copy/link"))
- .expect("Failed to read from file")
- .as_slice(),
- b"Hello, world!\n"
- );
- }
- #[test]
- fn retina_icon_paths() {
- assert!(!is_retina("data/icons/512x512.png"));
- assert!(is_retina("data/icons/512x512@2x.png"));
- }
- #[test]
- fn resource_relative_paths() {
- assert_eq!(
- resource_relpath(&PathBuf::from("./data/images/button.png")),
- PathBuf::from("data/images/button.png")
- );
- assert_eq!(
- resource_relpath(&PathBuf::from("../../images/wheel.png")),
- PathBuf::from("_up_/_up_/images/wheel.png")
- );
- assert_eq!(
- resource_relpath(&PathBuf::from("/home/ferris/crab.png")),
- PathBuf::from("_root_/home/ferris/crab.png")
- );
- }
- }
|