<commit_before>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::fs;
use std::io::{self, Write};
use std::path::Path;

use flate2;
use flate2::write::GzEncoder;
use tar::Builder;
use walkdir::WalkDir;
use xz2::write::XzEncoder;

actor!{
    #[derive(Debug)]
    pub struct Tarballer {
        /// The input folder to be compressed
        input: String = "package",

        /// The prefix of the tarballs
        output: String = "./dist",

        /// The fold in which the input is to be found
        work_dir: String = "./workdir",
    }
}

impl Tarballer {
    /// Generate the actual tarballs
    pub fn run(self) -> io::Result<()> {
        let tar_gz = self.output.clone() + ".tar.gz";
        let tar_xz = self.output.clone() + ".tar.xz";

        // Remove any existing files
        for file in &[&tar_gz, &tar_xz] {
            if Path::new(file).exists() {
                fs::remove_file(file)?;
            }
        }

        // Sort files by their suffix, to group files with the same name from
        // different locations (likely identical) and files with the same
        // extension (likely containing similar data).
        let mut paths = get_recursive_paths(self.work_dir.as_ref(), self.input.as_ref())?;
        paths.sort_by(|a, b| a.bytes().rev().cmp(b.bytes().rev()));

        // Prepare the .tar.gz file
        let output = fs::File::create(&tar_gz)?;
        let gz = GzEncoder::new(output, flate2::Compression::Best);

        // Prepare the .tar.xz file
        let output = fs::File::create(&tar_xz)?;
        let xz = XzEncoder::new(output, 9);

        // Write the tar into both encoded files
        let mut builder = Builder::new(Tee(gz, xz));
        for path in paths {
            let path = Path::new(&path);
            let src = Path::new(&self.work_dir).join(path);
            if path.is_dir() {
                builder.append_dir(path, src)?;
            } else {
                let mut src = fs::File::open(src)?;
                builder.append_file(path, &mut src)?;
            }
        }
        let Tee(gz, xz) = builder.into_inner()?;

        // Finish both encoded files
        gz.finish()?;
        xz.finish()?;

        Ok(())
    }
}

fn get_recursive_paths(root: &Path, name: &Path) -> io::Result<Vec<String>> {
    let mut paths = vec![];
    for entry in WalkDir::new(root.join(name)).min_depth(1) {
        let entry = entry?;
        let path = entry.path().strip_prefix(root).unwrap();
        let path = path.to_str().unwrap().to_owned();

        if entry.file_type().is_dir() {
            // Include only empty dirs, as others get add via their contents.
            // FIXME: do we really need empty dirs at all?
            if fs::read_dir(entry.path())?.next().is_none() {
                paths.push(path);
            }
        } else {
            paths.push(path);
        }
    }
    Ok(paths)
}

struct Tee<A, B>(A, B);

impl<A: Write, B: Write> Write for Tee<A, B> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.0.write_all(buf)
            .and(self.1.write_all(buf))
            .and(Ok(buf.len()))
    }

    fn flush(&mut self) -> io::Result<()> {
        self.0.flush().and(self.1.flush())
    }
}
<commit_msg>Write all directories to the tarball for rustup.rs#1092<commit_after>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::fs;
use std::io::{self, Write};
use std::path::Path;

use flate2;
use flate2::write::GzEncoder;
use tar::Builder;
use walkdir::WalkDir;
use xz2::write::XzEncoder;

actor!{
    #[derive(Debug)]
    pub struct Tarballer {
        /// The input folder to be compressed
        input: String = "package",

        /// The prefix of the tarballs
        output: String = "./dist",

        /// The fold in which the input is to be found
        work_dir: String = "./workdir",
    }
}

impl Tarballer {
    /// Generate the actual tarballs
    pub fn run(self) -> io::Result<()> {
        let tar_gz = self.output.clone() + ".tar.gz";
        let tar_xz = self.output.clone() + ".tar.xz";

        // Remove any existing files
        for file in &[&tar_gz, &tar_xz] {
            if Path::new(file).exists() {
                fs::remove_file(file)?;
            }
        }

        // Sort files by their suffix, to group files with the same name from
        // different locations (likely identical) and files with the same
        // extension (likely containing similar data).
        let (dirs, mut files) = get_recursive_paths(self.work_dir.as_ref(),
                                                    self.input.as_ref())?;
        files.sort_by(|a, b| a.bytes().rev().cmp(b.bytes().rev()));

        // Prepare the .tar.gz file
        let output = fs::File::create(&tar_gz)?;
        let gz = GzEncoder::new(output, flate2::Compression::Best);

        // Prepare the .tar.xz file
        let output = fs::File::create(&tar_xz)?;
        let xz = XzEncoder::new(output, 9);

        // Write the tar into both encoded files.  We write all directories
        // first, so files may be directly created. (see rustup.rs#1092)
        let mut builder = Builder::new(Tee(gz, xz));
        for path in dirs {
            let src = Path::new(&self.work_dir).join(&path);
            builder.append_dir(&path, src)?;
        }
        for path in files {
            let src = Path::new(&self.work_dir).join(&path);
            fs::File::open(src)
                .and_then(|mut file| builder.append_file(&path, &mut file))?;
        }
        let Tee(gz, xz) = builder.into_inner()?;

        // Finish both encoded files
        gz.finish()?;
        xz.finish()?;

        Ok(())
    }
}

/// Returns all `(directories, files)` under the source path
fn get_recursive_paths(root: &Path, name: &Path) -> io::Result<(Vec<String>, Vec<String>)> {
    let mut dirs = vec![];
    let mut files = vec![];
    for entry in WalkDir::new(root.join(name)).min_depth(1) {
        let entry = entry?;
        let path = entry.path().strip_prefix(root).unwrap();
        let path = path.to_str().unwrap().to_owned();

        if entry.file_type().is_dir() {
            dirs.push(path);
        } else {
            files.push(path);
        }
    }
    Ok((dirs, files))
}

struct Tee<A, B>(A, B);

impl<A: Write, B: Write> Write for Tee<A, B> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.0.write_all(buf)
            .and(self.1.write_all(buf))
            .and(Ok(buf.len()))
    }

    fn flush(&mut self) -> io::Result<()> {
        self.0.flush().and(self.1.flush())
    }
}
<|endoftext|>


<commit_before>use futures::future;
use futures::Future;
use hyper;
use hyper::Uri;
use hyper::client::{Client, Request};
use mktemp::Temp;
use slog;
use std::io::prelude::*;
use std::path::Path;
use std::process::Command;
use tokio_core::reactor::{Handle, Remote};
use tokio_process::CommandExt;
use tera::Tera;

use http::*;
use papers::DocumentSpec;
use error::{Error, ErrorKind};

pub struct Workspace {
    dir: Temp,
    document_spec: DocumentSpec,
    handle: Handle,
    template_path: ::std::path::PathBuf,
    logger: slog::Logger,
}

struct WorkspaceContext {
    handle: Handle,
    logger: slog::Logger,
    temp_dir_path: ::std::path::PathBuf,
}

/// We ignore file names that end with a slash for now, and always determine the file name from the Uri
/// TODO: investigate Content-Disposition: attachment
fn extract_file_name_from_uri(uri: &Uri) -> Option<String> {
    match uri.path().split('/').last() {
        Some(name) => {
            if !name.is_empty() {
                Some(name.to_string())
            } else {
                None
            }
        },
        None => None,
    }
}

/// Since `mktemp::Temp` implements Drop by deleting the directory, we don't need to worry about
/// leaving files or directories behind. On the flipside, we must ensure it is not dropped before
/// the last returned future that needs the directory finishes.
impl Workspace {

    pub fn new(remote: Remote, document_spec: DocumentSpec, logger: slog::Logger) -> Result<Workspace, Error> {
        let dir = Temp::new_dir()?;
        let mut template_path = dir.to_path_buf();
        template_path.push(Path::new("out.tex"));
        Ok(Workspace {
            document_spec: document_spec,
            handle: remote.handle().unwrap(),
            template_path: template_path,
            dir: dir,
            logger: logger,
        })
    }

    pub fn preview(self) -> Box<Future<Item=String, Error=Error>> {
        let Workspace {
            handle,
            document_spec,
            ..
        } = self;

        let DocumentSpec {
            template_url,
            variables,
            ..
        } = document_spec;

        // Download the template and populate it
        let work = Client::configure()
            .connector(https_connector(&handle.clone()))
            .build(&handle.clone())
            .get_follow_redirect(&template_url.0)
            .and_then(|res| res.get_body_bytes())
            .and_then(|bytes| {
                ::std::string::String::from_utf8(bytes).map_err(Error::from)
            }).and_then(move |template_string| {
                Tera::one_off(&template_string, &variables, false).map_err(Error::from)
            });

        Box::new(work)
    }

