<commit_before>use Endpoint;
use error;
use reqwest;
use reqwest::header;
use serde::Deserialize;
use serde::Serialize;
use serde_json;
use serde_json::Value;
use serde_urlencoded;
use std;
use std::env;
use std::fmt;
use std::io::Read;

const API_BASE_PATH: &str = "http://proxer.me/api/v1/";

/// Client that holds the api key and sends requests
#[derive(Debug, Clone)]
pub struct Client {
	api_key: String,
	base_uri: String,
	user_agent: String,
}

#[allow(unused)]
impl Client {
	/// Create a new api client
	pub fn new(api_key: String) -> Self
	{
		let crates_version = &std::env::var("CARGO_PKG_VERSION")
			.unwrap_or("unknown".to_string());

		let crates_name = std::env::var("CARGO_PKG_NAME")
			.unwrap_or("unknown".to_string());

		let ua = format!("libproxer-rust({}/v{})", crates_name, crates_version);

		Self {
			api_key: api_key,
			base_uri: API_BASE_PATH.to_string(),
			user_agent: ua,
		}
	}

	pub fn with_env_key(env_key: &str) -> Option<Client>
	{
		match env::var_os(env_key)
		{
			Some(r) => Some(Client::new(r.into_string().unwrap())),
			None => None,
		}
	}

	/// execute a request that satisfies [Request](../trait.Request.html)
	pub fn execute<'a, T: super::Endpoint<'a> + Clone + fmt::Debug>(
		&self,
		mut endpoint: T,
	) -> Result<T::ResponseType, error::Error>
	{

		let uristring = self.base_uri.to_string() + T::URL;

		header!{(ProxerApiKey, "Proxer-Api-Key") => [String]}

		let mut headers = reqwest::header::Headers::new();

		headers.set(header::UserAgent::new(self.user_agent.to_string()));
		headers.set(header::Host::new("proxer.me", None));
		headers.set(header::ContentType::form_url_encoded());
		headers.set(ProxerApiKey(self.api_key.clone()));

		let mut http_req = reqwest::Request::new(
			reqwest::Method::Post,
			uristring.parse().unwrap(),
		);

		// add our headers to the request
		http_req.headers_mut().extend(headers.iter());

		match serde_urlencoded::to_string(endpoint.params_mut().clone())
		{
			Ok(body) => {
				debug!("post data: {}", body);
				*http_req.body_mut() = Some(body.into());
			}
			Err(err) => panic!("can't serialize form parameters"),
		}

		let client = reqwest::Client::new();

		let response = client.execute(http_req);

		// This section needs some reqriting. maybe... later
		match response
		{
			Err(e) => return Err(error::Error::Http),
			Ok(mut res) => {
				let mut json_string = String::new();
				res.read_to_string(&mut json_string);

				// println!("{}", json_string);
				//
				// let value: Value = serde_json::from_str(&json_string).unwrap();
				// println!("{:#?}", value);

				match serde_json::from_str::<ApiResponse<T::ResponseType>>(
					&json_string,
				)
				{
					Err(e) => return Err(error::Error::Json(e)),
					Ok(r) => {
						if r.error != 0 {
							Err(
								error::Error::Api(
									error::api::Api::new(r.error, r.message),
								)
							)
						}
						else {
							Ok(r.data.unwrap())
						}
					}
				}
			}
		}
	}
}

/// responses are parsed into ApiRequest
#[derive(Debug, Clone, Deserialize)]
pub struct ApiResponse<T> {
	/// api error
	pub error: i64,
	/// api message
	pub message: String,
	/// actual data
	pub data: Option<T>,
	/// optional error code
	/// in case of an error this contains the error code
	pub code: Option<i64>,
}
<commit_msg>fix bug that causes wrong error types<commit_after>use Endpoint;
use error;
use reqwest;
use reqwest::header;
use serde::Deserialize;
use serde::Serialize;
use serde_json;
use serde_json::Value;
use serde_urlencoded;
use std;
use std::env;
use std::fmt;
use std::io::Read;

const API_BASE_PATH: &str = "http://proxer.me/api/v1/";

/// Client that holds the api key and sends requests
#[derive(Debug, Clone)]
pub struct Client {
	api_key: String,
	base_uri: String,
	user_agent: String,
}

#[allow(unused)]
impl Client {
	/// Create a new api client
	pub fn new(api_key: String) -> Self
	{
		let crates_version = &std::env::var("CARGO_PKG_VERSION")
			.unwrap_or("unknown".to_string());

		let crates_name = std::env::var("CARGO_PKG_NAME")
			.unwrap_or("unknown".to_string());

		let ua = format!("libproxer-rust({}/v{})", crates_name, crates_version);

		Self {
			api_key: api_key,
			base_uri: API_BASE_PATH.to_string(),
			user_agent: ua,
		}
	}

	pub fn with_env_key(env_key: &str) -> Option<Client>
	{
		match env::var_os(env_key)
		{
			Some(r) => Some(Client::new(r.into_string().unwrap())),
			None => None,
		}
	}

	/// execute a request that satisfies [Request](../trait.Request.html)
	pub fn execute<'a, T: super::Endpoint<'a> + Clone + fmt::Debug>(
		&self,
		mut endpoint: T,
	) -> Result<T::ResponseType, error::Error>
	{

		let uristring = self.base_uri.to_string() + T::URL;

		header!{(ProxerApiKey, "Proxer-Api-Key") => [String]}

		let mut headers = reqwest::header::Headers::new();

		headers.set(header::UserAgent::new(self.user_agent.to_string()));
		headers.set(header::Host::new("proxer.me", None));
		headers.set(header::ContentType::form_url_encoded());
		headers.set(ProxerApiKey(self.api_key.clone()));

		let mut http_req = reqwest::Request::new(
			reqwest::Method::Post,
			uristring.parse().unwrap(),
		);

		// add our headers to the request
		http_req.headers_mut().extend(headers.iter());

		match serde_urlencoded::to_string(endpoint.params_mut().clone())
		{
			Ok(body) => {
				debug!("post data: {}", body);
				*http_req.body_mut() = Some(body.into());
			}
			Err(err) => panic!("can't serialize form parameters"),
		}

		let client = reqwest::Client::new();

		let response = client.execute(http_req);

		// This section needs some reqriting. maybe... later
		match response
		{
			Err(e) => return Err(error::Error::Http),
			Ok(mut res) => {
				let mut json_string = String::new();
				res.read_to_string(&mut json_string);

				// println!("{}", json_string);
				//
				// let value: Value = serde_json::from_str(&json_string).unwrap();
				// println!("{:#?}", value);

				match serde_json::from_str::<ApiResponse<T::ResponseType>>(
					&json_string,
				)
				{
					Err(e) => return Err(error::Error::Json(e)),
					Ok(r) => {
						if r.error != 0 {
							Err(
								error::Error::Api(
									error::api::Api::new(r.code.unwrap(), r.message),
								)
							)
						}
						else {
							Ok(r.data.unwrap())
						}
					}
				}
			}
		}
	}
}

/// responses are parsed into ApiRequest
#[derive(Debug, Clone, Deserialize)]
pub struct ApiResponse<T> {
	/// api error
	pub error: i64,
	/// api message
	pub message: String,
	/// actual data
	pub data: Option<T>,
	/// optional error code
	/// in case of an error this contains the error code
	pub code: Option<i64>,
}
<|endoftext|>


<commit_before>use htslib::bam::record::Record;

pub trait RecordCheck {
    fn valid(&self, rec: &Record) -> bool;
    fn vir_pos(&self, rec: &Record) -> i32;
}

pub struct SingleChecker {
    pub exact_length: bool,
    pub read_length: usize,
    pub min_quality: u8,
}

fn vir_pos_common(read_length: usize, exact_length: bool, rec: &Record) -> i32 {
    if exact_length && rec.is_reverse() {
        rec.pos() + (rec.seq().len() as i32 - read_length as i32)
    } else {
        rec.pos()
    }
}

impl RecordCheck for SingleChecker {
    fn valid(&self, record: &Record) -> bool {
        !record.is_unmapped() && (!self.exact_length || record.seq().len() == self.read_length) && record.mapq() >= self.min_quality
    }

    fn vir_pos(&self, rec: &Record) -> i32 {
        vir_pos_common(self.read_length, self.exact_length, rec)
    }
}

pub struct PairedChecker {
    pub exact_length: bool,
    pub read_length: usize,
    pub min_quality: u8,
    pub min_dist: i32,
    pub max_dist: i32,
    pub force_paired: bool,
    pub max_distance: bool,
}

impl RecordCheck for PairedChecker {
    fn valid(&self, record: &Record) -> bool {
        // check single read conditions
        if record.is_unmapped() || (self.exact_length && record.seq().len() != self.read_length) || record.mapq() < self.min_quality {
            return false;
        }
        // mandatory paired condition
        if ( !record.is_paired() || record.is_mate_unmapped() || record.tid() != record.mtid() ) && self.force_paired {
            return false;
        }
        // check pair distance
        if record.is_paired() && self.max_distance {
            let dist = (record.pos() - record.mpos()).abs() + self.read_length as i32; // TODO FIX!
            if dist < self.min_dist || dist > self.max_dist {
                return false;
            }
        }
        true
    }

    fn vir_pos(&self, rec: &Record) -> i32 {
        vir_pos_common(self.read_length, self.exact_length, rec)
    }
}
<commit_msg>Real fix for coordinates<commit_after>use htslib::bam::record::Record;