    pub fn execute(self) -> Box<Future<Item=(), Error=()>> {
        let Workspace {
            handle,
            document_spec,
            template_path,
            dir,
            logger,
        } = self;

        let DocumentSpec {
            assets_urls,
            callback_url,
            template_url,
            variables,
        } = document_spec;

        debug!(logger, "Starting Workspace worker");

        let context = WorkspaceContext {
            handle: handle,
            logger: logger,
            temp_dir_path: dir.to_path_buf(),
        };

        let error_logger = context.logger.clone();
        let error_path_handle = context.handle.clone();
        let error_path_callback_url = callback_url.0.clone();

        // First download the template and populate it
        let work = Client::configure()
            .connector(https_connector(&context.handle.clone()))
            .build(&context.handle.clone())
            .get_follow_redirect(&template_url.0)
            .and_then(|res| res.get_body_bytes())
            .and_then(|bytes| {
                debug!(context.logger, "Successfully downloaded the template");
                ::std::string::String::from_utf8(bytes)
                    .map_err(Error::from)
                    .map(|template_string| (context, template_string))
            }).and_then(move |(context, template_string)| {
                Tera::one_off(&template_string, &variables, false)
                    .map_err(Error::from)
                    .map(|latex_string| (context, latex_string))
            }).and_then(|(context, latex_string)| {
                debug!(context.logger, "Writing template to {:?}", template_path.clone());
                let mut file = ::std::fs::File::create(template_path.clone()).unwrap();
                file.write_all(latex_string.as_bytes()).expect("could not write latex file");
                debug!(context.logger, "Template successfully written to {:?}", template_path.clone());
                Ok((context, template_path))
            })

        // Then download the assets and save them in the temporary directory
                .and_then(move |(context, template_path)| {
                    let inner_handle = context.handle.clone();
                    let inner_temp_dir_path = context.temp_dir_path.clone();
                    debug!(context.logger, "Downloading assets {:?}", assets_urls);
                    let futures = assets_urls.into_iter().map(move |uri| {
                        let mut path = inner_temp_dir_path.clone();
                        Client::configure()
                            .connector(https_connector(&inner_handle.clone()))
                            .build(&inner_handle.clone())
                            .get_follow_redirect(&uri.0)
                            .map(move |res| (res, uri.0))
                            .and_then(move |(res, uri)| {
                                let file_name = res.file_name().take();
                                res.get_body_bytes().map(|bytes| (bytes, file_name, uri))
                            }).and_then(move |(bytes, file_name, uri)| {
                                let file_name = file_name.or_else(|| extract_file_name_from_uri(&uri));
                                if let Some(file_name) = file_name {
                                    path.push(file_name);
                                    ::std::fs::File::create(&path)
                                        .and_then(|mut file| file.write_all(&bytes))
                                        .map(|_| ())
                                        .map_err(Error::from)
                                } else {
                                    Ok(())
                                }
                            })
                    });
                    future::join_all(futures).map(|result| (context, template_path, result))
                })

        // Then run latex
                .and_then(move |(context, template_path, _)| {
                    let inner_handle = context.handle.clone();
                    Command::new("xelatex")
                        .arg(&format!("-output-directory={}", &context.temp_dir_path.to_str().unwrap()))
                        .arg("-interaction=nonstopmode")
                        .arg(template_path.clone())
                        .output_async(&inner_handle)
                        .map(|output| (context, output))
                        .map_err(Error::from)
                }).and_then(|(context, output)| {
                    let stdout = String::from_utf8(output.clone().stdout).unwrap();
                    if output.status.success() {
                        debug!(context.logger, "{}", stdout);
                        Ok(context)
                    } else {
                        Err(ErrorKind::LatexFailed(stdout).into())
                    }
                })

        // Then construct the path to the generated PDF
                .map(move |context| {
                    let mut path = context.temp_dir_path.clone();
                    path.push(Path::new("out.pdf"));
                    (context, path)
                })

        // Then get a multipart request from the generated PDF
                .and_then(move |(context, pdf_path)| {
                    debug!(context.logger, "Reading the pdf from {:?}", pdf_path);
                    multipart_request_with_file(
                        Request::new(hyper::Method::Post, callback_url.0),
                        pdf_path
                    ).map(|r| (context, r))
                })

        // Finally, post the PDF to the callback URL
                .and_then(move |(context, request)| {
                    // Avoid dir being dropped early
                    let _dir = dir;

                    Client::configure()
                        .connector(https_connector(&context.handle.clone()))
                        .build(&context.handle.clone())
                        .request(request)
                        .map(|_| ())
                        .map_err(Error::from)

        // Report errors to the callback url
                }).or_else(move |error| {
                    error!(error_logger, format!("{}", error));
                    let req = Request::new(hyper::Method::Post, error_path_callback_url);
                    Client::new(&error_path_handle)
                        .request(multipart_request_with_error(req, &error).unwrap())
                        .map(|_| ())
                });

        Box::new(work.map_err(|_| ()))
    }
}

#[cfg(test)]
mod tests {
    use hyper::Uri;
    use super::extract_file_name_from_uri;

    #[test]
    fn test_extract_file_name_from_uri_works() {
        let assert_extracted = |input: &'static str, expected_output: Option<&'static str>| {
            let uri = input.parse::<Uri>().unwrap();
            assert_eq!(extract_file_name_from_uri(&uri), expected_output.map(|o| o.to_string()));
        };

        assert_extracted("/logo.png", Some("logo.png"));
        assert_extracted("/assets/", None);
        assert_extracted("/assets/icon", Some("icon"));
        assert_extracted("/", None);
        assert_extracted("http://www.store2be.com", None);
    }
}
<commit_msg>✨ Use the -file-line-error option from pdflatex<commit_after>use futures::future;
use futures::Future;
use hyper;
use hyper::Uri;
use hyper::client::{Client, Request};
use mktemp::Temp;
use slog;
use std::io::prelude::*;
use std::path::Path;
use std::process::Command;
use tokio_core::reactor::{Handle, Remote};
use tokio_process::CommandExt;
use tera::Tera;

use http::*;
use papers::DocumentSpec;
use error::{Error, ErrorKind};

pub struct Workspace {
    dir: Temp,
    document_spec: DocumentSpec,
    handle: Handle,
    template_path: ::std::path::PathBuf,
    logger: slog::Logger,
}

struct WorkspaceContext {
    handle: Handle,
    logger: slog::Logger,
    temp_dir_path: ::std::path::PathBuf,
}

/// We ignore file names that end with a slash for now, and always determine the file name from the Uri
/// TODO: investigate Content-Disposition: attachment
fn extract_file_name_from_uri(uri: &Uri) -> Option<String> {
    match uri.path().split('/').last() {
        Some(name) => {
            if !name.is_empty() {
                Some(name.to_string())
            } else {
                None
            }
        },
        None => None,
    }
}

/// Since `mktemp::Temp` implements Drop by deleting the directory, we don't need to worry about
/// leaving files or directories behind. On the flipside, we must ensure it is not dropped before
/// the last returned future that needs the directory finishes.
impl Workspace {

    pub fn new(remote: Remote, document_spec: DocumentSpec, logger: slog::Logger) -> Result<Workspace, Error> {
        let dir = Temp::new_dir()?;
        let mut template_path = dir.to_path_buf();
        template_path.push(Path::new("out.tex"));
        Ok(Workspace {
            document_spec: document_spec,
            handle: remote.handle().unwrap(),
            template_path: template_path,
            dir: dir,
            logger: logger,
        })
    }

    pub fn preview(self) -> Box<Future<Item=String, Error=Error>> {
        let Workspace {
            handle,
            document_spec,
            ..
        } = self;

        let DocumentSpec {
            template_url,
            variables,
            ..
        } = document_spec;

        // Download the template and populate it
        let work = Client::configure()
            .connector(https_connector(&handle.clone()))
            .build(&handle.clone())
            .get_follow_redirect(&template_url.0)
            .and_then(|res| res.get_body_bytes())
            .and_then(|bytes| {
                ::std::string::String::from_utf8(bytes).map_err(Error::from)
            }).and_then(move |template_string| {
                Tera::one_off(&template_string, &variables, false).map_err(Error::from)
            });

        Box::new(work)
    }

    pub fn execute(self) -> Box<Future<Item=(), Error=()>> {
        let Workspace {
            handle,
            document_spec,
            template_path,
            dir,
            logger,
        } = self;

        let DocumentSpec {
            assets_urls,
            callback_url,
            template_url,
            variables,
        } = document_spec;

        debug!(logger, "Starting Workspace worker");

        let context = WorkspaceContext {
            handle: handle,
            logger: logger,
            temp_dir_path: dir.to_path_buf(),
        };

        let error_logger = context.logger.clone();
        let error_path_handle = context.handle.clone();
        let error_path_callback_url = callback_url.0.clone();

        // First download the template and populate it
        let work = Client::configure()
            .connector(https_connector(&context.handle.clone()))
            .build(&context.handle.clone())
            .get_follow_redirect(&template_url.0)
            .and_then(|res| res.get_body_bytes())
            .and_then(|bytes| {
                debug!(context.logger, "Successfully downloaded the template");
                ::std::string::String::from_utf8(bytes)
                    .map_err(Error::from)
                    .map(|template_string| (context, template_string))
            }).and_then(move |(context, template_string)| {
                Tera::one_off(&template_string, &variables, false)
                    .map_err(Error::from)
                    .map(|latex_string| (context, latex_string))
            }).and_then(|(context, latex_string)| {
                debug!(context.logger, "Writing template to {:?}", template_path.clone());
                let mut file = ::std::fs::File::create(template_path.clone()).unwrap();
                file.write_all(latex_string.as_bytes()).expect("could not write latex file");
                debug!(context.logger, "Template successfully written to {:?}", template_path.clone());
                Ok((context, template_path))
            })

        // Then download the assets and save them in the temporary directory
                .and_then(move |(context, template_path)| {
                    let inner_handle = context.handle.clone();
                    let inner_temp_dir_path = context.temp_dir_path.clone();
                    debug!(context.logger, "Downloading assets {:?}", assets_urls);
                    let futures = assets_urls.into_iter().map(move |uri| {
                        let mut path = inner_temp_dir_path.clone();
                        Client::configure()
                            .connector(https_connector(&inner_handle.clone()))
                            .build(&inner_handle.clone())
                            .get_follow_redirect(&uri.0)
                            .map(move |res| (res, uri.0))
                            .and_then(move |(res, uri)| {
                                let file_name = res.file_name().take();
                                res.get_body_bytes().map(|bytes| (bytes, file_name, uri))
                            }).and_then(move |(bytes, file_name, uri)| {
                                let file_name = file_name.or_else(|| extract_file_name_from_uri(&uri));
                                if let Some(file_name) = file_name {
                                    path.push(file_name);
                                    ::std::fs::File::create(&path)
                                        .and_then(|mut file| file.write_all(&bytes))
                                        .map(|_| ())
                                        .map_err(Error::from)
                                } else {
                                    Ok(())
                                }
                            })
                    });
                    future::join_all(futures).map(|result| (context, template_path, result))
                })

        // Then run latex
                .and_then(move |(context, template_path, _)| {
                    let inner_handle = context.handle.clone();
                    Command::new("xelatex")
                        .arg(&format!("-output-directory={}", &context.temp_dir_path.to_str().unwrap()))
                        .arg("-interaction=nonstopmode")
                        .arg("-file-line-error")
                        .arg(template_path.clone())
                        .output_async(&inner_handle)
                        .map(|output| (context, output))
                        .map_err(Error::from)
                }).and_then(|(context, output)| {
                    let stdout = String::from_utf8(output.clone().stdout).unwrap();
                    if output.status.success() {
                        debug!(context.logger, "{}", stdout);
                        Ok(context)
                    } else {
                        Err(ErrorKind::LatexFailed(stdout).into())
                    }
                })

        // Then construct the path to the generated PDF
                .map(move |context| {
                    let mut path = context.temp_dir_path.clone();
                    path.push(Path::new("out.pdf"));
                    (context, path)
                })

        // Then get a multipart request from the generated PDF
                .and_then(move |(context, pdf_path)| {
                    debug!(context.logger, "Reading the pdf from {:?}", pdf_path);
                    multipart_request_with_file(
                        Request::new(hyper::Method::Post, callback_url.0),
                        pdf_path
                    ).map(|r| (context, r))
                })

        // Finally, post the PDF to the callback URL
                .and_then(move |(context, request)| {
                    // Avoid dir being dropped early
                    let _dir = dir;

                    Client::configure()
                        .connector(https_connector(&context.handle.clone()))
                        .build(&context.handle.clone())
                        .request(request)
                        .map(|_| ())
                        .map_err(Error::from)

        // Report errors to the callback url
                }).or_else(move |error| {
                    error!(error_logger, format!("{}", error));
                    let req = Request::new(hyper::Method::Post, error_path_callback_url);
                    Client::new(&error_path_handle)
                        .request(multipart_request_with_error(req, &error).unwrap())
                        .map(|_| ())
                });

        Box::new(work.map_err(|_| ()))
    }
}

#[cfg(test)]
mod tests {
    use hyper::Uri;
    use super::extract_file_name_from_uri;

    #[test]
    fn test_extract_file_name_from_uri_works() {
        let assert_extracted = |input: &'static str, expected_output: Option<&'static str>| {
            let uri = input.parse::<Uri>().unwrap();
            assert_eq!(extract_file_name_from_uri(&uri), expected_output.map(|o| o.to_string()));
        };

        assert_extracted("/logo.png", Some("logo.png"));
        assert_extracted("/assets/", None);
        assert_extracted("/assets/icon", Some("icon"));
        assert_extracted("/", None);
        assert_extracted("http://www.store2be.com", None);
    }
}
<|endoftext|>


<commit_before>use anyhow::*;
use image::DynamicImage;
use std::path::Path;

use crate::utils;
use crate::utils::AudioFormat;

pub fn read(image_path: &Path) -> Result<DynamicImage> {
	match utils::get_audio_format(image_path) {
		Some(AudioFormat::APE) => read_ape(image_path),
		Some(AudioFormat::FLAC) => read_flac(image_path),
		Some(AudioFormat::MP3) => read_id3(image_path),
		Some(AudioFormat::MP4) => read_mp4(image_path),
		Some(AudioFormat::MPC) => read_ape(image_path),
		Some(AudioFormat::OGG) => read_vorbis(image_path),
		Some(AudioFormat::OPUS) => read_opus(image_path),
		None => Ok(image::open(image_path)?),
	}
}

fn read_ape(_: &Path) -> Result<DynamicImage> {
	Err(crate::Error::msg("Embedded ape artworks not yet supported"))
}

fn read_flac(path: &Path) -> Result<DynamicImage> {
	let tag = metaflac::Tag::read_from_path(path)?;

	if let Some(p) = tag.pictures().next() {
		return Ok(image::load_from_memory(&p.data)?);
	}

	Err(crate::Error::msg(format!(
		"Embedded flac artwork not found for file: {}",
		path.display()
	)))
}

fn read_id3(path: &Path) -> Result<DynamicImage> {
	let tag = id3::Tag::read_from_path(path)?;

	if let Some(p) = tag.pictures().next() {
		return Ok(image::load_from_memory(&p.data)?);
	}

	Err(crate::Error::msg(format!(
		"Embedded id3 artwork not found for file: {}",
		path.display()
	)))
}

fn read_mp4(path: &Path) -> Result<DynamicImage> {
	let tag = mp4ameta::Tag::read_from_path(path)?;

	match tag.artwork().and_then(|d| d.image_data()) {
		Some(v) => Ok(image::load_from_memory(v)?),
		_ => Err(crate::Error::msg(format!(
			"Embedded mp4 artwork not found for file: {}",
			path.display()
		))),
	}
}

fn read_vorbis(_: &Path) -> Result<DynamicImage> {
	Err(crate::Error::msg(
		"Embedded vorbis artworks are not yet supported",
	))
}

fn read_opus(_: &Path) -> Result<DynamicImage> {
	Err(crate::Error::msg(
		"Embedded opus artworks are not yet supported",
	))
}

#[test]
fn test_read_artowork() {
	let ext_img = image::open("test-data/artwork/Folder.png")
		.unwrap()
		.to_rgb8();
	let embedded_img = image::open("test-data/artwork/Embedded.png")
		.unwrap()
		.to_rgb8();

	let folder_img = read(Path::new("test-data/artwork/Folder.png"))
		.unwrap()
		.to_rgb8();
	assert_eq!(folder_img, ext_img);

	let ape_img = read(Path::new("test-data/artwork/sample.ape"))
		.map(|d| d.to_rgb8())
		.ok();
	assert_eq!(ape_img, None);

	let flac_img = read(Path::new("test-data/artwork/sample.flac"))
		.unwrap()
		.to_rgb8();
	assert_eq!(flac_img, embedded_img);

	let mp3_img = read(Path::new("test-data/artwork/sample.mp3"))
		.unwrap()
		.to_rgb8();
	assert_eq!(mp3_img, embedded_img);

	let m4a_img = read(Path::new("test-data/artwork/sample.m4a"))
		.unwrap()
		.to_rgb8();
	assert_eq!(m4a_img, embedded_img);

	let ogg_img = read(Path::new("test-data/artwork/sample.ogg"))
		.map(|d| d.to_rgb8())
		.ok();
	assert_eq!(ogg_img, None);

	let opus_img = read(Path::new("test-data/artwork/sample.opus"))
		.map(|d| d.to_rgb8())
		.ok();
	assert_eq!(opus_img, None);
}
<commit_msg>Fixed typo<commit_after>use anyhow::*;
use image::DynamicImage;
use std::path::Path;

use crate::utils;
use crate::utils::AudioFormat;

pub fn read(image_path: &Path) -> Result<DynamicImage> {
	match utils::get_audio_format(image_path) {
		Some(AudioFormat::APE) => read_ape(image_path),
		Some(AudioFormat::FLAC) => read_flac(image_path),
		Some(AudioFormat::MP3) => read_id3(image_path),
		Some(AudioFormat::MP4) => read_mp4(image_path),
		Some(AudioFormat::MPC) => read_ape(image_path),
		Some(AudioFormat::OGG) => read_vorbis(image_path),
		Some(AudioFormat::OPUS) => read_opus(image_path),
		None => Ok(image::open(image_path)?),
	}
}

fn read_ape(_: &Path) -> Result<DynamicImage> {
	Err(crate::Error::msg("Embedded ape artworks not yet supported"))
}

fn read_flac(path: &Path) -> Result<DynamicImage> {
	let tag = metaflac::Tag::read_from_path(path)?;

	if let Some(p) = tag.pictures().next() {
		return Ok(image::load_from_memory(&p.data)?);
	}

	Err(crate::Error::msg(format!(
		"Embedded flac artwork not found for file: {}",
		path.display()
	)))
}

fn read_id3(path: &Path) -> Result<DynamicImage> {
	let tag = id3::Tag::read_from_path(path)?;

	if let Some(p) = tag.pictures().next() {
		return Ok(image::load_from_memory(&p.data)?);
	}

	Err(crate::Error::msg(format!(
		"Embedded id3 artwork not found for file: {}",
		path.display()
	)))
}

fn read_mp4(path: &Path) -> Result<DynamicImage> {
	let tag = mp4ameta::Tag::read_from_path(path)?;

	match tag.artwork().and_then(|d| d.image_data()) {
		Some(v) => Ok(image::load_from_memory(v)?),
		_ => Err(crate::Error::msg(format!(
			"Embedded mp4 artwork not found for file: {}",
			path.display()
		))),
	}
}

fn read_vorbis(_: &Path) -> Result<DynamicImage> {
	Err(crate::Error::msg(
		"Embedded vorbis artworks are not yet supported",
	))
}

fn read_opus(_: &Path) -> Result<DynamicImage> {
	Err(crate::Error::msg(
		"Embedded opus artworks are not yet supported",
	))
}