pub trait RecordCheck {
    fn valid(&self, rec: &Record) -> bool;
    fn vir_pos(&self, rec: &Record) -> i32;
}

pub struct SingleChecker {
    pub exact_length: bool,
    pub read_length: usize,
    pub min_quality: u8,
}

fn vir_pos_common(read_length: usize, exact_length: bool, rec: &Record) -> i32 {
    if !exact_length && rec.is_reverse() {
        rec.pos() + (rec.seq().len() as i32) - (read_length as i32)
    } else {
        rec.pos()
    }
}

impl RecordCheck for SingleChecker {
    fn valid(&self, record: &Record) -> bool {
        !record.is_unmapped() && (!self.exact_length || record.seq().len() == self.read_length) && record.mapq() >= self.min_quality
    }

    fn vir_pos(&self, rec: &Record) -> i32 {
        vir_pos_common(self.read_length, self.exact_length, rec)
    }
}

pub struct PairedChecker {
    pub exact_length: bool,
    pub read_length: usize,
    pub min_quality: u8,
    pub min_dist: i32,
    pub max_dist: i32,
    pub force_paired: bool,
    pub max_distance: bool,
}

impl RecordCheck for PairedChecker {
    fn valid(&self, record: &Record) -> bool {
        // check single read conditions
        if record.is_unmapped() || (self.exact_length && record.seq().len() != self.read_length) || record.mapq() < self.min_quality {
            return false;
        }
        // mandatory paired condition
        if ( !record.is_paired() || record.is_mate_unmapped() || record.tid() != record.mtid() ) && self.force_paired {
            return false;
        }
        // check pair distance
        if record.is_paired() && self.max_distance {
            let dist = (record.pos() - record.mpos()).abs() + self.read_length as i32; // TODO FIX!
            if dist < self.min_dist || dist > self.max_dist {
                return false;
            }
        }
        true
    }

    fn vir_pos(&self, rec: &Record) -> i32 {
        vir_pos_common(self.read_length, self.exact_length, rec)
    }
}
<|endoftext|>


<commit_before>pub mod tileset;

extern crate find_folder;
extern crate image as im;

use piston_window::*;
use gfx_device_gl::Resources;
use gfx_device_gl::CommandBuffer;
use gfx_graphics::GfxGraphics;
use std::fs::File;
use std::io::prelude::*;
use std::str::FromStr;
use self::tileset::{Tileset, TILESET_HEIGHT, TILE_HEIGHT, TILESET_WIDTH, TILE_WIDTH};
use ::level::Level;

pub fn read_tileset(path: &str, mut w: &mut PistonWindow) -> Tileset {

    let mut tileset = Tileset::new();

    let mut ts = match im::open(path) {
        Ok(x) => x,
        Err(i) => panic!("{:?}", i),
    };

    for i in 0..(TILESET_HEIGHT / TILE_HEIGHT) {
        for j in 0..(TILESET_WIDTH / TILE_WIDTH) {

            let tile = ts.crop(j * TILE_WIDTH, i * TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT).to_rgba();

            tileset.get_set().push(
                Texture::from_image(
                    &mut w.factory,
                    &tile,
                    &TextureSettings::new()
            ).unwrap());
        }
    }

    tileset
}

pub fn read_level(path: &str) -> Level {
    let mut f = match File::open(path) {
        Ok(res) => res,
        Err(e) => panic!(e),
    };

    let mut buffer = String::new();

    match f.read_to_string(&mut buffer) {
        Ok(_) => (),
        Err(e) => panic!("{:?}", e),
    };

    let mut rows = buffer.split("\n").filter(|s| !s.is_empty());

    let first_row = match rows.next() {
        Some(x) => x,
        None => panic!("No valid Level-File"),
    };

    let mut sizes = first_row.split(" ");

    let x = match sizes.next() {
        Some(x) => x,
        None => panic!("No valid Level-File"),
    };
    let x_new = match usize::from_str(x) {
        Ok(a) => a,
        Err(_) => panic!("No valid Level-File"),
    };

    let y = match sizes.next() {
        Some(x) => x,
        None => panic!("No valid Level-File"),
    };
    let y_new = match usize::from_str(y) {
        Ok(a) => a,
        Err(_) => panic!("No valid Level-File"),
    };

    let mut level = Level::with_size(x_new, y_new);

    let mut i = 0;
    let mut j = 0;
    // Zeilen
    for s in rows {
        j = 0;
        // Spalten
        for c in s.split(" ").filter(|s| !s.is_empty()) {
            let n = match u32::from_str(c) {
                Ok(a) => a,
                Err(e) => panic!("{:?}", e),
            };
            level.get_data()[i][j] = n;
            j += 1;
        }
        i += 1;
    }

    level
}

pub fn render_level(
    tileset: &Tileset,
    g: &mut GfxGraphics<Resources,CommandBuffer>,
    view: math::Matrix2d,
    level: &mut Level)
{
    for i in 0..level.get_y() {
        for j in 0..level.get_x() {
            let tile = match tileset.get_texture(level.get_data()[i][j]) {
                Some(x) => x,
                None => panic!("No texture found."),
            };

            render_tile(&tile, g, view, j as u32 * 16 + j as u32, i as u32 * 16 + i as u32);
        }
    }
}

pub fn render_tile(
    texture: &Texture<Resources>,
    g: &mut GfxGraphics<Resources, CommandBuffer>,
    view: math::Matrix2d,
    x_coord: u32,
    y_coord: u32)
{
    // Skaliere Tile um Faktor 4
    image(texture, view.scale(4.0, 4.0).trans(x_coord as f64, y_coord as f64), g);
}
<commit_msg>Weitere plattformunabhängige Erweiterung<commit_after>pub mod tileset;

extern crate find_folder;
extern crate image as im;

use piston_window::*;
use gfx_device_gl::Resources;
use gfx_device_gl::CommandBuffer;
use gfx_graphics::GfxGraphics;
use std::fs::File;
use std::io::prelude::*;
use std::str::FromStr;
use self::tileset::{Tileset, TILESET_HEIGHT, TILE_HEIGHT, TILESET_WIDTH, TILE_WIDTH};
use ::level::Level;

pub fn read_tileset(path: &str, mut w: &mut PistonWindow) -> Tileset {

    let mut tileset = Tileset::new();

    let mut ts = match im::open(path) {
        Ok(x) => x,
        Err(i) => panic!("{:?}", i),
    };

    for i in 0..(TILESET_HEIGHT / TILE_HEIGHT) {
        for j in 0..(TILESET_WIDTH / TILE_WIDTH) {

            let tile = ts.crop(j * TILE_WIDTH, i * TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT).to_rgba();

            tileset.get_set().push(
                Texture::from_image(
                    &mut w.factory,
                    &tile,
                    &TextureSettings::new()
            ).unwrap());
        }
    }

    tileset
}

pub fn read_level(path: &str) -> Level {
    let mut f = match File::open(path) {
        Ok(res) => res,
        Err(e) => panic!(e),
    };

    let mut buffer = String::new();

    match f.read_to_string(&mut buffer) {
        Ok(_) => (),
        Err(e) => panic!("{:?}", e),
    };

    let mut rows = buffer.lines().filter(|s| !s.is_empty());

    let first_row = match rows.next() {
        Some(x) => x,
        None => panic!("No valid Level-File"),
    };

    let mut sizes = first_row.split(" ");

    let x = match sizes.next() {
        Some(x) => x,
        None => panic!("No valid Level-File"),
    };
    let x_new = match usize::from_str(x) {
        Ok(a) => a,
        Err(_) => panic!("No valid Level-File"),
    };

    let y = match sizes.next() {
        Some(x) => x,
        None => panic!("No valid Level-File"),
    };
    let y_new = match usize::from_str(y) {
        Ok(a) => a,
        Err(_) => panic!("No valid Level-File"),
    };

    let mut level = Level::with_size(x_new, y_new);

    let mut i = 0;
    let mut j = 0;
    // Zeilen
    for s in rows {
        j = 0;
        // Spalten
        for c in s.split(" ").filter(|s| !s.is_empty()) {
            let n = match u32::from_str(c) {
                Ok(a) => a,
                Err(e) => panic!("{:?}", e),
            };
            level.get_data()[i][j] = n;
            j += 1;
        }
        i += 1;
    }

    level
}

pub fn render_level(
    tileset: &Tileset,
    g: &mut GfxGraphics<Resources,CommandBuffer>,
    view: math::Matrix2d,
    level: &mut Level)
{
    for i in 0..level.get_y() {
        for j in 0..level.get_x() {
            let tile = match tileset.get_texture(level.get_data()[i][j]) {
                Some(x) => x,
                None => panic!("No texture found."),
            };

            render_tile(&tile, g, view, j as u32 * 16 + j as u32, i as u32 * 16 + i as u32);
        }
    }
}

pub fn render_tile(
    texture: &Texture<Resources>,
    g: &mut GfxGraphics<Resources, CommandBuffer>,
    view: math::Matrix2d,
    x_coord: u32,
    y_coord: u32)
{
    // Skaliere Tile um Faktor 4
    image(texture, view.scale(4.0, 4.0).trans(x_coord as f64, y_coord as f64), g);
}
<|endoftext|>


<commit_before>/*
Module: fs

File system manipulation
*/

import os;
import os::getcwd;
import os_fs;

#[abi = "cdecl"]
native mod rustrt {
    fn rust_file_is_dir(path: str::sbuf) -> int;
}

/*
Function: path_sep

Get the default path separator for the host platform
*/
fn path_sep() -> str { ret str::from_char(os_fs::path_sep); }

// FIXME: This type should probably be constrained
/*
Type: path

A path or fragment of a filesystem path
*/
type path = str;

/*
Function: dirname

Get the directory portion of a path

Returns all of the path up to, but excluding, the final path separator.
The dirname of "/usr/share" will be "/usr", but the dirname of
"/usr/share/" is "/usr/share".

If the path is not prefixed with a directory, then "." is returned.
*/
fn dirname(p: path) -> path {
    let i: int = str::rindex(p, os_fs::path_sep as u8);
    if i == -1 {
        i = str::rindex(p, os_fs::alt_path_sep as u8);
        if i == -1 { ret "."; }
    }
    ret str::substr(p, 0u, i as uint);
}

/*
Function: basename

Get the file name portion of a path

Returns the portion of the path after the final path separator.
The basename of "/usr/share" will be "share". If there are no
path separators in the path then the returned path is identical to
the provided path. If an empty path is provided or the path ends
with a path separator then an empty path is returned.
*/
fn basename(p: path) -> path {
    let i: int = str::rindex(p, os_fs::path_sep as u8);
    if i == -1 {
        i = str::rindex(p, os_fs::alt_path_sep as u8);
        if i == -1 { ret p; }
    }
    let len = str::byte_len(p);
    if i + 1 as uint >= len { ret p; }
    ret str::slice(p, i + 1 as uint, len);
}

// FIXME: Need some typestate to avoid bounds check when len(pre) == 0
/*
Function: connect

Connects to path segments

Given paths `pre` and `post` this function will return a path
that is equal to `post` appended to `pre`, inserting a path separator
between the two as needed.
*/
fn connect(pre: path, post: path) -> path {
    let len = str::byte_len(pre);
    ret if pre[len - 1u] == os_fs::path_sep as u8 {

            // Trailing '/'?
            pre + post
        } else { pre + path_sep() + post };
}

/*
Function: connect_many

Connects a vector of path segments into a single path.

Inserts path separators as needed.
*/
fn connect_many(paths: [path]) : vec::is_not_empty(paths) -> path {
    ret if vec::len(paths) == 1u {
        paths[0]
    } else {
        let rest = vec::slice(paths, 1u, vec::len(paths));
        check vec::is_not_empty(rest);
        connect(paths[0], connect_many(rest))
    }
}

/*
Function: file_id_dir

Indicates whether a path represents a directory.
*/
fn file_is_dir(p: path) -> bool {
    ret str::as_buf(p, {|buf| rustrt::rust_file_is_dir(buf) != 0 });
}

/*
Function: make_dir

Creates a directory at the specific path.
*/
fn make_dir(p: path, mode: int) -> bool {
    ret mkdir(p, mode);

    #[cfg(target_os = "win32")]
    fn mkdir(_p: path, _mode: int) -> bool {
        // FIXME: turn mode into something useful?
        let noctx = ptr::null();
        ret str::as_buf(_p, {|buf|
            os::kernel32::CreateDirectory(buf, noctx)
        });
    }

    #[cfg(target_os = "linux")]
    #[cfg(target_os = "macos")]
    fn mkdir(_p: path, _mode: int) -> bool {
        ret str::as_buf(_p, {|buf| os::libc::mkdir(buf, _mode) == 0 });
    }
}

/*
Function: list_dir

Lists the contents of a directory.
*/
fn list_dir(p: path) -> [str] {
    let p = p;
    let pl = str::byte_len(p);
    if pl == 0u || p[pl - 1u] as char != os_fs::path_sep { p += path_sep(); }
    let full_paths: [str] = [];
    for filename: str in os_fs::list_dir(p) {
        if !str::eq(filename, ".") {
            if !str::eq(filename, "..") { full_paths += [p + filename]; }
        }
    }
    ret full_paths;
}

/*
Function: path_is_absolute

Indicates whether a path is absolute.

A path is considered absolute if it begins at the filesystem root ("/") or,
on Windows, begins with a drive letter.
*/
fn path_is_absolute(p: path) -> bool { ret os_fs::path_is_absolute(p); }

// FIXME: under Windows, we should prepend the current drive letter to paths
// that start with a slash.
/*
Function: make_absolute

Convert a relative path to an absolute path

If the given path is relative, return it prepended with the current working
directory. If the given path is already an absolute path, return it
as is.
*/
fn make_absolute(p: path) -> path {
    if path_is_absolute(p) { ret p; } else { ret connect(getcwd(), p); }
}

/*
Function: split

Split a path into it's individual components

Splits a given path by path separators and returns a vector containing
each piece of the path. On Windows, if the path is absolute then
the first element of the returned vector will be the drive letter
followed by a colon.
*/
fn split(p: path) -> [path] {
    let split1 = str::split(p, os_fs::path_sep as u8);
    let split2 = [];
    for s in split1 {
        split2 += str::split(s, os_fs::alt_path_sep as u8);
    }
    ret split2;
}

/*
Function: splitext

Split a path into a pair of strings with the first element being the filename
without the extension and the second being either empty or the file extension
including the period. Leading periods in the basename are ignored.  If the
path includes directory components then they are included in the filename part
of the result pair.
*/
fn splitext(p: path) -> (str, str) {
    if str::is_empty(p) { ("", "") }
    else {
        let parts = str::split(p, '.' as u8);
        if vec::len(parts) > 1u {
            let base = str::connect(vec::init(parts), ".");
            let ext = "." + option::get(vec::last(parts));

            fn is_dotfile(base: str) -> bool {
                str::is_empty(base)
                    || str::ends_with(
                        base, str::from_char(os_fs::path_sep))
                    || str::ends_with(
                        base, str::from_char(os_fs::alt_path_sep))
            }

            fn ext_contains_sep(ext: str) -> bool {
                vec::len(split(ext)) > 1u
            }

            fn no_basename(ext: str) -> bool {
                str::ends_with(
                    ext, str::from_char(os_fs::path_sep))
                    || str::ends_with(
                        ext, str::from_char(os_fs::alt_path_sep))
            }

            if is_dotfile(base)
                || ext_contains_sep(ext)
                || no_basename(ext) {
                (p, "")
            } else {
                (base, ext)
            }
        } else {
            (p, "")
        }
    }
}