#[test]
fn test_read_artwork() {
	let ext_img = image::open("test-data/artwork/Folder.png")
		.unwrap()
		.to_rgb8();
	let embedded_img = image::open("test-data/artwork/Embedded.png")
		.unwrap()
		.to_rgb8();

	let folder_img = read(Path::new("test-data/artwork/Folder.png"))
		.unwrap()
		.to_rgb8();
	assert_eq!(folder_img, ext_img);

	let ape_img = read(Path::new("test-data/artwork/sample.ape"))
		.map(|d| d.to_rgb8())
		.ok();
	assert_eq!(ape_img, None);

	let flac_img = read(Path::new("test-data/artwork/sample.flac"))
		.unwrap()
		.to_rgb8();
	assert_eq!(flac_img, embedded_img);

	let mp3_img = read(Path::new("test-data/artwork/sample.mp3"))
		.unwrap()
		.to_rgb8();
	assert_eq!(mp3_img, embedded_img);

	let m4a_img = read(Path::new("test-data/artwork/sample.m4a"))
		.unwrap()
		.to_rgb8();
	assert_eq!(m4a_img, embedded_img);

	let ogg_img = read(Path::new("test-data/artwork/sample.ogg"))
		.map(|d| d.to_rgb8())
		.ok();
	assert_eq!(ogg_img, None);

	let opus_img = read(Path::new("test-data/artwork/sample.opus"))
		.map(|d| d.to_rgb8())
		.ok();
	assert_eq!(opus_img, None);
}
<|endoftext|>


<commit_before>/*
 *
 * Computer-graphics specific implementations.
 * Currently, it is mostly implemented for homogeneous matrices in 2- and 3-space.
 *
 */

use num::One;

use base::allocator::Allocator;
use base::dimension::{DimName, DimNameDiff, DimNameSub, U1};
use base::storage::{Storage, StorageMut};
use base::{
    DefaultAllocator, Matrix3, Matrix4, MatrixN, Scalar, SquareMatrix, Unit, Vector, Vector3,
    VectorN,
};
use geometry::{
    Isometry, IsometryMatrix3, Orthographic3, Perspective3, Point, Point3, Rotation2, Rotation3,
};

use alga::general::{Real, Ring};
use alga::linear::Transformation;

impl<N, D: DimName> MatrixN<N, D>
where
    N: Scalar + Ring,
    DefaultAllocator: Allocator<N, D, D>,
{
    /// Creates a new homogeneous matrix that applies the same scaling factor on each dimension.
    #[inline]
    pub fn new_scaling(scaling: N) -> Self {
        let mut res = Self::from_diagonal_element(scaling);
        res[(D::dim() - 1, D::dim() - 1)] = N::one();

        res
    }

    /// Creates a new homogeneous matrix that applies a distinct scaling factor for each dimension.
    #[inline]
    pub fn new_nonuniform_scaling<SB>(scaling: &Vector<N, DimNameDiff<D, U1>, SB>) -> Self
    where
        D: DimNameSub<U1>,
        SB: Storage<N, DimNameDiff<D, U1>>,
    {
        let mut res = Self::one();
        for i in 0..scaling.len() {
            res[(i, i)] = scaling[i];
        }

        res
    }

    /// Creates a new homogeneous matrix that applies a pure translation.
    #[inline]
    pub fn new_translation<SB>(translation: &Vector<N, DimNameDiff<D, U1>, SB>) -> Self
    where
        D: DimNameSub<U1>,
        SB: Storage<N, DimNameDiff<D, U1>>,
    {
        let mut res = Self::one();
        res.fixed_slice_mut::<DimNameDiff<D, U1>, U1>(0, D::dim() - 1)
            .copy_from(translation);

        res
    }
}

impl<N: Real> Matrix3<N> {
    /// Builds a 2 dimensional homogeneous rotation matrix from an angle in radian.
    #[inline]
    pub fn new_rotation(angle: N) -> Self {
        Rotation2::new(angle).to_homogeneous()
    }
}

impl<N: Real> Matrix4<N> {
    /// Builds a 3D homogeneous rotation matrix from an axis and an angle (multiplied together).
    ///
    /// Returns the identity matrix if the given argument is zero.
    #[inline]
    pub fn new_rotation(axisangle: Vector3<N>) -> Self {
        Rotation3::new(axisangle).to_homogeneous()
    }

    /// Builds a 3D homogeneous rotation matrix from an axis and an angle (multiplied together).
    ///
    /// Returns the identity matrix if the given argument is zero.
    #[inline]
    pub fn new_rotation_wrt_point(axisangle: Vector3<N>, pt: Point3<N>) -> Self {
        let rot = Rotation3::from_scaled_axis(axisangle);
        Isometry::rotation_wrt_point(rot, pt).to_homogeneous()
    }

    /// Builds a 3D homogeneous rotation matrix from an axis and an angle (multiplied together).
    ///
    /// Returns the identity matrix if the given argument is zero.
    /// This is identical to `Self::new_rotation`.
    #[inline]
    pub fn from_scaled_axis(axisangle: Vector3<N>) -> Self {
        Rotation3::from_scaled_axis(axisangle).to_homogeneous()
    }

    /// Creates a new rotation from Euler angles.
    ///
    /// The primitive rotations are applied in order: 1 roll − 2 pitch − 3 yaw.
    pub fn from_euler_angles(roll: N, pitch: N, yaw: N) -> Self {
        Rotation3::from_euler_angles(roll, pitch, yaw).to_homogeneous()
    }

    /// Builds a 3D homogeneous rotation matrix from an axis and a rotation angle.
    pub fn from_axis_angle(axis: &Unit<Vector3<N>>, angle: N) -> Self {
        Rotation3::from_axis_angle(axis, angle).to_homogeneous()
    }

    /// Creates a new homogeneous matrix for an orthographic projection.
    #[inline]
    pub fn new_orthographic(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> Self {
        Orthographic3::new(left, right, bottom, top, znear, zfar).unwrap()
    }

    /// Creates a new homogeneous matrix for a perspective projection.
    #[inline]
    pub fn new_perspective(aspect: N, fovy: N, znear: N, zfar: N) -> Self {
        Perspective3::new(aspect, fovy, znear, zfar).unwrap()
    }

    /// Creates an isometry that corresponds to the local frame of an observer standing at the
    /// point `eye` and looking toward `target`.
    ///
    /// It maps the view direction `target - eye` to the positive `z` axis and the origin to the
    /// `eye`.
    #[inline]
    pub fn new_observer_frame(eye: &Point3<N>, target: &Point3<N>, up: &Vector3<N>) -> Self {
        IsometryMatrix3::new_observer_frame(eye, target, up).to_homogeneous()
    }

    /// Builds a right-handed look-at view matrix.
    #[inline]
    pub fn look_at_rh(eye: &Point3<N>, target: &Point3<N>, up: &Vector3<N>) -> Self {
        IsometryMatrix3::look_at_rh(eye, target, up).to_homogeneous()
    }

    /// Builds a left-handed look-at view matrix.
    #[inline]
    pub fn look_at_lh(eye: &Point3<N>, target: &Point3<N>, up: &Vector3<N>) -> Self {
        IsometryMatrix3::look_at_lh(eye, target, up).to_homogeneous()
    }
}

impl<N: Scalar + Ring, D: DimName, S: Storage<N, D, D>> SquareMatrix<N, D, S> {
    /// Computes the transformation equal to `self` followed by an uniform scaling factor.
    #[inline]
    pub fn append_scaling(&self, scaling: N) -> MatrixN<N, D>
    where
        D: DimNameSub<U1>,
        DefaultAllocator: Allocator<N, D, D>,
    {
        let mut res = self.clone_owned();
        res.append_scaling_mut(scaling);
        res
    }

    /// Computes the transformation equal to an uniform scaling factor followed by `self`.
    #[inline]
    pub fn prepend_scaling(&self, scaling: N) -> MatrixN<N, D>
    where
        D: DimNameSub<U1>,
        DefaultAllocator: Allocator<N, D, D>,
    {
        let mut res = self.clone_owned();
        res.prepend_scaling_mut(scaling);
        res
    }

    /// Computes the transformation equal to `self` followed by a non-uniform scaling factor.
    #[inline]
    pub fn append_nonuniform_scaling<SB>(
        &self,
        scaling: &Vector<N, DimNameDiff<D, U1>, SB>,
    ) -> MatrixN<N, D>
    where
        D: DimNameSub<U1>,
        SB: Storage<N, DimNameDiff<D, U1>>,
        DefaultAllocator: Allocator<N, D, D>,
    {
        let mut res = self.clone_owned();
        res.append_nonuniform_scaling_mut(scaling);
        res
    }

    /// Computes the transformation equal to a non-uniform scaling factor followed by `self`.
    #[inline]
    pub fn prepend_nonuniform_scaling<SB>(
        &self,
        scaling: &Vector<N, DimNameDiff<D, U1>, SB>,
    ) -> MatrixN<N, D>
    where
        D: DimNameSub<U1>,
        SB: Storage<N, DimNameDiff<D, U1>>,
        DefaultAllocator: Allocator<N, D, D>,
    {
        let mut res = self.clone_owned();
        res.prepend_nonuniform_scaling_mut(scaling);
        res
    }

    /// Computes the transformation equal to `self` followed by a translation.
    #[inline]
    pub fn append_translation<SB>(&self, shift: &Vector<N, DimNameDiff<D, U1>, SB>) -> MatrixN<N, D>
    where
        D: DimNameSub<U1>,
        SB: Storage<N, DimNameDiff<D, U1>>,
        DefaultAllocator: Allocator<N, D, D>,
    {
        let mut res = self.clone_owned();
        res.append_translation_mut(shift);
        res
    }

    /// Computes the transformation equal to a translation followed by `self`.
    #[inline]
    pub fn prepend_translation<SB>(
        &self,
        shift: &Vector<N, DimNameDiff<D, U1>, SB>,
    ) -> MatrixN<N, D>
    where
        D: DimNameSub<U1>,
        SB: Storage<N, DimNameDiff<D, U1>>,
        DefaultAllocator: Allocator<N, D, D> + Allocator<N, DimNameDiff<D, U1>>,
    {
        let mut res = self.clone_owned();
        res.prepend_translation_mut(shift);
        res
    }
}

impl<N: Scalar + Ring, D: DimName, S: StorageMut<N, D, D>> SquareMatrix<N, D, S> {
    /// Computes in-place the transformation equal to `self` followed by an uniform scaling factor.
    #[inline]
    pub fn append_scaling_mut(&mut self, scaling: N)
    where D: DimNameSub<U1> {
        let mut to_scale = self.fixed_rows_mut::<DimNameDiff<D, U1>>(0);
        to_scale *= scaling;
    }

    /// Computes in-place the transformation equal to an uniform scaling factor followed by `self`.
    #[inline]
    pub fn prepend_scaling_mut(&mut self, scaling: N)
    where D: DimNameSub<U1> {
        let mut to_scale = self.fixed_columns_mut::<DimNameDiff<D, U1>>(0);
        to_scale *= scaling;
    }

    /// Computes in-place the transformation equal to `self` followed by a non-uniform scaling factor.
    #[inline]
    pub fn append_nonuniform_scaling_mut<SB>(&mut self, scaling: &Vector<N, DimNameDiff<D, U1>, SB>)
    where
        D: DimNameSub<U1>,
        SB: Storage<N, DimNameDiff<D, U1>>,
    {
        for i in 0..scaling.len() {
            let mut to_scale = self.fixed_rows_mut::<U1>(i);
            to_scale *= scaling[i];
        }
    }

    /// Computes in-place the transformation equal to a non-uniform scaling factor followed by `self`.
    #[inline]
    pub fn prepend_nonuniform_scaling_mut<SB>(
        &mut self,
        scaling: &Vector<N, DimNameDiff<D, U1>, SB>,
    ) where
        D: DimNameSub<U1>,
        SB: Storage<N, DimNameDiff<D, U1>>,
    {
        for i in 0..scaling.len() {
            let mut to_scale = self.fixed_columns_mut::<U1>(i);
            to_scale *= scaling[i];
        }
    }

    /// Computes the transformation equal to `self` followed by a translation.
    #[inline]
    pub fn append_translation_mut<SB>(&mut self, shift: &Vector<N, DimNameDiff<D, U1>, SB>)
    where
        D: DimNameSub<U1>,
        SB: Storage<N, DimNameDiff<D, U1>>,
    {
        for i in 0..D::dim() {
            for j in 0..D::dim() - 1 {
                self[(j, i)] += shift[j] * self[(D::dim() - 1, i)];
            }
        }
    }

    /// Computes the transformation equal to a translation followed by `self`.
    #[inline]
    pub fn prepend_translation_mut<SB>(&mut self, shift: &Vector<N, DimNameDiff<D, U1>, SB>)
    where
        D: DimNameSub<U1>,
        SB: Storage<N, DimNameDiff<D, U1>>,
        DefaultAllocator: Allocator<N, DimNameDiff<D, U1>>,
    {
        let scale = self
            .fixed_slice::<U1, DimNameDiff<D, U1>>(D::dim() - 1, 0)
            .tr_dot(&shift);
        let post_translation =
            self.fixed_slice::<DimNameDiff<D, U1>, DimNameDiff<D, U1>>(0, 0) * shift;

        self[(D::dim() - 1, D::dim() - 1)] += scale;

        let mut translation = self.fixed_slice_mut::<DimNameDiff<D, U1>, U1>(0, D::dim() - 1);
        translation += post_translation;
    }
}

impl<N: Real, D: DimNameSub<U1>, S: Storage<N, D, D>> SquareMatrix<N, D, S>
where DefaultAllocator: Allocator<N, D, D>
        + Allocator<N, DimNameDiff<D, U1>>
        + Allocator<N, DimNameDiff<D, U1>, DimNameDiff<D, U1>>
{
    /// Transforms the given vector, assuming the matirx `self` uses homogeneous coordinates.
    #[inline]
    fn transform_vector(
        &self,
        v: &VectorN<N, DimNameDiff<D, U1>>,
    ) -> VectorN<N, DimNameDiff<D, U1>>
    {
        let transform = self.fixed_slice::<DimNameDiff<D, U1>, DimNameDiff<D, U1>>(0, 0);
        let normalizer = self.fixed_slice::<U1, DimNameDiff<D, U1>>(D::dim() - 1, 0);
        let n = normalizer.tr_dot(&v);

        if !n.is_zero() {
            return transform * (v / n);
        }

        transform * v
    }

    /// Transforms the given point, assuming the matrix `self` uses homogeneous coordinates.
    #[inline]
    fn transform_point(&self, pt: &Point<N, DimNameDiff<D, U1>>) -> Point<N, DimNameDiff<D, U1>> {
        let transform = self.fixed_slice::<DimNameDiff<D, U1>, DimNameDiff<D, U1>>(0, 0);
        let translation = self.fixed_slice::<DimNameDiff<D, U1>, U1>(0, D::dim() - 1);
        let normalizer = self.fixed_slice::<U1, DimNameDiff<D, U1>>(D::dim() - 1, 0);
        let n = normalizer.tr_dot(&pt.coords)
            + unsafe { *self.get_unchecked(D::dim() - 1, D::dim() - 1) };

        if !n.is_zero() {
            return transform * (pt / n) + translation;
        }

        transform * pt + translation
    }
}

impl<N: Real, D: DimNameSub<U1>> Transformation<Point<N, DimNameDiff<D, U1>>> for MatrixN<N, D>
where DefaultAllocator: Allocator<N, D, D>
        + Allocator<N, DimNameDiff<D, U1>>
        + Allocator<N, DimNameDiff<D, U1>, DimNameDiff<D, U1>>
{
    #[inline]
    fn transform_vector(
        &self,
        v: &VectorN<N, DimNameDiff<D, U1>>,
    ) -> VectorN<N, DimNameDiff<D, U1>>
    {
        self.transform_vector(v)
    }

    #[inline]
    fn transform_point(&self, pt: &Point<N, DimNameDiff<D, U1>>) -> Point<N, DimNameDiff<D, U1>> {
        self.transform_point(pt)
    }
}
<commit_msg>Fix typo.<commit_after>/*
 *
 * Computer-graphics specific implementations.
 * Currently, it is mostly implemented for homogeneous matrices in 2- and 3-space.
 *
 */

use num::One;

use base::allocator::Allocator;
use base::dimension::{DimName, DimNameDiff, DimNameSub, U1};
use base::storage::{Storage, StorageMut};
use base::{
    DefaultAllocator, Matrix3, Matrix4, MatrixN, Scalar, SquareMatrix, Unit, Vector, Vector3,
    VectorN,
};
use geometry::{
    Isometry, IsometryMatrix3, Orthographic3, Perspective3, Point, Point3, Rotation2, Rotation3,
};

use alga::general::{Real, Ring};
use alga::linear::Transformation;

impl<N, D: DimName> MatrixN<N, D>
where
    N: Scalar + Ring,
    DefaultAllocator: Allocator<N, D, D>,
{
    /// Creates a new homogeneous matrix that applies the same scaling factor on each dimension.
    #[inline]
    pub fn new_scaling(scaling: N) -> Self {
        let mut res = Self::from_diagonal_element(scaling);
        res[(D::dim() - 1, D::dim() - 1)] = N::one();

        res
    }

    /// Creates a new homogeneous matrix that applies a distinct scaling factor for each dimension.
    #[inline]
    pub fn new_nonuniform_scaling<SB>(scaling: &Vector<N, DimNameDiff<D, U1>, SB>) -> Self
    where
        D: DimNameSub<U1>,
        SB: Storage<N, DimNameDiff<D, U1>>,
    {
        let mut res = Self::one();
        for i in 0..scaling.len() {
            res[(i, i)] = scaling[i];
        }

        res
    }

    /// Creates a new homogeneous matrix that applies a pure translation.
    #[inline]
    pub fn new_translation<SB>(translation: &Vector<N, DimNameDiff<D, U1>, SB>) -> Self
    where
        D: DimNameSub<U1>,
        SB: Storage<N, DimNameDiff<D, U1>>,
    {
        let mut res = Self::one();
        res.fixed_slice_mut::<DimNameDiff<D, U1>, U1>(0, D::dim() - 1)
            .copy_from(translation);

        res
    }
}

impl<N: Real> Matrix3<N> {
    /// Builds a 2 dimensional homogeneous rotation matrix from an angle in radian.
    #[inline]
    pub fn new_rotation(angle: N) -> Self {
        Rotation2::new(angle).to_homogeneous()
    }
}

impl<N: Real> Matrix4<N> {
    /// Builds a 3D homogeneous rotation matrix from an axis and an angle (multiplied together).
    ///
    /// Returns the identity matrix if the given argument is zero.
    #[inline]
    pub fn new_rotation(axisangle: Vector3<N>) -> Self {
        Rotation3::new(axisangle).to_homogeneous()
    }

    /// Builds a 3D homogeneous rotation matrix from an axis and an angle (multiplied together).
    ///
    /// Returns the identity matrix if the given argument is zero.
    #[inline]
    pub fn new_rotation_wrt_point(axisangle: Vector3<N>, pt: Point3<N>) -> Self {
        let rot = Rotation3::from_scaled_axis(axisangle);
        Isometry::rotation_wrt_point(rot, pt).to_homogeneous()
    }

    /// Builds a 3D homogeneous rotation matrix from an axis and an angle (multiplied together).
    ///
    /// Returns the identity matrix if the given argument is zero.
    /// This is identical to `Self::new_rotation`.
    #[inline]
    pub fn from_scaled_axis(axisangle: Vector3<N>) -> Self {
        Rotation3::from_scaled_axis(axisangle).to_homogeneous()
    }