/*
Function: normalize

Removes extra "." and ".." entries from paths.

Does not follow symbolic links.
*/
fn normalize(p: path) -> path {
    let s = split(p);
    let s = strip_dots(s);
    let s = rollup_doubledots(s);

    let s = if check vec::is_not_empty(s) {
        connect_many(s)
    } else {
        ""
    };
    let s = reabsolute(p, s);
    let s = reterminate(p, s);

    let s = if str::byte_len(s) == 0u {
        "."
    } else {
        s
    };

    ret s;

    fn strip_dots(s: [path]) -> [path] {
        vec::filter_map({ |elem|
            if elem == "." {
                option::none
            } else {
                option::some(elem)
            }
        }, s)
    }

    fn rollup_doubledots(s: [path]) -> [path] {
        if vec::is_empty(s) {
            ret [];
        }

        let t = [];
        let i = vec::len(s);
        let skip = 0;
        do {
            i -= 1u;
            if s[i] == ".." {
                skip += 1;
            } else {
                if skip == 0 {
                    t += [s[i]];
                } else {
                    skip -= 1;
                }
            }
        } while i != 0u;
        let t = vec::reversed(t);
        while skip > 0 {
            t += [".."];
            skip -= 1;
        }
        ret t;
    }

    #[cfg(target_os = "linux")]
    #[cfg(target_os = "macos")]
    fn reabsolute(orig: path, new: path) -> path {
        if path_is_absolute(orig) {
            path_sep() + new
        } else {
            new
        }
    }

    #[cfg(target_os = "win32")]
    fn reabsolute(orig: path, new: path) -> path {
       if path_is_absolute(orig) && orig[0] == os_fs::path_sep as u8 {
           str::from_char(os_fs::path_sep) + new
       } else {
           new
       }
    }

    fn reterminate(orig: path, new: path) -> path {
        let last = orig[str::byte_len(orig) - 1u];
        if last == os_fs::path_sep as u8
            || last == os_fs::path_sep as u8 {
            ret new + path_sep();
        } else {
            ret new;
        }
    }
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
<commit_msg>once more with feeling...<commit_after>/*
Module: fs

File system manipulation
*/

import os;
import os::getcwd;
import os_fs;

#[abi = "cdecl"]
native mod rustrt {
    fn rust_file_is_dir(path: str::sbuf) -> int;
}

/*
Function: path_sep

Get the default path separator for the host platform
*/
fn path_sep() -> str { ret str::from_char(os_fs::path_sep); }

// FIXME: This type should probably be constrained
/*
Type: path

A path or fragment of a filesystem path
*/
type path = str;

/*
Function: dirname

Get the directory portion of a path

Returns all of the path up to, but excluding, the final path separator.
The dirname of "/usr/share" will be "/usr", but the dirname of
"/usr/share/" is "/usr/share".

If the path is not prefixed with a directory, then "." is returned.
*/
fn dirname(p: path) -> path {
    let i: int = str::rindex(p, os_fs::path_sep as u8);
    if i == -1 {
        i = str::rindex(p, os_fs::alt_path_sep as u8);
        if i == -1 { ret "."; }
    }
    ret str::substr(p, 0u, i as uint);
}

/*
Function: basename

Get the file name portion of a path

Returns the portion of the path after the final path separator.
The basename of "/usr/share" will be "share". If there are no
path separators in the path then the returned path is identical to
the provided path. If an empty path is provided or the path ends
with a path separator then an empty path is returned.
*/
fn basename(p: path) -> path {
    let i: int = str::rindex(p, os_fs::path_sep as u8);
    if i == -1 {
        i = str::rindex(p, os_fs::alt_path_sep as u8);
        if i == -1 { ret p; }
    }
    let len = str::byte_len(p);
    if i + 1 as uint >= len { ret p; }
    ret str::slice(p, i + 1 as uint, len);
}

// FIXME: Need some typestate to avoid bounds check when len(pre) == 0
/*
Function: connect

Connects to path segments

Given paths `pre` and `post` this function will return a path
that is equal to `post` appended to `pre`, inserting a path separator
between the two as needed.
*/
fn connect(pre: path, post: path) -> path {
    let len = str::byte_len(pre);
    ret if pre[len - 1u] == os_fs::path_sep as u8 {

            // Trailing '/'?
            pre + post
        } else { pre + path_sep() + post };
}

/*
Function: connect_many

Connects a vector of path segments into a single path.

Inserts path separators as needed.
*/
fn connect_many(paths: [path]) : vec::is_not_empty(paths) -> path {
    ret if vec::len(paths) == 1u {
        paths[0]
    } else {
        let rest = vec::slice(paths, 1u, vec::len(paths));
        check vec::is_not_empty(rest);
        connect(paths[0], connect_many(rest))
    }
}

/*
Function: file_id_dir

Indicates whether a path represents a directory.
*/
fn file_is_dir(p: path) -> bool {
    ret str::as_buf(p, {|buf| rustrt::rust_file_is_dir(buf) != 0 });
}

/*
Function: make_dir

Creates a directory at the specific path.
*/
fn make_dir(p: path, mode: int) -> bool {
    ret mkdir(p, mode);

    #[cfg(target_os = "win32")]
    fn mkdir(_p: path, _mode: int) -> bool {
        // FIXME: turn mode into something useful?
        ret str::as_buf(_p, {|buf|
            os::kernel32::CreateDirectory(buf, ptr::null())
        });
    }

    #[cfg(target_os = "linux")]
    #[cfg(target_os = "macos")]
    fn mkdir(_p: path, _mode: int) -> bool {
        ret str::as_buf(_p, {|buf| os::libc::mkdir(buf, _mode) == 0 });
    }
}

/*
Function: list_dir

Lists the contents of a directory.
*/
fn list_dir(p: path) -> [str] {
    let p = p;
    let pl = str::byte_len(p);
    if pl == 0u || p[pl - 1u] as char != os_fs::path_sep { p += path_sep(); }
    let full_paths: [str] = [];
    for filename: str in os_fs::list_dir(p) {
        if !str::eq(filename, ".") {
            if !str::eq(filename, "..") { full_paths += [p + filename]; }
        }
    }
    ret full_paths;
}

/*
Function: path_is_absolute

Indicates whether a path is absolute.

A path is considered absolute if it begins at the filesystem root ("/") or,
on Windows, begins with a drive letter.
*/
fn path_is_absolute(p: path) -> bool { ret os_fs::path_is_absolute(p); }

// FIXME: under Windows, we should prepend the current drive letter to paths
// that start with a slash.
/*
Function: make_absolute

Convert a relative path to an absolute path

If the given path is relative, return it prepended with the current working
directory. If the given path is already an absolute path, return it
as is.
*/
fn make_absolute(p: path) -> path {
    if path_is_absolute(p) { ret p; } else { ret connect(getcwd(), p); }
}

/*
Function: split

Split a path into it's individual components

Splits a given path by path separators and returns a vector containing
each piece of the path. On Windows, if the path is absolute then
the first element of the returned vector will be the drive letter
followed by a colon.
*/
fn split(p: path) -> [path] {
    let split1 = str::split(p, os_fs::path_sep as u8);
    let split2 = [];
    for s in split1 {
        split2 += str::split(s, os_fs::alt_path_sep as u8);
    }
    ret split2;
}

/*
Function: splitext

Split a path into a pair of strings with the first element being the filename
without the extension and the second being either empty or the file extension
including the period. Leading periods in the basename are ignored.  If the
path includes directory components then they are included in the filename part
of the result pair.
*/
fn splitext(p: path) -> (str, str) {
    if str::is_empty(p) { ("", "") }
    else {
        let parts = str::split(p, '.' as u8);
        if vec::len(parts) > 1u {
            let base = str::connect(vec::init(parts), ".");
            let ext = "." + option::get(vec::last(parts));

            fn is_dotfile(base: str) -> bool {
                str::is_empty(base)
                    || str::ends_with(
                        base, str::from_char(os_fs::path_sep))
                    || str::ends_with(
                        base, str::from_char(os_fs::alt_path_sep))
            }

            fn ext_contains_sep(ext: str) -> bool {
                vec::len(split(ext)) > 1u
            }

            fn no_basename(ext: str) -> bool {
                str::ends_with(
                    ext, str::from_char(os_fs::path_sep))
                    || str::ends_with(
                        ext, str::from_char(os_fs::alt_path_sep))
            }

            if is_dotfile(base)
                || ext_contains_sep(ext)
                || no_basename(ext) {
                (p, "")
            } else {
                (base, ext)
            }
        } else {
            (p, "")
        }
    }
}

/*
Function: normalize

Removes extra "." and ".." entries from paths.

Does not follow symbolic links.
*/
fn normalize(p: path) -> path {
    let s = split(p);
    let s = strip_dots(s);
    let s = rollup_doubledots(s);

    let s = if check vec::is_not_empty(s) {
        connect_many(s)
    } else {
        ""
    };
    let s = reabsolute(p, s);
    let s = reterminate(p, s);

    let s = if str::byte_len(s) == 0u {
        "."
    } else {
        s
    };

    ret s;

    fn strip_dots(s: [path]) -> [path] {
        vec::filter_map({ |elem|
            if elem == "." {
                option::none
            } else {
                option::some(elem)
            }
        }, s)
    }

    fn rollup_doubledots(s: [path]) -> [path] {
        if vec::is_empty(s) {
            ret [];
        }

        let t = [];
        let i = vec::len(s);
        let skip = 0;
        do {
            i -= 1u;
            if s[i] == ".." {
                skip += 1;
            } else {
                if skip == 0 {
                    t += [s[i]];
                } else {
                    skip -= 1;
                }
            }
        } while i != 0u;
        let t = vec::reversed(t);
        while skip > 0 {
            t += [".."];
            skip -= 1;
        }
        ret t;
    }

    #[cfg(target_os = "linux")]
    #[cfg(target_os = "macos")]
    fn reabsolute(orig: path, new: path) -> path {
        if path_is_absolute(orig) {
            path_sep() + new
        } else {
            new
        }
    }

    #[cfg(target_os = "win32")]
    fn reabsolute(orig: path, new: path) -> path {
       if path_is_absolute(orig) && orig[0] == os_fs::path_sep as u8 {
           str::from_char(os_fs::path_sep) + new
       } else {
           new
       }
    }

    fn reterminate(orig: path, new: path) -> path {
        let last = orig[str::byte_len(orig) - 1u];
        if last == os_fs::path_sep as u8
            || last == os_fs::path_sep as u8 {
            ret new + path_sep();
        } else {
            ret new;
        }
    }
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
<|endoftext|>


<commit_before>use clippy_utils::diagnostics::span_lint_and_then;

use rustc_data_structures::fx::FxHashMap;
use rustc_hir::{
    def::Res, def_id::DefId, Item, ItemKind, PolyTraitRef, PrimTy, TraitBoundModifier, Ty, TyKind, UseKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span;

use crate::utils::conf;

declare_clippy_lint! {
    /// ### What it does
    /// Denies the configured types in clippy.toml.
    ///
    /// ### Why is this bad?
    /// Some types are undesirable in certain contexts.
    ///
    /// ### Example:
    /// An example clippy.toml configuration:
    /// ```toml
    /// # clippy.toml
    /// disallowed-types = [
    ///     # Can use a string as the path of the disallowed type.
    ///     "std::collections::BTreeMap",
    ///     # Can also use an inline table with a `path` key.
    ///     { path = "std::net::TcpListener" },
    ///     # When using an inline table, can add a `reason` for why the type
    ///     # is disallowed.
    ///     { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" },
    /// ]
    /// ```
    ///
    /// ```rust,ignore
    /// use std::collections::BTreeMap;
    /// // or its use
    /// let x = std::collections::BTreeMap::new();
    /// ```
    /// Use instead:
    /// ```rust,ignore
    /// // A similar type that is allowed by the config
    /// use std::collections::HashMap;
    /// ```
    pub DISALLOWED_TYPE,
    nursery,
    "use of a disallowed type"
}
#[derive(Clone, Debug)]
pub struct DisallowedType {
    disallowed: Vec<conf::DisallowedType>,
    def_ids: FxHashMap<DefId, Option<String>>,
    prim_tys: FxHashMap<PrimTy, Option<String>>,
}

impl DisallowedType {
    pub fn new(disallowed: Vec<conf::DisallowedType>) -> Self {
        Self {
            disallowed,
            def_ids: FxHashMap::default(),
            prim_tys: FxHashMap::default(),
        }
    }

    fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) {
        match res {
            Res::Def(_, did) => {
                if let Some(reason) = self.def_ids.get(did) {
                    emit(cx, &cx.tcx.def_path_str(*did), span, reason.as_deref());
                }
            },
            Res::PrimTy(prim) => {
                if let Some(reason) = self.prim_tys.get(prim) {
                    emit(cx, prim.name_str(), span, reason.as_deref());
                }
            },
            _ => {},
        }
    }
}

impl_lint_pass!(DisallowedType => [DISALLOWED_TYPE]);

impl<'tcx> LateLintPass<'tcx> for DisallowedType {
    fn check_crate(&mut self, cx: &LateContext<'_>) {
        for conf in &self.disallowed {
            let (path, reason) = match conf {
                conf::DisallowedType::Simple(path) => (path, None),
                conf::DisallowedType::WithReason { path, reason } => (
                    path,
                    reason.as_ref().map(|reason| format!("{} (from clippy.toml)", reason)),
                ),
            };
            let segs: Vec<_> = path.split("::").collect();
            match clippy_utils::path_to_res(cx, &segs) {
                Res::Def(_, id) => {
                    self.def_ids.insert(id, reason);
                },
                Res::PrimTy(ty) => {
                    self.prim_tys.insert(ty, reason);
                },
                _ => {},
            }
        }
    }

    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
        if let ItemKind::Use(path, UseKind::Single) = &item.kind {
            self.check_res_emit(cx, &path.res, item.span);
        }
    }

    fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
        if let TyKind::Path(path) = &ty.kind {
            self.check_res_emit(cx, &cx.qpath_res(path, ty.hir_id), ty.span);
        }
    }

    fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>, _: TraitBoundModifier) {
        self.check_res_emit(cx, &poly.trait_ref.path.res, poly.trait_ref.path.span);
    }
}

fn emit(cx: &LateContext<'_>, name: &str, span: Span, reason: Option<&str>) {
    span_lint_and_then(
        cx,
        DISALLOWED_TYPE,
        span,
        &format!("`{}` is not allowed according to config", name),
        |diag| {
            if let Some(reason) = reason {
                diag.note(reason);
            }
        },
    );
}
<commit_msg>Rename `disallowed` to `conf_disallowed`<commit_after>use clippy_utils::diagnostics::span_lint_and_then;

use rustc_data_structures::fx::FxHashMap;
use rustc_hir::{
    def::Res, def_id::DefId, Item, ItemKind, PolyTraitRef, PrimTy, TraitBoundModifier, Ty, TyKind, UseKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span;

use crate::utils::conf;

declare_clippy_lint! {
    /// ### What it does
    /// Denies the configured types in clippy.toml.
    ///
    /// ### Why is this bad?
    /// Some types are undesirable in certain contexts.
    ///
    /// ### Example:
    /// An example clippy.toml configuration:
    /// ```toml
    /// # clippy.toml
    /// disallowed-types = [
    ///     # Can use a string as the path of the disallowed type.
    ///     "std::collections::BTreeMap",
    ///     # Can also use an inline table with a `path` key.
    ///     { path = "std::net::TcpListener" },
    ///     # When using an inline table, can add a `reason` for why the type
    ///     # is disallowed.
    ///     { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" },
    /// ]
    /// ```
    ///
    /// ```rust,ignore
    /// use std::collections::BTreeMap;
    /// // or its use
    /// let x = std::collections::BTreeMap::new();
    /// ```
    /// Use instead:
    /// ```rust,ignore
    /// // A similar type that is allowed by the config
    /// use std::collections::HashMap;
    /// ```
    pub DISALLOWED_TYPE,
    nursery,
    "use of a disallowed type"
}
#[derive(Clone, Debug)]
pub struct DisallowedType {
    conf_disallowed: Vec<conf::DisallowedType>,
    def_ids: FxHashMap<DefId, Option<String>>,
    prim_tys: FxHashMap<PrimTy, Option<String>>,
}

impl DisallowedType {
    pub fn new(conf_disallowed: Vec<conf::DisallowedType>) -> Self {
        Self {
            conf_disallowed,
            def_ids: FxHashMap::default(),
            prim_tys: FxHashMap::default(),
        }
    }

    fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) {
        match res {
            Res::Def(_, did) => {
                if let Some(reason) = self.def_ids.get(did) {
                    emit(cx, &cx.tcx.def_path_str(*did), span, reason.as_deref());
                }
            },
            Res::PrimTy(prim) => {
                if let Some(reason) = self.prim_tys.get(prim) {
                    emit(cx, prim.name_str(), span, reason.as_deref());
                }
            },
            _ => {},
        }
    }
}

impl_lint_pass!(DisallowedType => [DISALLOWED_TYPE]);

impl<'tcx> LateLintPass<'tcx> for DisallowedType {
    fn check_crate(&mut self, cx: &LateContext<'_>) {
        for conf in &self.conf_disallowed {
            let (path, reason) = match conf {
                conf::DisallowedType::Simple(path) => (path, None),
                conf::DisallowedType::WithReason { path, reason } => (
                    path,
                    reason.as_ref().map(|reason| format!("{} (from clippy.toml)", reason)),
                ),
            };
            let segs: Vec<_> = path.split("::").collect();
            match clippy_utils::path_to_res(cx, &segs) {
                Res::Def(_, id) => {
                    self.def_ids.insert(id, reason);
                },
                Res::PrimTy(ty) => {
                    self.prim_tys.insert(ty, reason);
                },
                _ => {},
            }
        }
    }

    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
        if let ItemKind::Use(path, UseKind::Single) = &item.kind {
            self.check_res_emit(cx, &path.res, item.span);
        }
    }

    fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
        if let TyKind::Path(path) = &ty.kind {
            self.check_res_emit(cx, &cx.qpath_res(path, ty.hir_id), ty.span);
        }
    }

    fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>, _: TraitBoundModifier) {
        self.check_res_emit(cx, &poly.trait_ref.path.res, poly.trait_ref.path.span);
    }
}

fn emit(cx: &LateContext<'_>, name: &str, span: Span, reason: Option<&str>) {
    span_lint_and_then(
        cx,
        DISALLOWED_TYPE,
        span,
        &format!("`{}` is not allowed according to config", name),
        |diag| {
            if let Some(reason) = reason {
                diag.note(reason);
            }
        },
    );
}
<|endoftext|>


<commit_before>use rustc_ast::ast::{Lit, LitFloatType, LitIntType, LitKind};
use std::iter;

#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Radix {
    Binary,
    Octal,
    Decimal,
    Hexadecimal,
}

impl Radix {
    /// Returns a reasonable digit group size for this radix.
    #[must_use]
    fn suggest_grouping(self) -> usize {
        match self {
            Self::Binary | Self::Hexadecimal => 4,
            Self::Octal | Self::Decimal => 3,
        }
    }
}

/// A helper method to format numeric literals with digit grouping.
/// `lit` must be a valid numeric literal without suffix.
pub fn format(lit: &str, type_suffix: Option<&str>, float: bool) -> String {
    NumericLiteral::new(lit, type_suffix, float).format()
}

#[derive(Debug)]
pub struct NumericLiteral<'a> {
    /// Which radix the literal was represented in.
    pub radix: Radix,
    /// The radix prefix, if present.
    pub prefix: Option<&'a str>,

    /// The integer part of the number.
    pub integer: &'a str,
    /// The fraction part of the number.
    pub fraction: Option<&'a str>,
    /// The exponent separator (b'e' or b'E') including preceding underscore if present
    /// and the exponent part.
    pub exponent: Option<(&'a str, &'a str)>,

    /// The type suffix, including preceding underscore if present.
    pub suffix: Option<&'a str>,
}

impl<'a> NumericLiteral<'a> {
    pub fn from_lit(src: &'a str, lit: &Lit) -> Option<NumericLiteral<'a>> {
        NumericLiteral::from_lit_kind(src, &lit.kind)
    }

    pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option<NumericLiteral<'a>> {
        if lit_kind.is_numeric() && src.chars().next().map_or(false, |c| c.is_digit(10)) {
            let (unsuffixed, suffix) = split_suffix(src, lit_kind);
            let float = matches!(lit_kind, LitKind::Float(..));
            Some(NumericLiteral::new(unsuffixed, suffix, float))
        } else {
            None
        }
    }

    #[must_use]
    pub fn new(lit: &'a str, suffix: Option<&'a str>, float: bool) -> Self {
        // Determine delimiter for radix prefix, if present, and radix.
        let radix = if lit.starts_with("0x") {
            Radix::Hexadecimal
        } else if lit.starts_with("0b") {
            Radix::Binary
        } else if lit.starts_with("0o") {
            Radix::Octal
        } else {
            Radix::Decimal
        };

        // Grab part of the literal after prefix, if present.
        let (prefix, mut sans_prefix) = if let Radix::Decimal = radix {
            (None, lit)
        } else {
            let (p, s) = lit.split_at(2);
            (Some(p), s)
        };

        if suffix.is_some() && sans_prefix.ends_with('_') {
            // The '_' before the suffix isn't part of the digits
            sans_prefix = &sans_prefix[..sans_prefix.len() - 1];
        }

        let (integer, fraction, exponent) = Self::split_digit_parts(sans_prefix, float);

        Self {
            radix,
            prefix,
            integer,
            fraction,
            exponent,
            suffix,
        }
    }

    pub fn is_decimal(&self) -> bool {
        self.radix == Radix::Decimal
    }