    /// Creates a new rotation from Euler angles.
    ///
    /// The primitive rotations are applied in order: 1 roll − 2 pitch − 3 yaw.
    pub fn from_euler_angles(roll: N, pitch: N, yaw: N) -> Self {
        Rotation3::from_euler_angles(roll, pitch, yaw).to_homogeneous()
    }

    /// Builds a 3D homogeneous rotation matrix from an axis and a rotation angle.
    pub fn from_axis_angle(axis: &Unit<Vector3<N>>, angle: N) -> Self {
        Rotation3::from_axis_angle(axis, angle).to_homogeneous()
    }

    /// Creates a new homogeneous matrix for an orthographic projection.
    #[inline]
    pub fn new_orthographic(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> Self {
        Orthographic3::new(left, right, bottom, top, znear, zfar).unwrap()
    }

    /// Creates a new homogeneous matrix for a perspective projection.
    #[inline]
    pub fn new_perspective(aspect: N, fovy: N, znear: N, zfar: N) -> Self {
        Perspective3::new(aspect, fovy, znear, zfar).unwrap()
    }

    /// Creates an isometry that corresponds to the local frame of an observer standing at the
    /// point `eye` and looking toward `target`.
    ///
    /// It maps the view direction `target - eye` to the positive `z` axis and the origin to the
    /// `eye`.
    #[inline]
    pub fn new_observer_frame(eye: &Point3<N>, target: &Point3<N>, up: &Vector3<N>) -> Self {
        IsometryMatrix3::new_observer_frame(eye, target, up).to_homogeneous()
    }

    /// Builds a right-handed look-at view matrix.
    #[inline]
    pub fn look_at_rh(eye: &Point3<N>, target: &Point3<N>, up: &Vector3<N>) -> Self {
        IsometryMatrix3::look_at_rh(eye, target, up).to_homogeneous()
    }

    /// Builds a left-handed look-at view matrix.
    #[inline]
    pub fn look_at_lh(eye: &Point3<N>, target: &Point3<N>, up: &Vector3<N>) -> Self {
        IsometryMatrix3::look_at_lh(eye, target, up).to_homogeneous()
    }
}

impl<N: Scalar + Ring, D: DimName, S: Storage<N, D, D>> SquareMatrix<N, D, S> {
    /// Computes the transformation equal to `self` followed by an uniform scaling factor.
    #[inline]
    pub fn append_scaling(&self, scaling: N) -> MatrixN<N, D>
    where
        D: DimNameSub<U1>,
        DefaultAllocator: Allocator<N, D, D>,
    {
        let mut res = self.clone_owned();
        res.append_scaling_mut(scaling);
        res
    }

    /// Computes the transformation equal to an uniform scaling factor followed by `self`.
    #[inline]
    pub fn prepend_scaling(&self, scaling: N) -> MatrixN<N, D>
    where
        D: DimNameSub<U1>,
        DefaultAllocator: Allocator<N, D, D>,
    {
        let mut res = self.clone_owned();
        res.prepend_scaling_mut(scaling);
        res
    }

    /// Computes the transformation equal to `self` followed by a non-uniform scaling factor.
    #[inline]
    pub fn append_nonuniform_scaling<SB>(
        &self,
        scaling: &Vector<N, DimNameDiff<D, U1>, SB>,
    ) -> MatrixN<N, D>
    where
        D: DimNameSub<U1>,
        SB: Storage<N, DimNameDiff<D, U1>>,
        DefaultAllocator: Allocator<N, D, D>,
    {
        let mut res = self.clone_owned();
        res.append_nonuniform_scaling_mut(scaling);
        res
    }

    /// Computes the transformation equal to a non-uniform scaling factor followed by `self`.
    #[inline]
    pub fn prepend_nonuniform_scaling<SB>(
        &self,
        scaling: &Vector<N, DimNameDiff<D, U1>, SB>,
    ) -> MatrixN<N, D>
    where
        D: DimNameSub<U1>,
        SB: Storage<N, DimNameDiff<D, U1>>,
        DefaultAllocator: Allocator<N, D, D>,
    {
        let mut res = self.clone_owned();
        res.prepend_nonuniform_scaling_mut(scaling);
        res
    }

    /// Computes the transformation equal to `self` followed by a translation.
    #[inline]
    pub fn append_translation<SB>(&self, shift: &Vector<N, DimNameDiff<D, U1>, SB>) -> MatrixN<N, D>
    where
        D: DimNameSub<U1>,
        SB: Storage<N, DimNameDiff<D, U1>>,
        DefaultAllocator: Allocator<N, D, D>,
    {
        let mut res = self.clone_owned();
        res.append_translation_mut(shift);
        res
    }

    /// Computes the transformation equal to a translation followed by `self`.
    #[inline]
    pub fn prepend_translation<SB>(
        &self,
        shift: &Vector<N, DimNameDiff<D, U1>, SB>,
    ) -> MatrixN<N, D>
    where
        D: DimNameSub<U1>,
        SB: Storage<N, DimNameDiff<D, U1>>,
        DefaultAllocator: Allocator<N, D, D> + Allocator<N, DimNameDiff<D, U1>>,
    {
        let mut res = self.clone_owned();
        res.prepend_translation_mut(shift);
        res
    }
}

impl<N: Scalar + Ring, D: DimName, S: StorageMut<N, D, D>> SquareMatrix<N, D, S> {
    /// Computes in-place the transformation equal to `self` followed by an uniform scaling factor.
    #[inline]
    pub fn append_scaling_mut(&mut self, scaling: N)
    where D: DimNameSub<U1> {
        let mut to_scale = self.fixed_rows_mut::<DimNameDiff<D, U1>>(0);
        to_scale *= scaling;
    }

    /// Computes in-place the transformation equal to an uniform scaling factor followed by `self`.
    #[inline]
    pub fn prepend_scaling_mut(&mut self, scaling: N)
    where D: DimNameSub<U1> {
        let mut to_scale = self.fixed_columns_mut::<DimNameDiff<D, U1>>(0);
        to_scale *= scaling;
    }

    /// Computes in-place the transformation equal to `self` followed by a non-uniform scaling factor.
    #[inline]
    pub fn append_nonuniform_scaling_mut<SB>(&mut self, scaling: &Vector<N, DimNameDiff<D, U1>, SB>)
    where
        D: DimNameSub<U1>,
        SB: Storage<N, DimNameDiff<D, U1>>,
    {
        for i in 0..scaling.len() {
            let mut to_scale = self.fixed_rows_mut::<U1>(i);
            to_scale *= scaling[i];
        }
    }

    /// Computes in-place the transformation equal to a non-uniform scaling factor followed by `self`.
    #[inline]
    pub fn prepend_nonuniform_scaling_mut<SB>(
        &mut self,
        scaling: &Vector<N, DimNameDiff<D, U1>, SB>,
    ) where
        D: DimNameSub<U1>,
        SB: Storage<N, DimNameDiff<D, U1>>,
    {
        for i in 0..scaling.len() {
            let mut to_scale = self.fixed_columns_mut::<U1>(i);
            to_scale *= scaling[i];
        }
    }

    /// Computes the transformation equal to `self` followed by a translation.
    #[inline]
    pub fn append_translation_mut<SB>(&mut self, shift: &Vector<N, DimNameDiff<D, U1>, SB>)
    where
        D: DimNameSub<U1>,
        SB: Storage<N, DimNameDiff<D, U1>>,
    {
        for i in 0..D::dim() {
            for j in 0..D::dim() - 1 {
                self[(j, i)] += shift[j] * self[(D::dim() - 1, i)];
            }
        }
    }

    /// Computes the transformation equal to a translation followed by `self`.
    #[inline]
    pub fn prepend_translation_mut<SB>(&mut self, shift: &Vector<N, DimNameDiff<D, U1>, SB>)
    where
        D: DimNameSub<U1>,
        SB: Storage<N, DimNameDiff<D, U1>>,
        DefaultAllocator: Allocator<N, DimNameDiff<D, U1>>,
    {
        let scale = self
            .fixed_slice::<U1, DimNameDiff<D, U1>>(D::dim() - 1, 0)
            .tr_dot(&shift);
        let post_translation =
            self.fixed_slice::<DimNameDiff<D, U1>, DimNameDiff<D, U1>>(0, 0) * shift;

        self[(D::dim() - 1, D::dim() - 1)] += scale;

        let mut translation = self.fixed_slice_mut::<DimNameDiff<D, U1>, U1>(0, D::dim() - 1);
        translation += post_translation;
    }
}

impl<N: Real, D: DimNameSub<U1>, S: Storage<N, D, D>> SquareMatrix<N, D, S>
where DefaultAllocator: Allocator<N, D, D>
        + Allocator<N, DimNameDiff<D, U1>>
        + Allocator<N, DimNameDiff<D, U1>, DimNameDiff<D, U1>>
{
    /// Transforms the given vector, assuming the matrix `self` uses homogeneous coordinates.
    #[inline]
    fn transform_vector(
        &self,
        v: &VectorN<N, DimNameDiff<D, U1>>,
    ) -> VectorN<N, DimNameDiff<D, U1>>
    {
        let transform = self.fixed_slice::<DimNameDiff<D, U1>, DimNameDiff<D, U1>>(0, 0);
        let normalizer = self.fixed_slice::<U1, DimNameDiff<D, U1>>(D::dim() - 1, 0);
        let n = normalizer.tr_dot(&v);

        if !n.is_zero() {
            return transform * (v / n);
        }

        transform * v
    }

    /// Transforms the given point, assuming the matrix `self` uses homogeneous coordinates.
    #[inline]
    fn transform_point(&self, pt: &Point<N, DimNameDiff<D, U1>>) -> Point<N, DimNameDiff<D, U1>> {
        let transform = self.fixed_slice::<DimNameDiff<D, U1>, DimNameDiff<D, U1>>(0, 0);
        let translation = self.fixed_slice::<DimNameDiff<D, U1>, U1>(0, D::dim() - 1);
        let normalizer = self.fixed_slice::<U1, DimNameDiff<D, U1>>(D::dim() - 1, 0);
        let n = normalizer.tr_dot(&pt.coords)
            + unsafe { *self.get_unchecked(D::dim() - 1, D::dim() - 1) };

        if !n.is_zero() {
            return transform * (pt / n) + translation;
        }

        transform * pt + translation
    }
}

impl<N: Real, D: DimNameSub<U1>> Transformation<Point<N, DimNameDiff<D, U1>>> for MatrixN<N, D>
where DefaultAllocator: Allocator<N, D, D>
        + Allocator<N, DimNameDiff<D, U1>>
        + Allocator<N, DimNameDiff<D, U1>, DimNameDiff<D, U1>>
{
    #[inline]
    fn transform_vector(
        &self,
        v: &VectorN<N, DimNameDiff<D, U1>>,
    ) -> VectorN<N, DimNameDiff<D, U1>>
    {
        self.transform_vector(v)
    }

    #[inline]
    fn transform_point(&self, pt: &Point<N, DimNameDiff<D, U1>>) -> Point<N, DimNameDiff<D, U1>> {
        self.transform_point(pt)
    }
}
<|endoftext|>


<commit_before>#![deny(warnings)]

extern crate extra;

use std::cell::Cell; // Provide mutable fields in immutable structs
use std::env;
use std::error::Error;
use std::fs;
use std::io::{self, BufReader, Read, Stderr, StdoutLock, Write};
use std::process::exit;
use extra::option::OptionalExt;

const MAN_PAGE: &'static str = /* @MANSTART{cat} */ r#"NAME
    cat - concatenate files and print on the standard output

SYNOPSIS
    cat [-h | --help] [-A | --show-all] [-b | --number-nonblank] [-e] [-E | --show-ends]
        [-n | --number] [-s | --squeeze-blank] [-t] [-T] FILES...

DESCRIPTION
    Concatenates all files to the standard output.