    pub fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(&str, &str)>) {
        let mut integer = digits;
        let mut fraction = None;
        let mut exponent = None;

        if float {
            for (i, c) in digits.char_indices() {
                match c {
                    '.' => {
                        integer = &digits[..i];
                        fraction = Some(&digits[i + 1..]);
                    },
                    'e' | 'E' => {
                        let exp_start = if digits[..i].ends_with('_') { i - 1 } else { i };

                        if integer.len() > exp_start {
                            integer = &digits[..exp_start];
                        } else {
                            fraction = Some(&digits[integer.len() + 1..exp_start]);
                        };
                        exponent = Some((&digits[exp_start..=i], &digits[i + 1..]));
                        break;
                    },
                    _ => {},
                }
            }
        }

        (integer, fraction, exponent)
    }

    /// Returns literal formatted in a sensible way.
    pub fn format(&self) -> String {
        let mut output = String::new();

        if let Some(prefix) = self.prefix {
            output.push_str(prefix);
        }

        let group_size = self.radix.suggest_grouping();

        Self::group_digits(
            &mut output,
            self.integer,
            group_size,
            true,
            self.radix == Radix::Hexadecimal,
        );

        if let Some(fraction) = self.fraction {
            output.push('.');
            Self::group_digits(&mut output, fraction, group_size, false, false);
        }

        if let Some((separator, exponent)) = self.exponent {
            output.push_str(separator);
            Self::group_digits(&mut output, exponent, group_size, true, false);
        }

        if let Some(suffix) = self.suffix {
            output.push('_');
            output.push_str(suffix);
        }

        output
    }

    pub fn group_digits(output: &mut String, input: &str, group_size: usize, partial_group_first: bool, pad: bool) {
        debug_assert!(group_size > 0);

        let mut digits = input.chars().filter(|&c| c != '_');

        let first_group_size;

        if partial_group_first {
            first_group_size = (digits.clone().count() - 1) % group_size + 1;
            if pad {
                for _ in 0..group_size - first_group_size {
                    output.push('0');
                }
            }
        } else {
            first_group_size = group_size;
        }

        for _ in 0..first_group_size {
            if let Some(digit) = digits.next() {
                output.push(digit);
            }
        }

        for (c, i) in iter::zip(digits, (0..group_size).cycle()) {
            if i == 0 {
                output.push('_');
            }
            output.push(c);
        }
    }
}

fn split_suffix<'a>(src: &'a str, lit_kind: &LitKind) -> (&'a str, Option<&'a str>) {
    debug_assert!(lit_kind.is_numeric());
    lit_suffix_length(lit_kind).map_or((src, None), |suffix_length| {
        let (unsuffixed, suffix) = src.split_at(src.len() - suffix_length);
        (unsuffixed, Some(suffix))
    })
}

fn lit_suffix_length(lit_kind: &LitKind) -> Option<usize> {
    debug_assert!(lit_kind.is_numeric());
    let suffix = match lit_kind {
        LitKind::Int(_, int_lit_kind) => match int_lit_kind {
            LitIntType::Signed(int_ty) => Some(int_ty.name_str()),
            LitIntType::Unsigned(uint_ty) => Some(uint_ty.name_str()),
            LitIntType::Unsuffixed => None,
        },
        LitKind::Float(_, float_lit_kind) => match float_lit_kind {
            LitFloatType::Suffixed(float_ty) => Some(float_ty.name_str()),
            LitFloatType::Unsuffixed => None,
        },
        _ => None,
    };

    suffix.map(str::len)
}
<commit_msg>Fix `NumericLiteral::format` that may produce a invalid literal<commit_after>use rustc_ast::ast::{Lit, LitFloatType, LitIntType, LitKind};
use std::iter;

#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Radix {
    Binary,
    Octal,
    Decimal,
    Hexadecimal,
}

impl Radix {
    /// Returns a reasonable digit group size for this radix.
    #[must_use]
    fn suggest_grouping(self) -> usize {
        match self {
            Self::Binary | Self::Hexadecimal => 4,
            Self::Octal | Self::Decimal => 3,
        }
    }
}

/// A helper method to format numeric literals with digit grouping.
/// `lit` must be a valid numeric literal without suffix.
pub fn format(lit: &str, type_suffix: Option<&str>, float: bool) -> String {
    NumericLiteral::new(lit, type_suffix, float).format()
}

#[derive(Debug)]
pub struct NumericLiteral<'a> {
    /// Which radix the literal was represented in.
    pub radix: Radix,
    /// The radix prefix, if present.
    pub prefix: Option<&'a str>,

    /// The integer part of the number.
    pub integer: &'a str,
    /// The fraction part of the number.
    pub fraction: Option<&'a str>,
    /// The exponent separator (b'e' or b'E') including preceding underscore if present
    /// and the exponent part.
    pub exponent: Option<(&'a str, &'a str)>,

    /// The type suffix, including preceding underscore if present.
    pub suffix: Option<&'a str>,
}

impl<'a> NumericLiteral<'a> {
    pub fn from_lit(src: &'a str, lit: &Lit) -> Option<NumericLiteral<'a>> {
        NumericLiteral::from_lit_kind(src, &lit.kind)
    }

    pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option<NumericLiteral<'a>> {
        if lit_kind.is_numeric() && src.chars().next().map_or(false, |c| c.is_digit(10)) {
            let (unsuffixed, suffix) = split_suffix(src, lit_kind);
            let float = matches!(lit_kind, LitKind::Float(..));
            Some(NumericLiteral::new(unsuffixed, suffix, float))
        } else {
            None
        }
    }

    #[must_use]
    pub fn new(lit: &'a str, suffix: Option<&'a str>, float: bool) -> Self {
        // Determine delimiter for radix prefix, if present, and radix.
        let radix = if lit.starts_with("0x") {
            Radix::Hexadecimal
        } else if lit.starts_with("0b") {
            Radix::Binary
        } else if lit.starts_with("0o") {
            Radix::Octal
        } else {
            Radix::Decimal
        };

        // Grab part of the literal after prefix, if present.
        let (prefix, mut sans_prefix) = if let Radix::Decimal = radix {
            (None, lit)
        } else {
            let (p, s) = lit.split_at(2);
            (Some(p), s)
        };

        if suffix.is_some() && sans_prefix.ends_with('_') {
            // The '_' before the suffix isn't part of the digits
            sans_prefix = &sans_prefix[..sans_prefix.len() - 1];
        }

        let (integer, fraction, exponent) = Self::split_digit_parts(sans_prefix, float);

        Self {
            radix,
            prefix,
            integer,
            fraction,
            exponent,
            suffix,
        }
    }

    pub fn is_decimal(&self) -> bool {
        self.radix == Radix::Decimal
    }

    pub fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(&str, &str)>) {
        let mut integer = digits;
        let mut fraction = None;
        let mut exponent = None;

        if float {
            for (i, c) in digits.char_indices() {
                match c {
                    '.' => {
                        integer = &digits[..i];
                        fraction = Some(&digits[i + 1..]);
                    },
                    'e' | 'E' => {
                        let exp_start = if digits[..i].ends_with('_') { i - 1 } else { i };

                        if integer.len() > exp_start {
                            integer = &digits[..exp_start];
                        } else {
                            fraction = Some(&digits[integer.len() + 1..exp_start]);
                        };
                        exponent = Some((&digits[exp_start..=i], &digits[i + 1..]));
                        break;
                    },
                    _ => {},
                }
            }
        }

        (integer, fraction, exponent)
    }

    /// Returns literal formatted in a sensible way.
    pub fn format(&self) -> String {
        let mut output = String::new();

        if let Some(prefix) = self.prefix {
            output.push_str(prefix);
        }

        let group_size = self.radix.suggest_grouping();

        Self::group_digits(
            &mut output,
            self.integer,
            group_size,
            true,
            self.radix == Radix::Hexadecimal,
        );

        if let Some(fraction) = self.fraction {
            output.push('.');
            Self::group_digits(&mut output, fraction, group_size, false, false);
        }

        if let Some((separator, exponent)) = self.exponent {
            output.push_str(separator);
            Self::group_digits(&mut output, exponent, group_size, true, false);
        }

        if let Some(suffix) = self.suffix {
            if output.ends_with('.') {
                output.push('0');
            }
            output.push('_');
            output.push_str(suffix);
        }

        output
    }

    pub fn group_digits(output: &mut String, input: &str, group_size: usize, partial_group_first: bool, pad: bool) {
        debug_assert!(group_size > 0);

        let mut digits = input.chars().filter(|&c| c != '_');

        let first_group_size;

        if partial_group_first {
            first_group_size = (digits.clone().count() - 1) % group_size + 1;
            if pad {
                for _ in 0..group_size - first_group_size {
                    output.push('0');
                }
            }
        } else {
            first_group_size = group_size;
        }

        for _ in 0..first_group_size {
            if let Some(digit) = digits.next() {
                output.push(digit);
            }
        }

        for (c, i) in iter::zip(digits, (0..group_size).cycle()) {
            if i == 0 {
                output.push('_');
            }
            output.push(c);
        }
    }
}

fn split_suffix<'a>(src: &'a str, lit_kind: &LitKind) -> (&'a str, Option<&'a str>) {
    debug_assert!(lit_kind.is_numeric());
    lit_suffix_length(lit_kind).map_or((src, None), |suffix_length| {
        let (unsuffixed, suffix) = src.split_at(src.len() - suffix_length);
        (unsuffixed, Some(suffix))
    })
}

fn lit_suffix_length(lit_kind: &LitKind) -> Option<usize> {
    debug_assert!(lit_kind.is_numeric());
    let suffix = match lit_kind {
        LitKind::Int(_, int_lit_kind) => match int_lit_kind {
            LitIntType::Signed(int_ty) => Some(int_ty.name_str()),
            LitIntType::Unsigned(uint_ty) => Some(uint_ty.name_str()),
            LitIntType::Unsuffixed => None,
        },
        LitKind::Float(_, float_lit_kind) => match float_lit_kind {
            LitFloatType::Suffixed(float_ty) => Some(float_ty.name_str()),
            LitFloatType::Unsuffixed => None,
        },
        _ => None,
    };

    suffix.map(str::len)
}
<|endoftext|>


<commit_before>use crate::core::compiler::{Compilation, CompileKind, Doctest, Metadata, Unit, UnitOutput};
use crate::core::shell::Verbosity;
use crate::core::{TargetKind, Workspace};
use crate::ops;
use crate::util::errors::CargoResult;
use crate::util::{add_path_args, CargoTestError, Config, Test};
use cargo_util::{ProcessBuilder, ProcessError};
use std::ffi::OsString;
use std::path::{Path, PathBuf};

pub struct TestOptions {
    pub compile_opts: ops::CompileOptions,
    pub no_run: bool,
    pub no_fail_fast: bool,
}

pub fn run_tests(
    ws: &Workspace<'_>,
    options: &TestOptions,
    test_args: &[&str],
) -> CargoResult<Option<CargoTestError>> {
    let compilation = compile_tests(ws, options)?;

    if options.no_run {
        let config = ws.config();
        let cwd = config.cwd();
        for UnitOutput {
            unit,
            path,
            script_meta,
        } in compilation.tests.iter()
        {
            let (exe_display, cmd) = cmd_builds(
                config,
                cwd,
                unit,
                path,
                script_meta,
                test_args,
                &compilation,
            )?;
            config
                .shell()
                .concise(|shell| shell.status("Executable", &exe_display))?;
            config
                .shell()
                .verbose(|shell| shell.status("Executable", &cmd))?;
        }
        return Ok(None);
    }
    let (test, mut errors) = run_unit_tests(ws.config(), options, test_args, &compilation)?;

    // If we have an error and want to fail fast, then return.
    if !errors.is_empty() && !options.no_fail_fast {
        return Ok(Some(CargoTestError::new(test, errors)));
    }

    let (doctest, docerrors) = run_doc_tests(ws, options, test_args, &compilation)?;
    let test = if docerrors.is_empty() { test } else { doctest };
    errors.extend(docerrors);
    if errors.is_empty() {
        Ok(None)
    } else {
        Ok(Some(CargoTestError::new(test, errors)))
    }
}

pub fn run_benches(
    ws: &Workspace<'_>,
    options: &TestOptions,
    args: &[&str],
) -> CargoResult<Option<CargoTestError>> {
    let compilation = compile_tests(ws, options)?;

    if options.no_run {
        let config = ws.config();
        let cwd = config.cwd();
        for UnitOutput {
            unit,
            path,
            script_meta,
        } in compilation.tests.iter()
        {
            let (exe_display, cmd) =
                cmd_builds(config, cwd, unit, path, script_meta, args, &compilation)?;
            config
                .shell()
                .concise(|shell| shell.status("Executable", &exe_display))?;
            config
                .shell()
                .verbose(|shell| shell.status("Executable", &cmd))?;
        }
        return Ok(None);
    }

    let mut args = args.to_vec();
    args.push("--bench");

    let (test, errors) = run_unit_tests(ws.config(), options, &args, &compilation)?;

    match errors.len() {
        0 => Ok(None),
        _ => Ok(Some(CargoTestError::new(test, errors))),
    }
}

fn compile_tests<'a>(ws: &Workspace<'a>, options: &TestOptions) -> CargoResult<Compilation<'a>> {
    let mut compilation = ops::compile(ws, &options.compile_opts)?;
    compilation.tests.sort();
    Ok(compilation)
}

/// Runs the unit and integration tests of a package.
fn run_unit_tests(
    config: &Config,
    options: &TestOptions,
    test_args: &[&str],
    compilation: &Compilation<'_>,
) -> CargoResult<(Test, Vec<ProcessError>)> {
    let cwd = config.cwd();
    let mut errors = Vec::new();

    for UnitOutput {
        unit,
        path,
        script_meta,
    } in compilation.tests.iter()
    {
        let _test = unit.target.name().to_string();

        let (exe_display, cmd) =
            cmd_builds(config, cwd, unit, path, script_meta, test_args, compilation)?;
        config
            .shell()
            .concise(|shell| shell.status("Running", &exe_display))?;
        config
            .shell()
            .verbose(|shell| shell.status("Running", &cmd))?;

        let result = cmd.exec();

        if let Err(e) = result {
            let e = e.downcast::<ProcessError>()?;
            errors.push((
                unit.target.kind().clone(),
                unit.target.name().to_string(),
                unit.pkg.name().to_string(),
                e,
            ));
            if !options.no_fail_fast {
                break;
            }
        }
    }

    if errors.len() == 1 {
        let (kind, name, pkg_name, e) = errors.pop().unwrap();
        Ok((
            Test::UnitTest {
                kind,
                name,
                pkg_name,
            },
            vec![e],
        ))
    } else {
        Ok((
            Test::Multiple,
            errors.into_iter().map(|(_, _, _, e)| e).collect(),
        ))
    }
}

fn cmd_builds(
    config: &Config,
    cwd: &Path,
    unit: &Unit,
    path: &PathBuf,
    script_meta: &Option<Metadata>,
    test_args: &[&str],
    compilation: &Compilation<'_>,
) -> CargoResult<(String, ProcessBuilder)> {
    let test_path = unit.target.src_path().path().unwrap();
    let exe_display = if let TargetKind::Test = unit.target.kind() {
        format!(
            "{} ({})",
            test_path
                .strip_prefix(unit.pkg.root())
                .unwrap_or(test_path)
                .display(),
            path.strip_prefix(cwd).unwrap_or(path).display()
        )
    } else {
        format!(
            "unittests ({})",
            path.strip_prefix(cwd).unwrap_or(path).display()
        )
    };

    let mut cmd = compilation.target_process(path, unit.kind, &unit.pkg, *script_meta)?;
    cmd.args(test_args);
    if unit.target.harness() && config.shell().verbosity() == Verbosity::Quiet {
        cmd.arg("--quiet");
    }

    Ok((exe_display, cmd))
}

fn run_doc_tests(
    ws: &Workspace<'_>,
    options: &TestOptions,
    test_args: &[&str],
    compilation: &Compilation<'_>,
) -> CargoResult<(Test, Vec<ProcessError>)> {
    let config = ws.config();
    let mut errors = Vec::new();
    let doctest_xcompile = config.cli_unstable().doctest_xcompile;
    let doctest_in_workspace = config.cli_unstable().doctest_in_workspace;

    for doctest_info in &compilation.to_doc_test {
        let Doctest {
            args,
            unstable_opts,
            unit,
            linker,
            script_meta,
        } = doctest_info;

        if !doctest_xcompile {
            match unit.kind {
                CompileKind::Host => {}
                CompileKind::Target(target) => {
                    if target.short_name() != compilation.host {
                        // Skip doctests, -Zdoctest-xcompile not enabled.
                        config.shell().verbose(|shell| {
                            shell.note(format!(
                                "skipping doctests for {} ({}), \
                                 cross-compilation doctests are not yet supported\n\
                                 See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#doctest-xcompile \
                                 for more information.",
                                unit.pkg,
                                unit.target.description_named()
                            ))
                        })?;
                        continue;
                    }
                }
            }
        }

        config.shell().status("Doc-tests", unit.target.name())?;
        let mut p = compilation.rustdoc_process(unit, *script_meta)?;
        p.arg("--crate-name").arg(&unit.target.crate_name());
        p.arg("--test");

        if doctest_in_workspace {
            add_path_args(ws, unit, &mut p);
            // FIXME(swatinem): remove the `unstable-options` once rustdoc stabilizes the `test-run-directory` option
            p.arg("-Z").arg("unstable-options");
            p.arg("--test-run-directory")
                .arg(unit.pkg.root().to_path_buf());
        } else {
            p.arg(unit.target.src_path().path().unwrap());
        }

        if doctest_xcompile {
            if let CompileKind::Target(target) = unit.kind {
                // use `rustc_target()` to properly handle JSON target paths
                p.arg("--target").arg(target.rustc_target());
            }
            p.arg("-Zunstable-options");
            p.arg("--enable-per-target-ignores");
            if let Some((runtool, runtool_args)) = compilation.target_runner(unit.kind) {
                p.arg("--runtool").arg(runtool);
                for arg in runtool_args {
                    p.arg("--runtool-arg").arg(arg);
                }
            }
            if let Some(linker) = linker {
                let mut joined = OsString::from("linker=");
                joined.push(linker);
                p.arg("-C").arg(joined);
            }
        }

        for &rust_dep in &[
            &compilation.deps_output[&unit.kind],
            &compilation.deps_output[&CompileKind::Host],
        ] {
            let mut arg = OsString::from("dependency=");
            arg.push(rust_dep);
            p.arg("-L").arg(arg);
        }

        for native_dep in compilation.native_dirs.iter() {
            p.arg("-L").arg(native_dep);
        }

        for arg in test_args {
            p.arg("--test-args").arg(arg);
        }

        if config.shell().verbosity() == Verbosity::Quiet {
            p.arg("--test-args").arg("--quiet");
        }

        p.args(args);

        if *unstable_opts {
            p.arg("-Zunstable-options");
        }

        config
            .shell()
            .verbose(|shell| shell.status("Running", p.to_string()))?;
        if let Err(e) = p.exec() {
            let e = e.downcast::<ProcessError>()?;
            errors.push(e);
            if !options.no_fail_fast {
                return Ok((Test::Doc, errors));
            }
        }
    }
    Ok((Test::Doc, errors))
}
<commit_msg>extract display_no_run_information<commit_after>use crate::core::compiler::{Compilation, CompileKind, Doctest, Metadata, Unit, UnitOutput};
use crate::core::shell::Verbosity;
use crate::core::{TargetKind, Workspace};
use crate::ops;
use crate::util::errors::CargoResult;
use crate::util::{add_path_args, CargoTestError, Config, Test};
use cargo_util::{ProcessBuilder, ProcessError};
use std::ffi::OsString;
use std::path::{Path, PathBuf};