    If no file is given, or if FILE is '-', read from standard input.

OPTIONS
    -A
    --show-all
        equivalent to -vET

    -b
    --number-nonblank
        number nonempty output lines, overriding -n

    -e
        equivalent to -vE

    -E
    --show-ends
        display $ at the end of each line

    -n
    --number
        number all output lines

    -s
    --squeeze-blank
        supress repeated empty output lines

    -t
        equivalent to -vT

    -T
    --show_tabs
        display TAB characters as ^I

    -v
    --show-nonprinting
        use caret (^) and M- notation, except for LFD and TAB.

    -h
    --help
        display this help and exit

AUTHOR
    Written by Michael Murphy.
"#; /* @MANEND */

struct Program {
    exit_status:      Cell<i32>,
    number:           bool,
    number_nonblank:  bool,
    show_ends:        bool,
    show_tabs:        bool,
    show_nonprinting: bool,
    squeeze_blank:    bool,
    paths:            Vec<String>,
}

impl Program {
    /// Initialize the program's arguments and flags.
    fn initialize(stdout: &mut StdoutLock, stderr: &mut Stderr) -> Program {
        let mut cat = Program {
            exit_status:      Cell::new(0i32),
            number:           false,
            number_nonblank:  false,
            show_ends:        false,
            show_tabs:        false,
            show_nonprinting: false,
            squeeze_blank:    false,
            paths:            Vec::new()
        };
        for argument in env::args().skip(1) {
            if argument.starts_with('-') && &argument != "-" {
                if argument.starts_with("--") {
                    match argument.as_str() {
                        "--help" => {
                            stdout.write(MAN_PAGE.as_bytes()).try(stderr);
                            stdout.flush().try(stderr);
                            exit(0);
                        }
                        "--show-all" => {
                            cat.show_nonprinting = true;
                            cat.show_ends = true;
                            cat.show_tabs = true;
                        },
                        "--number-nonblank" => {
                            cat.number_nonblank = true;
                            cat.number = false;
                        },
                        "--show-ends" => cat.show_ends = true,
                        "--number" => {
                            cat.number = true;
                            cat.number_nonblank = false;
                        },
                        "--squeeze-blank" => cat.squeeze_blank = true,
                        "--show-tabs" => cat.show_tabs = true,
                        "--show-nonprinting" => {
                            cat.show_nonprinting = true;
                        },
                        _ => {
                            stderr.write(b"invalid option -- '").try(stderr);
                            stderr.write(argument.as_bytes()).try(stderr);
                            stderr.write(b"'\nTry 'cat --help' for more information.\n").try(stderr);
                            stderr.flush().try(stderr);
                            exit(1);
                        }
                    }
                } else {
                    for character in argument.bytes().skip(1) {
                        match character {
                            b'h' => {
                                stdout.write(MAN_PAGE.as_bytes()).try(stderr);
                                stdout.flush().try(stderr);
                                exit(0);
                            }
                            b'A' => {
                                cat.show_nonprinting = true;
                                cat.show_ends = true;
                                cat.show_tabs = true;
                            },
                            b'b'=> {
                                cat.number_nonblank = true;
                                cat.number = false;
                            },
                            b'e' => {
                                cat.show_nonprinting = true;
                                cat.show_ends = true;
                            },
                            b'E'=> cat.show_ends = true,
                            b'n' => {
                                cat.number = true;
                                cat.number_nonblank = false;
                            },
                            b's' => cat.squeeze_blank = true,
                            b't' => {
                                cat.show_nonprinting = true;
                                cat.show_tabs = true;
                            },
                            b'T' => cat.show_tabs = true,
                            b'v' => {
                                cat.show_nonprinting = true;
                            },
                            _ => {
                                stderr.write(b"invalid option -- '").try(stderr);
                                stderr.write(&[character]).try(stderr);
                                stderr.write(b"'\nTry 'cat --help' for more information.\n").try(stderr);
                                stderr.flush().try(stderr);
                                exit(1);
                            }
                        }
                    }
                }
            } else {
                cat.paths.push(argument);
            }
        }
        cat
    }

    /// Execute the parameters given to the program.
    fn and_execute(&self, stdout: &mut StdoutLock, stderr: &mut Stderr) -> i32 {
        let stdin = io::stdin();
        let line_count = &mut 0usize;
        let flags_enabled = self.number || self.number_nonblank || self.show_ends || self.show_tabs ||
                            self.squeeze_blank || self.show_nonprinting;

        if self.paths.is_empty() && flags_enabled {
            self.cat(&mut stdin.lock(), line_count, stdout, stderr);
        } else if self.paths.is_empty() {
            io::copy(&mut stdin.lock(), stdout).try(stderr);
        } else {
            for path in &self.paths {
                if flags_enabled && path == "-" {
                    self.cat(&mut stdin.lock(), line_count, stdout, stderr);
                } else if flags_enabled {
                    fs::File::open(&path)
                        // Open the file and copy the file's contents to standard output based input arguments.
                        .map(|file| self.cat(BufReader::new(file), line_count, stdout, stderr))
                        // If an error occurred, print the error and set the exit status.
                        .unwrap_or_else(|message| {
                            stderr.write(path.as_bytes()).try(stderr);
                            stderr.write(b": ").try(stderr);
                            stderr.write(message.description().as_bytes()).try(stderr);
                            stderr.write(b"\n").try(stderr);
                            stderr.flush().try(stderr);
                            self.exit_status.set(1i32);
                        });
                } else if path == "-" {
                    // Copy the standard input directly to the standard output.
                    io::copy(&mut stdin.lock(), stdout).try(stderr);
                } else {
                    // Open a file and copy the contents directly to standard output.
                    fs::File::open(&path).map(|ref mut file| { io::copy(file, stdout).try(stderr); })
                        // If an error occurs, print the error and set the exit status.
                        .unwrap_or_else(|message| {
                            stderr.write(path.as_bytes()).try(stderr);
                            stderr.write(b": ").try(stderr);
                            stderr.write(message.description().as_bytes()).try(stderr);
                            stderr.write(b"\n").try(stderr);
                            stderr.flush().try(stderr);
                            self.exit_status.set(1i32);
                        });
                }
            }
        }
        self.exit_status.get()
    }

    /// Cats either a file or stdin based on the flag arguments given to the program.
    fn cat<F: Read>(&self, file: F, line_count: &mut usize, stdout: &mut StdoutLock, stderr: &mut Stderr) {
        let mut character_count = 0;
        let mut last_line_was_blank = false;

        for byte in file.bytes().map(|x| x.unwrap()) {
            if (self.number && character_count == 0) || (character_count == 0 && self.number_nonblank && byte != b'\n') {
                stdout.write(b"     ").try(stderr);
                stdout.write(line_count.to_string().as_bytes()).try(stderr);
                stdout.write(b"  ").try(stderr);
                *line_count += 1;
            }
            match byte {
                0...8 | 11...31 => if self.show_nonprinting {
                    push_caret(stdout, stderr, byte+64);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                9 => {
                    if self.show_tabs {
                        push_caret(stdout, stderr, b'I');
                    } else {
                        stdout.write(&[byte]).try(stderr);
                    }
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                }
                10 => {
                    if character_count == 0 {
                        if self.squeeze_blank && last_line_was_blank {
                            continue
                        } else if !last_line_was_blank {
                            last_line_was_blank = true;
                        }
                    } else {
                        last_line_was_blank = false;
                        character_count = 0;
                    }
                    if self.show_ends {
                        stdout.write(b"$\n").try(stderr);
                    } else {
                        stdout.write(b"\n").try(stderr);
                    }
                    stdout.flush().try(stderr);
                },
                32...126 => {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                127 => if self.show_nonprinting {
                    push_caret(stdout, stderr, b'?');
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                128...159 => if self.show_nonprinting {
                    stdout.write(b"M-^").try(stderr);
                    stdout.write(&[byte-64]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                } else {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                _ => if self.show_nonprinting {
                    stdout.write(b"M-").try(stderr);
                    stdout.write(&[byte-128]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                } else {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
            }
        }
    }
}
/// Increase the character count by one if number printing is enabled.
fn count_character(character_count: &mut usize, number: &bool, number_nonblank: &bool) {
    if *number || *number_nonblank {
        *character_count += 1;
    }
}

/// Print a caret notation to stdout.
fn push_caret(stdout: &mut StdoutLock, stderr: &mut Stderr, notation: u8) {
    stdout.write(&[b'^']).try(stderr);
    stdout.write(&[notation]).try(stderr);
}

fn main() {
    let stdout = io::stdout();
    let mut stdout = stdout.lock();
    let mut stderr = io::stderr();
    exit(Program::initialize(&mut stdout, &mut stderr).and_execute(&mut stdout, &mut stderr));
}
<commit_msg>Refactored cat.rs to use the coreutils::ArgParser.<commit_after>#![deny(warnings)]

extern crate coreutils;
extern crate extra;

use std::cell::Cell; // Provide mutable fields in immutable structs
use std::env;
use std::error::Error;
use std::fs;
use std::io::{self, BufReader, Read, Stderr, StdoutLock, Write};
use std::process::exit;
use extra::option::OptionalExt;
use coreutils::ArgParser;

const MAN_PAGE: &'static str = /* @MANSTART{cat} */ r#"NAME
    cat - concatenate files and print on the standard output

SYNOPSIS
    cat [-h | --help] [-A | --show-all] [-b | --number-nonblank] [-e] [-E | --show-ends]
        [-n | --number] [-s | --squeeze-blank] [-t] [-T] FILES...

DESCRIPTION
    Concatenates all files to the standard output.

    If no file is given, or if FILE is '-', read from standard input.

OPTIONS
    -A
    --show-all
        equivalent to -vET

    -b
    --number-nonblank
        number nonempty output lines, overriding -n

    -e
        equivalent to -vE

    -E
    --show-ends
        display $ at the end of each line

    -n
    --number
        number all output lines

    -s
    --squeeze-blank
        supress repeated empty output lines

    -t
        equivalent to -vT

    -T
    --show_tabs
        display TAB characters as ^I

    -v
    --show-nonprinting
        use caret (^) and M- notation, except for LFD and TAB.

    -h
    --help
        display this help and exit

AUTHOR
    Written by Michael Murphy.
"#; /* @MANEND */

struct Program {
    exit_status:      Cell<i32>,
    number:           bool,
    number_nonblank:  bool,
    show_ends:        bool,
    show_tabs:        bool,
    show_nonprinting: bool,
    squeeze_blank:    bool,
    paths:            Vec<String>,
}

impl Program {
    /// Initialize the program's arguments and flags.
    fn initialize(stdout: &mut StdoutLock, stderr: &mut Stderr) -> Program {
        let mut parser = ArgParser::new(10).
            add_flag("A", "show-all"). //vET
            add_flag("b", "number-nonblank").
            add_flag("e", ""). //vE
            add_flag("E", "show-ends").
            add_flag("n", "number").
            add_flag("s", "squeeze-blank").
            add_flag("t", ""). //vT
            add_flag("T", "show-tabs").
            add_flag("v", "show-nonprinting").
            add_flag("h", "help");
        parser.initialize(env::args());

        let mut cat = Program {
            exit_status:      Cell::new(0i32),
            number:           false,
            number_nonblank:  false,
            show_ends:        false,
            show_tabs:        false,
            show_nonprinting: false,
            squeeze_blank:    false,
            paths:            Vec::with_capacity(parser.args.len()),
        };

        if parser.flagged('h') || parser.flagged("help") {
            stdout.write(MAN_PAGE.as_bytes()).try(stderr);
            stdout.flush().try(stderr);
            exit(0);
        }

        if parser.flagged('A') || parser.flagged("show-all") {
            cat.show_nonprinting = true;
            cat.show_ends = true;
            cat.show_tabs = true;
        }

        if parser.flagged('b') || parser.flagged("number-nonblank") {
            cat.number_nonblank = true;
            cat.number = false;
        }

        if parser.flagged('E') || parser.flagged("show-ends") || parser.flagged('e') {
            cat.show_ends = true;
        }

        if parser.flagged('n') || parser.flagged("number") {
            cat.number = true;
            cat.number_nonblank = false;
        }

        if parser.flagged('s') || parser.flagged("squeeze-blank") {
            cat.squeeze_blank = true;
        }

        if parser.flagged('T') || parser.flagged("show-tabs") || parser.flagged('t') {
            cat.show_tabs = true;
        }

        if parser.flagged('v') || parser.flagged("show-nonprinting") || parser.flagged('e') || parser.flagged('t') {
            cat.show_nonprinting = true;
        }

        if !parser.args.is_empty() {
            cat.paths = parser.args;
        }
        cat
    }

    /// Execute the parameters given to the program.
    fn and_execute(&self, stdout: &mut StdoutLock, stderr: &mut Stderr) -> i32 {
        let stdin = io::stdin();
        let line_count = &mut 0usize;
        let flags_enabled = self.number || self.number_nonblank || self.show_ends || self.show_tabs ||
                            self.squeeze_blank || self.show_nonprinting;

        if self.paths.is_empty() && flags_enabled {
            self.cat(&mut stdin.lock(), line_count, stdout, stderr);
        } else if self.paths.is_empty() {
            io::copy(&mut stdin.lock(), stdout).try(stderr);
        } else {
            for path in &self.paths {
                if flags_enabled && path == "-" {
                    self.cat(&mut stdin.lock(), line_count, stdout, stderr);
                } else if flags_enabled {
                    fs::File::open(&path)
                        // Open the file and copy the file's contents to standard output based input arguments.
                        .map(|file| self.cat(BufReader::new(file), line_count, stdout, stderr))
                        // If an error occurred, print the error and set the exit status.
                        .unwrap_or_else(|message| {
                            stderr.write(path.as_bytes()).try(stderr);
                            stderr.write(b": ").try(stderr);
                            stderr.write(message.description().as_bytes()).try(stderr);
                            stderr.write(b"\n").try(stderr);
                            stderr.flush().try(stderr);
                            self.exit_status.set(1i32);
                        });
                } else if path == "-" {
                    // Copy the standard input directly to the standard output.
                    io::copy(&mut stdin.lock(), stdout).try(stderr);
                } else {
                    // Open a file and copy the contents directly to standard output.
                    fs::File::open(&path).map(|ref mut file| { io::copy(file, stdout).try(stderr); })
                        // If an error occurs, print the error and set the exit status.
                        .unwrap_or_else(|message| {
                            stderr.write(path.as_bytes()).try(stderr);
                            stderr.write(b": ").try(stderr);
                            stderr.write(message.description().as_bytes()).try(stderr);
                            stderr.write(b"\n").try(stderr);
                            stderr.flush().try(stderr);
                            self.exit_status.set(1i32);
                        });
                }
            }
        }
        self.exit_status.get()
    }

    /// Cats either a file or stdin based on the flag arguments given to the program.
    fn cat<F: Read>(&self, file: F, line_count: &mut usize, stdout: &mut StdoutLock, stderr: &mut Stderr) {
        let mut character_count = 0;
        let mut last_line_was_blank = false;

        for byte in file.bytes().map(|x| x.unwrap()) {
            if (self.number && character_count == 0) || (character_count == 0 && self.number_nonblank && byte != b'\n') {
                stdout.write(b"     ").try(stderr);
                stdout.write(line_count.to_string().as_bytes()).try(stderr);
                stdout.write(b"  ").try(stderr);
                *line_count += 1;
            }
            match byte {
                0...8 | 11...31 => if self.show_nonprinting {
                    push_caret(stdout, stderr, byte+64);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                9 => {
                    if self.show_tabs {
                        push_caret(stdout, stderr, b'I');
                    } else {
                        stdout.write(&[byte]).try(stderr);
                    }
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                }
                10 => {
                    if character_count == 0 {
                        if self.squeeze_blank && last_line_was_blank {
                            continue
                        } else if !last_line_was_blank {
                            last_line_was_blank = true;
                        }
                    } else {
                        last_line_was_blank = false;
                        character_count = 0;
                    }
                    if self.show_ends {
                        stdout.write(b"$\n").try(stderr);
                    } else {
                        stdout.write(b"\n").try(stderr);
                    }
                    stdout.flush().try(stderr);
                },
                32...126 => {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                127 => if self.show_nonprinting {
                    push_caret(stdout, stderr, b'?');
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                128...159 => if self.show_nonprinting {
                    stdout.write(b"M-^").try(stderr);
                    stdout.write(&[byte-64]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                } else {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
                _ => if self.show_nonprinting {
                    stdout.write(b"M-").try(stderr);
                    stdout.write(&[byte-128]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                } else {
                    stdout.write(&[byte]).try(stderr);
                    count_character(&mut character_count, &self.number, &self.number_nonblank);
                },
            }
        }
    }
}
/// Increase the character count by one if number printing is enabled.
fn count_character(character_count: &mut usize, number: &bool, number_nonblank: &bool) {
    if *number || *number_nonblank {
        *character_count += 1;
    }
}

/// Print a caret notation to stdout.
fn push_caret(stdout: &mut StdoutLock, stderr: &mut Stderr, notation: u8) {
    stdout.write(&[b'^']).try(stderr);
    stdout.write(&[notation]).try(stderr);
}

fn main() {
    let stdout = io::stdout();
    let mut stdout = stdout.lock();
    let mut stderr = io::stderr();
    exit(Program::initialize(&mut stdout, &mut stderr).and_execute(&mut stdout, &mut stderr));
}
<|endoftext|>