pub struct TestOptions {
    pub compile_opts: ops::CompileOptions,
    pub no_run: bool,
    pub no_fail_fast: bool,
}

pub fn run_tests(
    ws: &Workspace<'_>,
    options: &TestOptions,
    test_args: &[&str],
) -> CargoResult<Option<CargoTestError>> {
    let compilation = compile_tests(ws, options)?;

    if options.no_run {
        display_no_run_information(ws, test_args, &compilation)?;
        return Ok(None);
    }
    let (test, mut errors) = run_unit_tests(ws.config(), options, test_args, &compilation)?;

    // If we have an error and want to fail fast, then return.
    if !errors.is_empty() && !options.no_fail_fast {
        return Ok(Some(CargoTestError::new(test, errors)));
    }

    let (doctest, docerrors) = run_doc_tests(ws, options, test_args, &compilation)?;
    let test = if docerrors.is_empty() { test } else { doctest };
    errors.extend(docerrors);
    if errors.is_empty() {
        Ok(None)
    } else {
        Ok(Some(CargoTestError::new(test, errors)))
    }
}

pub fn run_benches(
    ws: &Workspace<'_>,
    options: &TestOptions,
    args: &[&str],
) -> CargoResult<Option<CargoTestError>> {
    let compilation = compile_tests(ws, options)?;

    if options.no_run {
        display_no_run_information(ws, args, &compilation)?;
        return Ok(None);
    }

    let mut args = args.to_vec();
    args.push("--bench");

    let (test, errors) = run_unit_tests(ws.config(), options, &args, &compilation)?;

    match errors.len() {
        0 => Ok(None),
        _ => Ok(Some(CargoTestError::new(test, errors))),
    }
}

fn compile_tests<'a>(ws: &Workspace<'a>, options: &TestOptions) -> CargoResult<Compilation<'a>> {
    let mut compilation = ops::compile(ws, &options.compile_opts)?;
    compilation.tests.sort();
    Ok(compilation)
}

/// Runs the unit and integration tests of a package.
fn run_unit_tests(
    config: &Config,
    options: &TestOptions,
    test_args: &[&str],
    compilation: &Compilation<'_>,
) -> CargoResult<(Test, Vec<ProcessError>)> {
    let cwd = config.cwd();
    let mut errors = Vec::new();

    for UnitOutput {
        unit,
        path,
        script_meta,
    } in compilation.tests.iter()
    {
        let (exe_display, cmd) =
            cmd_builds(config, cwd, unit, path, script_meta, test_args, compilation)?;
        config
            .shell()
            .concise(|shell| shell.status("Running", &exe_display))?;
        config
            .shell()
            .verbose(|shell| shell.status("Running", &cmd))?;

        let result = cmd.exec();

        if let Err(e) = result {
            let e = e.downcast::<ProcessError>()?;
            errors.push((
                unit.target.kind().clone(),
                unit.target.name().to_string(),
                unit.pkg.name().to_string(),
                e,
            ));
            if !options.no_fail_fast {
                break;
            }
        }
    }

    if errors.len() == 1 {
        let (kind, name, pkg_name, e) = errors.pop().unwrap();
        Ok((
            Test::UnitTest {
                kind,
                name,
                pkg_name,
            },
            vec![e],
        ))
    } else {
        Ok((
            Test::Multiple,
            errors.into_iter().map(|(_, _, _, e)| e).collect(),
        ))
    }
}

fn run_doc_tests(
    ws: &Workspace<'_>,
    options: &TestOptions,
    test_args: &[&str],
    compilation: &Compilation<'_>,
) -> CargoResult<(Test, Vec<ProcessError>)> {
    let config = ws.config();
    let mut errors = Vec::new();
    let doctest_xcompile = config.cli_unstable().doctest_xcompile;
    let doctest_in_workspace = config.cli_unstable().doctest_in_workspace;

    for doctest_info in &compilation.to_doc_test {
        let Doctest {
            args,
            unstable_opts,
            unit,
            linker,
            script_meta,
        } = doctest_info;

        if !doctest_xcompile {
            match unit.kind {
                CompileKind::Host => {}
                CompileKind::Target(target) => {
                    if target.short_name() != compilation.host {
                        // Skip doctests, -Zdoctest-xcompile not enabled.
                        config.shell().verbose(|shell| {
                            shell.note(format!(
                                "skipping doctests for {} ({}), \
                                 cross-compilation doctests are not yet supported\n\
                                 See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#doctest-xcompile \
                                 for more information.",
                                unit.pkg,
                                unit.target.description_named()
                            ))
                        })?;
                        continue;
                    }
                }
            }
        }

        config.shell().status("Doc-tests", unit.target.name())?;
        let mut p = compilation.rustdoc_process(unit, *script_meta)?;
        p.arg("--crate-name").arg(&unit.target.crate_name());
        p.arg("--test");

        if doctest_in_workspace {
            add_path_args(ws, unit, &mut p);
            // FIXME(swatinem): remove the `unstable-options` once rustdoc stabilizes the `test-run-directory` option
            p.arg("-Z").arg("unstable-options");
            p.arg("--test-run-directory")
                .arg(unit.pkg.root().to_path_buf());
        } else {
            p.arg(unit.target.src_path().path().unwrap());
        }

        if doctest_xcompile {
            if let CompileKind::Target(target) = unit.kind {
                // use `rustc_target()` to properly handle JSON target paths
                p.arg("--target").arg(target.rustc_target());
            }
            p.arg("-Zunstable-options");
            p.arg("--enable-per-target-ignores");
            if let Some((runtool, runtool_args)) = compilation.target_runner(unit.kind) {
                p.arg("--runtool").arg(runtool);
                for arg in runtool_args {
                    p.arg("--runtool-arg").arg(arg);
                }
            }
            if let Some(linker) = linker {
                let mut joined = OsString::from("linker=");
                joined.push(linker);
                p.arg("-C").arg(joined);
            }
        }

        for &rust_dep in &[
            &compilation.deps_output[&unit.kind],
            &compilation.deps_output[&CompileKind::Host],
        ] {
            let mut arg = OsString::from("dependency=");
            arg.push(rust_dep);
            p.arg("-L").arg(arg);
        }

        for native_dep in compilation.native_dirs.iter() {
            p.arg("-L").arg(native_dep);
        }

        for arg in test_args {
            p.arg("--test-args").arg(arg);
        }

        if config.shell().verbosity() == Verbosity::Quiet {
            p.arg("--test-args").arg("--quiet");
        }

        p.args(args);

        if *unstable_opts {
            p.arg("-Zunstable-options");
        }

        config
            .shell()
            .verbose(|shell| shell.status("Running", p.to_string()))?;
        if let Err(e) = p.exec() {
            let e = e.downcast::<ProcessError>()?;
            errors.push(e);
            if !options.no_fail_fast {
                return Ok((Test::Doc, errors));
            }
        }
    }
    Ok((Test::Doc, errors))
}

fn display_no_run_information(
    ws: &Workspace<'_>,
    test_args: &[&str],
    compilation: &Compilation<'_>,
) -> CargoResult<()> {
    let config = ws.config();
    let cwd = config.cwd();
    for UnitOutput {
        unit,
        path,
        script_meta,
    } in compilation.tests.iter()
    {
        let (exe_display, cmd) = cmd_builds(
            config,
            cwd,
            unit,
            path,
            script_meta,
            test_args,
            &compilation,
        )?;
        config
            .shell()
            .concise(|shell| shell.status("Executable", &exe_display))?;
        config
            .shell()
            .verbose(|shell| shell.status("Executable", &cmd))?;
    }

    return Ok(());
}

fn cmd_builds(
    config: &Config,
    cwd: &Path,
    unit: &Unit,
    path: &PathBuf,
    script_meta: &Option<Metadata>,
    test_args: &[&str],
    compilation: &Compilation<'_>,
) -> CargoResult<(String, ProcessBuilder)> {
    let test_path = unit.target.src_path().path().unwrap();
    let exe_display = if let TargetKind::Test = unit.target.kind() {
        format!(
            "{} ({})",
            test_path
                .strip_prefix(unit.pkg.root())
                .unwrap_or(test_path)
                .display(),
            path.strip_prefix(cwd).unwrap_or(path).display()
        )
    } else {
        format!(
            "unittests ({})",
            path.strip_prefix(cwd).unwrap_or(path).display()
        )
    };

    let mut cmd = compilation.target_process(path, unit.kind, &unit.pkg, *script_meta)?;
    cmd.args(test_args);
    if unit.target.harness() && config.shell().verbosity() == Verbosity::Quiet {
        cmd.arg("--quiet");
    }

    Ok((exe_display, cmd))
}
<|endoftext|>