<commit_before>extern crate csv;
extern crate rustc_serialize;

use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};

//////////////////////////////DISTANCES////////////////////////////////
fn manhattan_dist(x: &Vec<f32>, y: &Vec<f32>) -> f32 {
    if x.len() != y.len() {
        panic!("Should compare vectors of same size");
    }
    let mut distance = 0.0;

    for i in 0..x.len() {
        if x[i] > 0.0 && y[i] > 0.0 {
            distance += (x[i] - y[i]).abs();
        }
    }

    distance
}
///////////////////////////////////////////////////////////////////////

struct IndexedDB(HashMap<String, HashMap<String, f32>>, HashMap<String, HashMap<String, f32>>);

fn load_db(path: &str) -> IndexedDB {
    let mut rdr = csv::Reader::from_file(path).unwrap().has_headers(false);
    let mut ratings: HashMap<String, HashMap<String, f32>> = HashMap::new();
    let mut features: HashMap<String, HashMap<String, f32>> = HashMap::new();

    for record in rdr.decode() {
        let (user_id, feat_id, rating): (String, String, f32) = record.unwrap();
        let (user_id2, feat_id2, rating2) = (user_id.clone(), feat_id.clone(), rating);

        match ratings.entry(user_id) {
            Vacant(entry) => {
                let mut user_ratings = HashMap::new();
                user_ratings.insert(feat_id, rating);
                entry.insert(user_ratings);
            }
            Occupied(entry) => {
                entry.into_mut().insert(feat_id, rating);
            }
        }

        match features.entry(feat_id2) {
            Vacant(entry) => {
                let mut movie_ratings = HashMap::new();
                movie_ratings.insert(user_id2, rating2);
                entry.insert(movie_ratings);
            }
            Occupied(entry) => {
                entry.into_mut().insert(user_id2, rating2);
            }
        }
    }

    IndexedDB(ratings, features)
}

fn median(vec: &Vec<f32>) -> f32 {
    let mut vec_cpy = vec.clone();
    vec_cpy.sort_by(|a, b| a.partial_cmp(b).unwrap());
    let length = vec_cpy.len();
    let mid = length / 2;
    let mut ret = vec_cpy[mid];
    if length % 2 == 0 {
        ret += vec_cpy[mid - 1];
        ret /= 2.0;
    }
    println!("{} /2 -> {}", length, length / 2);
    ret
}

fn abs_standard_deviation(vec: &Vec<f32>) -> (f32, f32) {
    let median = median(vec);
    let mut asd = 0.0;

    for x in vec.iter() {
        asd += (x - median).abs();
    }

    (asd / vec.len() as f32, median)
}

fn mod_standard_score(val: f32, vec: &Vec<f32>) -> f32 {
    let (asd, median) = abs_standard_deviation(vec);

    (val - median) / asd
}

// fn user_rating_vector(db: &IndexedDB, id: &str) -> Vec<f32> {
//     let mut ret_vec = Vec::new();
//     if let Some(ref ratings) = db.0.get(&String::from(id)) {
//         for (_, &rating) in ratings.iter() {
//             ret_vec.push(rating);
//         }
//     }

//     ret_vec
// }

fn users_rating_vectors(db: &IndexedDB, id1: &str, id2: &str) -> (Vec<f32>, Vec<f32>) {
    let mut usr1_vec: Vec<f32> = Vec::new();
    let mut usr2_vec: Vec<f32> = Vec::new();

    if let Some(ref ratings1) = db.0.get(&String::from(id1)) {
        for (feat1_id, &rating1) in ratings1.iter() {
            usr1_vec.push(rating1);
            if let Some(ref ratings2) = db.0.get(&String::from(id2)) {
                if let Some(rating2) = ratings2.get(feat1_id) {
                    usr2_vec.push(*rating2);
                } else {
                    usr2_vec.push(0.0);
                }
            } else {
                panic!("user2 is not found!");
            }
        }
    } else {
        panic!("user1 not found!");
    }

    if let Some(ref ratings2) = db.0.get(&String::from(id2)) {
        for (feat2_id, &rating2) in ratings2.iter() {
            if let Some(ref ratings1) = db.0.get(&String::from(id1)) {
                if let Some(_) = ratings1.get(feat2_id) {
                } else {
                    usr2_vec.push(rating2);
                    usr1_vec.push(0.0);
                }
            } else {
                panic!("user2 is not found!");
            }
        }
    } else {
        panic!("user1 not found!");
    }

    (usr1_vec, usr2_vec)
}

fn nearest_neighbors(db: &IndexedDB,
                     id: &str,
                     func: fn(&Vec<f32>, &Vec<f32>) -> f32)
                     -> Vec<(f32, String)> {
    // let obj_vec = user_rating_vector(db, id);
    let mut dist_vec: Vec<(f32, String)> = Vec::new();

    // println!("OBJ: {:?}", obj_vec);

    for (rec_id, _) in db.0.iter() {
        if id != rec_id.as_str() {
            let rec_str = rec_id.clone();
            let (obj_vec, rec_vec) = users_rating_vectors(db, id, rec_id);
            println!("{:?}", rec_vec);
            println!("{} -> {}", rec_str, func(&obj_vec, &rec_vec));
            dist_vec.push((func(&obj_vec, &rec_vec), rec_str));
        }
    }

    dist_vec.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
    dist_vec
}

fn main() {
    println!("Loading database, please wait...");
    let db = load_db("./data/music.csv");
    println!("Database ready!\n---------------------------------------------");

    println!("{:?}", nearest_neighbors(&db, "Dr Dog/Fate", manhattan_dist));

    // println!("Med: {:?}", abs_standard_deviation(&mut vec![43., 45., 55., 69., 70., 75., 105., 115.]));
}
<commit_msg>Mod SS working<commit_after>extern crate csv;
extern crate rustc_serialize;

use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};

//////////////////////////////DISTANCES////////////////////////////////
fn manhattan_dist(x: &Vec<f32>, y: &Vec<f32>) -> f32 {
    if x.len() != y.len() {
        panic!("Should compare vectors of same size");
    }
    let mut distance = 0.0;

    for i in 0..x.len() {
        if x[i] > 0.0 && y[i] > 0.0 {
            distance += (x[i] - y[i]).abs();
        }
    }

    distance
}
///////////////////////////////////////////////////////////////////////

struct IndexedDB(HashMap<String, HashMap<String, f32>>, HashMap<String, HashMap<String, f32>>);

fn load_db(path: &str) -> IndexedDB {
    let mut rdr = csv::Reader::from_file(path).unwrap().has_headers(false);
    let mut ratings: HashMap<String, HashMap<String, f32>> = HashMap::new();
    let mut features: HashMap<String, HashMap<String, f32>> = HashMap::new();

    for record in rdr.decode() {
        let (user_id, feat_id, rating): (String, String, f32) = record.unwrap();
        let (user_id2, feat_id2, rating2) = (user_id.clone(), feat_id.clone(), rating);

        match ratings.entry(user_id) {
            Vacant(entry) => {
                let mut user_ratings = HashMap::new();
                user_ratings.insert(feat_id, rating);
                entry.insert(user_ratings);
            }
            Occupied(entry) => {
                entry.into_mut().insert(feat_id, rating);
            }
        }

        match features.entry(feat_id2) {
            Vacant(entry) => {
                let mut movie_ratings = HashMap::new();
                movie_ratings.insert(user_id2, rating2);
                entry.insert(movie_ratings);
            }
            Occupied(entry) => {
                entry.into_mut().insert(user_id2, rating2);
            }
        }
    }

    IndexedDB(ratings, features)
}

fn median(vec: &Vec<f32>) -> f32 {
    let mut vec_cpy = vec.clone();
    vec_cpy.sort_by(|a, b| a.partial_cmp(b).unwrap());
    let length = vec_cpy.len();
    let mid = length / 2;
    let mut ret = vec_cpy[mid];
    if length % 2 == 0 {
        ret += vec_cpy[mid - 1];
        ret /= 2.0;
    }
    // println!("{} /2 -> {}", length, length / 2);
    ret
}

fn abs_standard_deviation(vec: &Vec<f32>) -> (f32, f32) {
    let median = median(vec);
    let mut asd = 0.0;

    for x in vec.iter() {
        asd += (x - median).abs();
    }

    (asd / vec.len() as f32, median)
}

// fn mod_standard_score(val: f32, vec: &Vec<f32>) -> f32 {
//     let (asd, median) = abs_standard_deviation(vec);

//     (val - median) / asd
// }

fn mod_standard_score(vec: &Vec<f32>) -> Vec<f32> {
    let (asd, median) = abs_standard_deviation(vec);
    let mut stnzd_vec = Vec::new();

    for val in vec.iter() {
        stnzd_vec.push((val - median) / asd);
    }

    stnzd_vec
}

fn users_rating_vectors(db: &IndexedDB, id1: &str, id2: &str) -> (Vec<f32>, Vec<f32>) {
    let mut usr1_vec: Vec<f32> = Vec::new();
    let mut usr2_vec: Vec<f32> = Vec::new();

    if let Some(ref ratings1) = db.0.get(&String::from(id1)) {
        for (feat1_id, &rating1) in ratings1.iter() {
            usr1_vec.push(rating1);
            if let Some(ref ratings2) = db.0.get(&String::from(id2)) {
                if let Some(rating2) = ratings2.get(feat1_id) {
                    usr2_vec.push(*rating2);
                } else {
                    usr2_vec.push(0.0);
                }
            } else {
                panic!("user2 is not found!");
            }
        }
    } else {
        panic!("user1 not found!");
    }

    if let Some(ref ratings2) = db.0.get(&String::from(id2)) {
        for (feat2_id, &rating2) in ratings2.iter() {
            if let Some(ref ratings1) = db.0.get(&String::from(id1)) {
                if let Some(_) = ratings1.get(feat2_id) {
                } else {
                    usr2_vec.push(rating2);
                    usr1_vec.push(0.0);
                }
            } else {
                panic!("user2 is not found!");
            }
        }
    } else {
        panic!("user1 not found!");
    }

    (usr1_vec, usr2_vec)
}

fn nearest_neighbors(db: &IndexedDB,
                     id: &str,
                     func: fn(&Vec<f32>, &Vec<f32>) -> f32)
                     -> Vec<(f32, String)> {
    // let obj_vec = user_rating_vector(db, id);
    let mut dist_vec: Vec<(f32, String)> = Vec::new();

    // println!("OBJ: {:?}", obj_vec);

    for (rec_id, _) in db.0.iter() {
        if id != rec_id.as_str() {
            let rec_str = rec_id.clone();
            let (obj_vec, rec_vec) = users_rating_vectors(db, id, rec_id);
            // println!("{:?}\n{:?}", obj_vec, rec_vec);
            // println!("{} -> {}", rec_str, func(&obj_vec, &rec_vec));
            dist_vec.push((func(&obj_vec, &rec_vec), rec_str));
        }
    }

    dist_vec.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
    dist_vec
}

fn main() {
    println!("Loading database, please wait...");
    let db = load_db("./data/music.csv");
    println!("Database ready!\n---------------------------------------------");

    println!("{:?}",
             nearest_neighbors(&db, "Dr Dog/Fate", manhattan_dist));

    println!("Med: {:?}",
             abs_standard_deviation(&mut vec![43., 45., 55., 69., 70., 75., 105., 115.]));

    println!("{:?}",
             mod_standard_score(&vec![43., 45., 55., 69., 70., 75., 105., 115.]));
}
<|endoftext|>


<commit_before>use core::num::{Zero, One};
use numeric::*;

use std::cmp::FuzzyEq;

use mat::{Mat4, BaseMat4};

/**
 * Create a perspective projection matrix
 *
 * This is the equivalent of the gluPerspective function, the algorithm of which
 * can be found [here](http://www.opengl.org/wiki/GluPerspective_code).
 */
#[inline(always)]
pub fn perspective<T:Copy + Float + Zero + One + FuzzyEq<T> + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T>>(fovy: T, aspectRatio: T, near: T, far: T) -> Mat4<T> {
    let ymax = near * tan(radians(fovy));
    let xmax = ymax * aspectRatio;

    frustum(-xmax, xmax, -ymax, ymax, near, far)
}

/**
 * Define a view frustrum
 *
 * This is the equivalent of the now deprecated [glFrustrum]
 * (http://www.opengl.org/sdk/docs/man2/xhtml/glFrustum.xml) function.
 */
#[inline(always)]
pub fn frustum<T:Copy + Float + Zero + One + FuzzyEq<T> + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T>>(left: T, right: T, bottom: T, top: T, near: T, far: T) -> Mat4<T> {
    let _0: T = num::cast(0);
    let _1: T = num::cast(1);
    let _2: T = num::cast(2);

    let c0r0 = (_2 * near) / (right - left);
    let c0r1 = _0;
    let c0r2 = _0;
    let c0r3 = _0;

    let c1r0 = _0;
    let c1r1 = (_2 * near) / (top - bottom);
    let c1r2 = _0;
    let c1r3 = _0;

    let c2r0 = (right + left) / (right - left);
    let c2r1 = (top + bottom) / (top - bottom);
    let c2r2 = -(far + near) / (far - near);
    let c2r3 = -_1;

    let c3r0 = _0;
    let c3r1 = _0;
    let c3r2 = -(_2 * far * near) / (far - near);
    let c3r3 = _0;

    BaseMat4::new(c0r0, c0r1, c0r2, c0r3,
                  c1r0, c1r1, c1r2, c1r3,
                  c2r0, c2r1, c2r2, c2r3,
                  c3r0, c3r1, c3r2, c3r3)
}<commit_msg>Fix projection::perspective.<commit_after>use core::num::{Zero, One};
use numeric::*;

use std::cmp::FuzzyEq;

use mat::{Mat4, BaseMat4};

/**
 * Create a perspective projection matrix
 *
 * Note: the fovy parameter should be specified in degrees.
 *
 * This is the equivalent of the gluPerspective function, the algorithm of which
 * can be found [here](http://www.opengl.org/wiki/GluPerspective_code).
 */
#[inline(always)]
pub fn perspective<T:Copy + Float + Zero + One + FuzzyEq<T> + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T>>(fovy: T, aspectRatio: T, near: T, far: T) -> Mat4<T> {
    let _2: T = num::cast(2);

    let ymax = near * tan(radians(fovy / _2));
    let xmax = ymax * aspectRatio;

    frustum(-xmax, xmax, -ymax, ymax, near, far)
}

/**
 * Define a view frustrum
 *
 * This is the equivalent of the now deprecated [glFrustrum]
 * (http://www.opengl.org/sdk/docs/man2/xhtml/glFrustum.xml) function.
 */
#[inline(always)]
pub fn frustum<T:Copy + Float + Zero + One + FuzzyEq<T> + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T>>(left: T, right: T, bottom: T, top: T, near: T, far: T) -> Mat4<T> {
    let _0: T = num::cast(0);
    let _1: T = num::cast(1);
    let _2: T = num::cast(2);

    let c0r0 = (_2 * near) / (right - left);
    let c0r1 = _0;
    let c0r2 = _0;
    let c0r3 = _0;

    let c1r0 = _0;
    let c1r1 = (_2 * near) / (top - bottom);
    let c1r2 = _0;
    let c1r3 = _0;

    let c2r0 = (right + left) / (right - left);
    let c2r1 = (top + bottom) / (top - bottom);
    let c2r2 = -(far + near) / (far - near);
    let c2r3 = -_1;

    let c3r0 = _0;
    let c3r1 = _0;
    let c3r2 = -(_2 * far * near) / (far - near);
    let c3r3 = _0;

    BaseMat4::new(c0r0, c0r1, c0r2, c0r3,
                  c1r0, c1r1, c1r2, c1r3,
                  c2r0, c2r1, c2r2, c2r3,
                  c3r0, c3r1, c3r2, c3r3)
}<|endoftext|>


<commit_before>mod request;
mod protocol;
mod context;
mod parser;
mod body;
mod response;

use netbuf::Buf;
use hyper::method::Method::Head;
use hyper::version::HttpVersion as Version;

use self::response::{NOT_IMPLEMENTED_HEAD, NOT_IMPLEMENTED};

pub use self::request::Head;
pub use self::context::Context;
pub use self::protocol::{RecvMode, Server};
pub use self::parser::Parser;

// TODO(tailhook) MAX_HEADERS_SIZE can be moved to Context
// (i.e. made non-constant), but it's more of a problem for MAX_HEADERS_NUM
// because that would mean we can't allocate array of headers on the stack
// so performance will degrade. Customizing MAX_HEADERS_SIZE is not very
// useful on it's own

/// Note httparse requires we preallocate array of this size so be wise
pub const MAX_HEADERS_NUM: usize = 256;
/// This one is not preallocated, but too large buffer is of limited use
/// because of previous parameter.
pub const MAX_HEADERS_SIZE: usize = 16384;
/// Maximum length of chunk size line. it would be okay with 12 bytes, but in
/// theory there might be some extensions which we probably should skip
///
/// Note: we don't have a limit on chunk body size. In buffered request mode
/// it's limited by either memory or artificial limit returned from handler.
/// In unbuffered mode we can process chunk of unlimited size as long as
/// request handler is able to handle it.
pub const MAX_CHUNK_HEAD: usize = 128;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ResponseBody {
    Normal,
    Ignored,  // HEAD requests, 304 responses
    Denied,  // 101, 204 responses (100 too if it is used here)
}

#[derive(Debug)]
enum ResponseImpl {
    /// Nothing has been sent
    Start { version: Version, body: ResponseBody },
    /// Status line is already in the buffer
    Headers { body: ResponseBody,
              content_length: Option<u64>, chunked: bool },
    ZeroBodyResponse,  // When response body is Denied
    IgnoredBody, // When response body is Ignored
    FixedSizeBody(u64),
    ChunkedBody,
    Done,
}

pub struct Response<'a>(&'a mut Buf, ResponseImpl);

impl ResponseImpl {
    fn with<'x>(self, out_buf: &'x mut Buf) -> Response<'x> {
        Response(out_buf, self)
    }
}

impl<'a> Response<'a> {
    fn internal(self) -> ResponseImpl {
        self.1
    }

    /// This is used for error pages, where it's impossible to parse input
    /// headers (i.e. get Head object needed for `Response::new`)
    fn simple<'x>(out_buf: &'x mut Buf, is_head: bool) -> Response<'x>
    {
        use self::ResponseBody::*;
        Response(out_buf, ResponseImpl::Start {
            body: if is_head { Ignored } else { Normal },
            // Always assume HTTP/1.0 when version is unknown
            version: Version::Http10,
        })
    }

    /// Creates new response by extracting needed fields from Head
    fn new<'x>(out_buf: &'x mut Buf, head: &Head) -> Response<'x>
    {
        use self::ResponseBody::*;
        // TODO(tailhook) implement Connection: Close,
        // (including explicit one in HTTP/1.0) and maybe others
        Response(out_buf, ResponseImpl::Start {
            body: if head.method == Head { Ignored } else { Normal },
            version: head.version,
        })
    }

    /// Returns true if it's okay too proceed with keep-alive connection
    fn finish(self) -> bool {
        use self::ResponseImpl::*;
        use self::ResponseBody::*;
        if self.is_complete() {
            return true;
        }
        let Response(buf, me) = self;
        match me {
            // If response is not even started yet, send something to make
            // debugging easier
            Start { body: Denied, .. } | Start { body: Ignored, .. } => {
                buf.extend(NOT_IMPLEMENTED_HEAD.as_bytes());
            }
            Start { body: Normal, .. } => {
                buf.extend(NOT_IMPLEMENTED.as_bytes());
            }
            _ => {}
        }
        return false;
    }
}
<commit_msg>Changed the order of fields to make state machine 8 bytes smaller<commit_after>mod request;
mod protocol;
mod context;
mod parser;
mod body;
mod response;

use netbuf::Buf;
use hyper::method::Method::Head;
use hyper::version::HttpVersion as Version;

use self::response::{NOT_IMPLEMENTED_HEAD, NOT_IMPLEMENTED};

pub use self::request::Head;
pub use self::context::Context;
pub use self::protocol::{RecvMode, Server};
pub use self::parser::Parser;

// TODO(tailhook) MAX_HEADERS_SIZE can be moved to Context
// (i.e. made non-constant), but it's more of a problem for MAX_HEADERS_NUM
// because that would mean we can't allocate array of headers on the stack
// so performance will degrade. Customizing MAX_HEADERS_SIZE is not very
// useful on it's own

/// Note httparse requires we preallocate array of this size so be wise
pub const MAX_HEADERS_NUM: usize = 256;
/// This one is not preallocated, but too large buffer is of limited use
/// because of previous parameter.
pub const MAX_HEADERS_SIZE: usize = 16384;
/// Maximum length of chunk size line. it would be okay with 12 bytes, but in
/// theory there might be some extensions which we probably should skip
///
/// Note: we don't have a limit on chunk body size. In buffered request mode
/// it's limited by either memory or artificial limit returned from handler.
/// In unbuffered mode we can process chunk of unlimited size as long as
/// request handler is able to handle it.
pub const MAX_CHUNK_HEAD: usize = 128;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ResponseBody {
    Normal,
    Ignored,  // HEAD requests, 304 responses
    Denied,  // 101, 204 responses (100 too if it is used here)
}

#[derive(Debug)]
enum ResponseImpl {
    /// Nothing has been sent
    Start { version: Version, body: ResponseBody },
    /// Status line is already in the buffer
    Headers { body: ResponseBody, chunked: bool,
              content_length: Option<u64> },
    ZeroBodyResponse,  // When response body is Denied
    IgnoredBody, // When response body is Ignored
    FixedSizeBody(u64),
    ChunkedBody,
    Done,
}

pub struct Response<'a>(&'a mut Buf, ResponseImpl);

impl ResponseImpl {
    fn with<'x>(self, out_buf: &'x mut Buf) -> Response<'x> {
        Response(out_buf, self)
    }
}

impl<'a> Response<'a> {
    fn internal(self) -> ResponseImpl {
        self.1
    }

    /// This is used for error pages, where it's impossible to parse input
    /// headers (i.e. get Head object needed for `Response::new`)
    fn simple<'x>(out_buf: &'x mut Buf, is_head: bool) -> Response<'x>
    {
        use self::ResponseBody::*;
        Response(out_buf, ResponseImpl::Start {
            body: if is_head { Ignored } else { Normal },
            // Always assume HTTP/1.0 when version is unknown
            version: Version::Http10,
        })
    }

    /// Creates new response by extracting needed fields from Head
    fn new<'x>(out_buf: &'x mut Buf, head: &Head) -> Response<'x>
    {
        use self::ResponseBody::*;
        // TODO(tailhook) implement Connection: Close,
        // (including explicit one in HTTP/1.0) and maybe others
        Response(out_buf, ResponseImpl::Start {
            body: if head.method == Head { Ignored } else { Normal },
            version: head.version,
        })
    }

    /// Returns true if it's okay too proceed with keep-alive connection
    fn finish(self) -> bool {
        use self::ResponseImpl::*;
        use self::ResponseBody::*;
        if self.is_complete() {
            return true;
        }
        let Response(buf, me) = self;
        match me {
            // If response is not even started yet, send something to make
            // debugging easier
            Start { body: Denied, .. } | Start { body: Ignored, .. } => {
                buf.extend(NOT_IMPLEMENTED_HEAD.as_bytes());
            }
            Start { body: Normal, .. } => {
                buf.extend(NOT_IMPLEMENTED.as_bytes());
            }
            _ => {}
        }
        return false;
    }
}
<|endoftext|>


<commit_before>//! Configuration options for a single run of the servo application. Created
//! from command line arguments.

use azure::azure_hl::{BackendType, CairoBackend, CoreGraphicsBackend};
use azure::azure_hl::{CoreGraphicsAcceleratedBackend, Direct2DBackend, SkiaBackend};

pub struct Opts {
    urls: ~[~str],
    render_mode: RenderMode,
    render_backend: BackendType,
    n_render_threads: uint,
}

pub enum RenderMode {
    Screen,
    Png(~str)
}

#[allow(non_implicitly_copyable_typarams)]
pub fn from_cmdline_args(args: &[~str]) -> Opts {
    use std::getopts;

    let args = args.tail();

    let opts = ~[
        getopts::optopt(~"o"),
        getopts::optopt(~"r"),
        getopts::optopt(~"t"),
    ];

    let opt_match = match getopts::getopts(args, opts) {
      result::Ok(m) => { copy m }
      result::Err(f) => { fail getopts::fail_str(copy f) }
    };

    let urls = if opt_match.free.is_empty() {
        fail ~"servo asks that you provide 1 or more URLs"
    } else {
        copy opt_match.free
    };

    let render_mode = match getopts::opt_maybe_str(copy opt_match, ~"o") {
      Some(move output_file) => { Png(move output_file) }
      None => { Screen }
    };

    let render_backend = match getopts::opt_maybe_str(copy opt_match, ~"r") {
        Some(move backend_str) => {
            if backend_str == ~"direct2d" {
                Direct2DBackend
            } else if backend_str == ~"core-graphics" {
                CoreGraphicsBackend
            } else if backend_str == ~"core-graphics-accelerated" {
                CoreGraphicsAcceleratedBackend
            } else if backend_str == ~"cairo" {
                CairoBackend
            } else if backend_str == ~"skia" {
                SkiaBackend
            } else {
                fail ~"unknown backend type"
            }
        }
        None => CairoBackend
    };

    let n_render_threads: uint = match getopts::opt_maybe_str(move opt_match, ~"t") {
        Some(move n_render_threads_str) => from_str::from_str(n_render_threads_str).get(),
        None => 2,      // FIXME: Number of cores.
    };

    Opts {
        urls: move urls,
        render_mode: move render_mode,
        render_backend: move render_backend,
        n_render_threads: n_render_threads,
    }
}
<commit_msg>Set number of cores = 1 due to suspected Cairo thread unsafety<commit_after>//! Configuration options for a single run of the servo application. Created
//! from command line arguments.

use azure::azure_hl::{BackendType, CairoBackend, CoreGraphicsBackend};
use azure::azure_hl::{CoreGraphicsAcceleratedBackend, Direct2DBackend, SkiaBackend};

pub struct Opts {
    urls: ~[~str],
    render_mode: RenderMode,
    render_backend: BackendType,
    n_render_threads: uint,
}

pub enum RenderMode {
    Screen,
    Png(~str)
}

#[allow(non_implicitly_copyable_typarams)]
pub fn from_cmdline_args(args: &[~str]) -> Opts {
    use std::getopts;

    let args = args.tail();

    let opts = ~[
        getopts::optopt(~"o"),
        getopts::optopt(~"r"),
        getopts::optopt(~"t"),
    ];

    let opt_match = match getopts::getopts(args, opts) {
      result::Ok(m) => { copy m }
      result::Err(f) => { fail getopts::fail_str(copy f) }
    };

    let urls = if opt_match.free.is_empty() {
        fail ~"servo asks that you provide 1 or more URLs"
    } else {
        copy opt_match.free
    };

    let render_mode = match getopts::opt_maybe_str(copy opt_match, ~"o") {
      Some(move output_file) => { Png(move output_file) }
      None => { Screen }
    };

    let render_backend = match getopts::opt_maybe_str(copy opt_match, ~"r") {
        Some(move backend_str) => {
            if backend_str == ~"direct2d" {
                Direct2DBackend
            } else if backend_str == ~"core-graphics" {
                CoreGraphicsBackend
            } else if backend_str == ~"core-graphics-accelerated" {
                CoreGraphicsAcceleratedBackend
            } else if backend_str == ~"cairo" {
                CairoBackend
            } else if backend_str == ~"skia" {
                SkiaBackend
            } else {
                fail ~"unknown backend type"
            }
        }
        None => CairoBackend
    };

    let n_render_threads: uint = match getopts::opt_maybe_str(move opt_match, ~"t") {
        Some(move n_render_threads_str) => from_str::from_str(n_render_threads_str).get(),
        None => 1,      // FIXME: Number of cores.
    };

    Opts {
        urls: move urls,
        render_mode: move render_mode,
        render_backend: move render_backend,
        n_render_threads: n_render_threads,
    }
}
<|endoftext|>


<commit_before>use std::error;
use std::process;

use config;

use super::{Arguments, Program};
use super::super::Executer as ParamsExecuter;

pub struct Executer<'c> {
    config: &'c config::command::ParamsConfig,
}

impl<'c> Executer<'c> {
    pub fn from_config(config: &'c config::command::ParamsConfig) -> Self {
        trace!("command::params::exec::Executer::from_config");

        Executer { config: config }
    }

    pub fn run(
        &self,
        program: &'c Program<'c>,
        args: &'c Arguments<'c>,
    ) -> Result<(), Box<error::Error>> {
        trace!("command::params::exec::Executer::run");

        info!("exec: {} {}", program, args.join(" "));
        let params = try!(self.params());
        let mut cmd = process::Command::new(program);

        cmd.args(args);

        for param in params.iter() {
            if let (Some(name_with_path), Some(value)) = (param.name.as_ref(), param.value.as_ref())
            {
                if let Ok(name) = self.strip_path(name_with_path) {
                    cmd.env(name, value);
                }
            }
        }

        // TODO: Handle signals
        let mut child = try!(cmd.spawn());
        let _output = try!(child.wait());

        Ok(())
    }
}

impl<'c> ParamsExecuter for Executer<'c> {
    fn config(&self) -> &config::command::ParamsConfig { &self.config }
}
<commit_msg>Check exit status of command executed in `params exec`<commit_after>use std::error;
use std::process;

use config;
use output;
use command::error::CommandError;

use super::{Arguments, Program};
use super::super::Executer as ParamsExecuter;

pub struct Executer<'c> {
    config: &'c config::command::ParamsConfig,
}

impl<'c> Executer<'c> {
    pub fn from_config(config: &'c config::command::ParamsConfig) -> Self {
        trace!("command::params::exec::Executer::from_config");

        Executer { config: config }
    }

    pub fn run(
        &self,
        program: &'c Program<'c>,
        args: &'c Arguments<'c>,
    ) -> Result<(), Box<error::Error>> {
        trace!("command::params::exec::Executer::run");

        info!("exec: {} {}", program, args.join(" "));
        let params = try!(self.params());
        let mut cmd = process::Command::new(program);

        cmd.args(args);

        for param in params.iter() {
            if let (Some(name_with_path), Some(value)) = (param.name.as_ref(), param.value.as_ref())
            {
                if let Ok(name) = self.strip_path(name_with_path) {
                    cmd.env(name, value);
                }
            }
        }

        // TODO: Handle signals
        let mut child = try!(cmd.spawn());
        let output = try!(child.wait());

        if output.success() {
            Ok(())
        } else {
            output::PrintLine::error(&format!(
                "Command exit with status code: {}",
                output.code().unwrap_or(0)
            ));
            Err(Box::new(CommandError::Unknown))
        }
    }
}

impl<'c> ParamsExecuter for Executer<'c> {
    fn config(&self) -> &config::command::ParamsConfig { &self.config }
}
<|endoftext|>


<commit_before>mod progress;

use self::progress::Progress;
use anyhow::Result;
use flate2::read::GzDecoder;
use std::fs;
use std::path::Path;
use tar::Archive;
use walkdir::DirEntry;

const REVISION: &str = "2c462a2f776b899d46743b1b44eda976e846e61d";

pub fn base_dir_filter(entry: &DirEntry) -> bool {
    let path = entry.path();
    if path.is_dir() {
        return true; // otherwise walkdir does not visit the files
    }
    if path.extension().map(|e| e != "rs").unwrap_or(true) {
        return false;
    }

    let mut path_string = path.to_string_lossy();
    if cfg!(windows) {
        path_string = path_string.replace('\\', "/").into();
    }
    assert!(path_string.starts_with("tests/rust/src/"));
    let path = &path_string["tests/rust/src/".len()..];

    // TODO assert that parsing fails on the parse-fail cases
    if path.starts_with("test/parse-fail")
        || path.starts_with("test/compile-fail")
        || path.starts_with("test/rustfix")
    {
        return false;
    }

    if path.starts_with("test/ui") {
        let stderr_path = entry.path().with_extension("stderr");
        if stderr_path.exists() {
            // Expected to fail in some way
            return false;
        }
    }

    match path {
        // TODO: const trait bounds
        // https://github.com/dtolnay/syn/issues/767
        "test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.rs" |
        "test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/syntax.rs" |

        // Deprecated placement syntax
        "test/ui/obsolete-in-place/bad.rs" |

        // Deprecated anonymous parameter syntax in traits
        "test/ui/error-codes/e0119/auxiliary/issue-23563-a.rs" |
        "test/ui/issues/issue-13105.rs" |
        "test/ui/issues/issue-13775.rs" |
        "test/ui/issues/issue-34074.rs" |

        // 2015-style dyn that libsyntax rejects
        "test/ui/dyn-keyword/dyn-2015-no-warnings-without-lints.rs" |

        // not actually test cases
        "test/rustdoc-ui/test-compile-fail2.rs" |
        "test/rustdoc-ui/test-compile-fail3.rs" |
        "test/ui/include-single-expr-helper.rs" |
        "test/ui/include-single-expr-helper-1.rs" |
        "test/ui/issues/auxiliary/issue-21146-inc.rs" |
        "test/ui/json-bom-plus-crlf-multifile-aux.rs" |
        "test/ui/macros/auxiliary/macro-comma-support.rs" |
        "test/ui/macros/auxiliary/macro-include-items-expr.rs" => false,

        _ => true,
    }
}

pub fn clone_rust() {
    let needs_clone = match fs::read_to_string("tests/rust/COMMIT") {
        Err(_) => true,
        Ok(contents) => contents.trim() != REVISION,
    };
    if needs_clone {
        download_and_unpack().unwrap();
    }
}

fn download_and_unpack() -> Result<()> {
    let url = format!(
        "https://github.com/rust-lang/rust/archive/{}.tar.gz",
        REVISION
    );
    let response = reqwest::blocking::get(&url)?.error_for_status()?;
    let progress = Progress::new(response);
    let decoder = GzDecoder::new(progress);
    let mut archive = Archive::new(decoder);
    let prefix = format!("rust-{}", REVISION);

    let tests_rust = Path::new("tests/rust");
    if tests_rust.exists() {
        fs::remove_dir_all(tests_rust)?;
    }

    for entry in archive.entries()? {
        let mut entry = entry?;
        let path = entry.path()?;
        if path == Path::new("pax_global_header") {
            continue;
        }
        let relative = path.strip_prefix(&prefix)?;
        let out = tests_rust.join(relative);
        entry.unpack(&out)?;
    }

    fs::write("tests/rust/COMMIT", REVISION)?;
    Ok(())
}
<commit_msg>Factor out excluded test file list<commit_after>mod progress;

use self::progress::Progress;
use anyhow::Result;
use flate2::read::GzDecoder;
use std::fs;
use std::path::Path;
use tar::Archive;
use walkdir::DirEntry;

const REVISION: &str = "2c462a2f776b899d46743b1b44eda976e846e61d";

static EXCLUDE: &[&str] = &[
    // TODO: const trait bounds
    // https://github.com/dtolnay/syn/issues/767
    "test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/feature-gate.rs",
    "test/ui/rfc-2632-const-trait-impl/const-trait-bound-opt-out/syntax.rs",

    // Deprecated placement syntax
    "test/ui/obsolete-in-place/bad.rs",

    // Deprecated anonymous parameter syntax in traits
    "test/ui/error-codes/e0119/auxiliary/issue-23563-a.rs",
    "test/ui/issues/issue-13105.rs",
    "test/ui/issues/issue-13775.rs",
    "test/ui/issues/issue-34074.rs",

    // 2015-style dyn that libsyntax rejects
    "test/ui/dyn-keyword/dyn-2015-no-warnings-without-lints.rs",

    // not actually test cases
    "test/rustdoc-ui/test-compile-fail2.rs",
    "test/rustdoc-ui/test-compile-fail3.rs",
    "test/ui/include-single-expr-helper.rs",
    "test/ui/include-single-expr-helper-1.rs",
    "test/ui/issues/auxiliary/issue-21146-inc.rs",
    "test/ui/json-bom-plus-crlf-multifile-aux.rs",
    "test/ui/macros/auxiliary/macro-comma-support.rs",
    "test/ui/macros/auxiliary/macro-include-items-expr.rs",
];

pub fn base_dir_filter(entry: &DirEntry) -> bool {
    let path = entry.path();
    if path.is_dir() {
        return true; // otherwise walkdir does not visit the files
    }
    if path.extension().map(|e| e != "rs").unwrap_or(true) {
        return false;
    }

    let mut path_string = path.to_string_lossy();
    if cfg!(windows) {
        path_string = path_string.replace('\\', "/").into();
    }
    assert!(path_string.starts_with("tests/rust/src/"));
    let path = &path_string["tests/rust/src/".len()..];

    // TODO assert that parsing fails on the parse-fail cases
    if path.starts_with("test/parse-fail")
        || path.starts_with("test/compile-fail")
        || path.starts_with("test/rustfix")
    {
        return false;
    }

    if path.starts_with("test/ui") {
        let stderr_path = entry.path().with_extension("stderr");
        if stderr_path.exists() {
            // Expected to fail in some way
            return false;
        }
    }

    !EXCLUDE.contains(&path)
}

pub fn clone_rust() {
    let needs_clone = match fs::read_to_string("tests/rust/COMMIT") {
        Err(_) => true,
        Ok(contents) => contents.trim() != REVISION,
    };
    if needs_clone {
        download_and_unpack().unwrap();
    }
}

fn download_and_unpack() -> Result<()> {
    let url = format!(
        "https://github.com/rust-lang/rust/archive/{}.tar.gz",
        REVISION
    );
    let response = reqwest::blocking::get(&url)?.error_for_status()?;
    let progress = Progress::new(response);
    let decoder = GzDecoder::new(progress);
    let mut archive = Archive::new(decoder);
    let prefix = format!("rust-{}", REVISION);

    let tests_rust = Path::new("tests/rust");
    if tests_rust.exists() {
        fs::remove_dir_all(tests_rust)?;
    }

    for entry in archive.entries()? {
        let mut entry = entry?;
        let path = entry.path()?;
        if path == Path::new("pax_global_header") {
            continue;
        }
        let relative = path.strip_prefix(&prefix)?;
        let out = tests_rust.join(relative);
        entry.unpack(&out)?;
    }

    fs::write("tests/rust/COMMIT", REVISION)?;
    Ok(())
}
<|endoftext|>


<commit_before>#[macro_use]
mod macros;

use proc_macro2::{Delimiter, Group, Literal, Span, TokenStream, TokenTree};
use quote::ToTokens;
use std::iter::FromIterator;
use std::str::FromStr;
use syn::{Lit, LitFloat, LitInt};

fn lit(s: &str) -> Lit {
    match TokenStream::from_str(s)
        .unwrap()
        .into_iter()
        .next()
        .unwrap()
    {
        TokenTree::Literal(lit) => Lit::new(lit),
        _ => panic!(),
    }
}

#[test]
fn strings() {
    fn test_string(s: &str, value: &str) {
        match lit(s) {
            Lit::Str(lit) => {
                assert_eq!(lit.value(), value);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_string(&again, value);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_string("\"a\"", "a");
    test_string("\"\\n\"", "\n");
    test_string("\"\\r\"", "\r");
    test_string("\"\\t\"", "\t");
    test_string("\"🐕\"", "🐕"); // NOTE: This is an emoji
    test_string("\"\\\"\"", "\"");
    test_string("\"'\"", "'");
    test_string("\"\"", "");
    test_string("\"\\u{1F415}\"", "\u{1F415}");
    test_string("\"\\u{1_2__3_}\"", "\u{123}");
    test_string(
        "\"contains\nnewlines\\\nescaped newlines\"",
        "contains\nnewlinesescaped newlines",
    );
    test_string("r\"raw\nstring\\\nhere\"", "raw\nstring\\\nhere");
    test_string("\"...\"q", "...");
    test_string("r\"...\"q", "...");
    test_string("r##\"...\"##q", "...");
}

#[test]
fn byte_strings() {
    fn test_byte_string(s: &str, value: &[u8]) {
        match lit(s) {
            Lit::ByteStr(lit) => {
                assert_eq!(lit.value(), value);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_byte_string(&again, value);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_byte_string("b\"a\"", b"a");
    test_byte_string("b\"\\n\"", b"\n");
    test_byte_string("b\"\\r\"", b"\r");
    test_byte_string("b\"\\t\"", b"\t");
    test_byte_string("b\"\\\"\"", b"\"");
    test_byte_string("b\"'\"", b"'");
    test_byte_string("b\"\"", b"");
    test_byte_string(
        "b\"contains\nnewlines\\\nescaped newlines\"",
        b"contains\nnewlinesescaped newlines",
    );
    test_byte_string("br\"raw\nstring\\\nhere\"", b"raw\nstring\\\nhere");
    test_byte_string("b\"...\"q", b"...");
    test_byte_string("br\"...\"q", b"...");
    test_byte_string("br##\"...\"##q", b"...");
}

#[test]
fn bytes() {
    fn test_byte(s: &str, value: u8) {
        match lit(s) {
            Lit::Byte(lit) => {
                assert_eq!(lit.value(), value);
                let again = lit.into_token_stream().to_string();
                assert_eq!(again, s);
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_byte("b'a'", b'a');
    test_byte("b'\\n'", b'\n');
    test_byte("b'\\r'", b'\r');
    test_byte("b'\\t'", b'\t');
    test_byte("b'\\''", b'\'');
    test_byte("b'\"'", b'"');
    test_byte("b'a'q", b'a');
}

#[test]
fn chars() {
    fn test_char(s: &str, value: char) {
        match lit(s) {
            Lit::Char(lit) => {
                assert_eq!(lit.value(), value);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_char(&again, value);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_char("'a'", 'a');
    test_char("'\\n'", '\n');
    test_char("'\\r'", '\r');
    test_char("'\\t'", '\t');
    test_char("'🐕'", '🐕'); // NOTE: This is an emoji
    test_char("'\\''", '\'');
    test_char("'\"'", '"');
    test_char("'\\u{1F415}'", '\u{1F415}');
    test_char("'a'q", 'a');
}

#[test]
fn ints() {
    fn test_int(s: &str, value: u64, suffix: &str) {
        match lit(s) {
            Lit::Int(lit) => {
                assert_eq!(lit.base10_digits().parse::<u64>().unwrap(), value);
                assert_eq!(lit.suffix(), suffix);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_int(&again, value, suffix);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_int("5", 5, "");
    test_int("5u32", 5, "u32");
    test_int("0E", 0, "E");
    test_int("0ECMA", 0, "ECMA");
    test_int("0o0A", 0, "A");
    test_int("5_0", 50, "");
    test_int("5_____0_____", 50, "");
    test_int("0x7f", 127, "");
    test_int("0x7F", 127, "");
    test_int("0b1001", 9, "");
    test_int("0o73", 59, "");
    test_int("0x7Fu8", 127, "u8");
    test_int("0b1001i8", 9, "i8");
    test_int("0o73u32", 59, "u32");
    test_int("0x__7___f_", 127, "");
    test_int("0x__7___F_", 127, "");
    test_int("0b_1_0__01", 9, "");
    test_int("0o_7__3", 59, "");
    test_int("0x_7F__u8", 127, "u8");
    test_int("0b__10__0_1i8", 9, "i8");
    test_int("0o__7__________________3u32", 59, "u32");
    test_int("0e1\u{5c5}", 0, "e1\u{5c5}");
}

#[test]
fn floats() {
    #[cfg_attr(feature = "cargo-clippy", allow(float_cmp))]
    fn test_float(s: &str, value: f64, suffix: &str) {
        match lit(s) {
            Lit::Float(lit) => {
                assert_eq!(lit.base10_digits().parse::<f64>().unwrap(), value);
                assert_eq!(lit.suffix(), suffix);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_float(&again, value, suffix);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_float("5.5", 5.5, "");
    test_float("5.5E12", 5.5e12, "");
    test_float("5.5e12", 5.5e12, "");
    test_float("1.0__3e-12", 1.03e-12, "");
    test_float("1.03e+12", 1.03e12, "");
    test_float("9e99e99", 9e99, "e99");
    test_float("1e_0", 1.0, "");
    test_float("0.0ECMA", 0.0, "ECMA");
}

#[test]
fn negative() {
    let span = Span::call_site();
    assert_eq!("-1", LitInt::new("-1", span).to_string());
    assert_eq!("-1i8", LitInt::new("-1i8", span).to_string());
    assert_eq!("-1i16", LitInt::new("-1i16", span).to_string());
    assert_eq!("-1i32", LitInt::new("-1i32", span).to_string());
    assert_eq!("-1i64", LitInt::new("-1i64", span).to_string());
    assert_eq!("-1.5", LitFloat::new("-1.5", span).to_string());
    assert_eq!("-1.5f32", LitFloat::new("-1.5f32", span).to_string());
    assert_eq!("-1.5f64", LitFloat::new("-1.5f64", span).to_string());
}

#[test]
fn negative_overflow() {
    assert!(syn::parse_str::<LitFloat>("-1.0e99f64").is_ok());
    assert!(syn::parse_str::<LitFloat>("-1.0e999f64").is_err());
}

#[test]
fn suffix() {
    fn get_suffix(token: &str) -> String {
        let lit = syn::parse_str::<Lit>(token).unwrap();
        match lit {
            Lit::Str(lit) => lit.suffix().to_owned(),
            Lit::ByteStr(lit) => lit.suffix().to_owned(),
            Lit::Byte(lit) => lit.suffix().to_owned(),
            Lit::Char(lit) => lit.suffix().to_owned(),
            Lit::Int(lit) => lit.suffix().to_owned(),
            Lit::Float(lit) => lit.suffix().to_owned(),
            _ => unimplemented!(),
        }
    }

    assert_eq!(get_suffix("\"\"s"), "s");
    assert_eq!(get_suffix("r\"\"r"), "r");
    assert_eq!(get_suffix("b\"\"b"), "b");
    assert_eq!(get_suffix("br\"\"br"), "br");
    assert_eq!(get_suffix("r#\"\"#r"), "r");
    assert_eq!(get_suffix("'c'c"), "c");
    assert_eq!(get_suffix("b'b'b"), "b");
    assert_eq!(get_suffix("1i32"), "i32");
    assert_eq!(get_suffix("1_i32"), "i32");
    assert_eq!(get_suffix("1.0f32"), "f32");
    assert_eq!(get_suffix("1.0_f32"), "f32");
}

#[test]
fn test_deep_group_empty() {
    let tokens = TokenStream::from_iter(vec![TokenTree::Group(Group::new(
        Delimiter::None,
        TokenStream::from_iter(vec![TokenTree::Group(Group::new(
            Delimiter::None,
            TokenStream::from_iter(vec![TokenTree::Literal(Literal::string("hi"))]),
        ))]),
    ))]);

    snapshot!(tokens as Lit, @r#""hi""# );
}
<commit_msg>Add test of Lit parsing error message<commit_after>#[macro_use]
mod macros;

use proc_macro2::{Delimiter, Group, Literal, Span, TokenStream, TokenTree};
use quote::ToTokens;
use std::iter::FromIterator;
use std::str::FromStr;
use syn::{Lit, LitFloat, LitInt, LitStr};

fn lit(s: &str) -> Lit {
    match TokenStream::from_str(s)
        .unwrap()
        .into_iter()
        .next()
        .unwrap()
    {
        TokenTree::Literal(lit) => Lit::new(lit),
        _ => panic!(),
    }
}

#[test]
fn strings() {
    fn test_string(s: &str, value: &str) {
        match lit(s) {
            Lit::Str(lit) => {
                assert_eq!(lit.value(), value);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_string(&again, value);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_string("\"a\"", "a");
    test_string("\"\\n\"", "\n");
    test_string("\"\\r\"", "\r");
    test_string("\"\\t\"", "\t");
    test_string("\"🐕\"", "🐕"); // NOTE: This is an emoji
    test_string("\"\\\"\"", "\"");
    test_string("\"'\"", "'");
    test_string("\"\"", "");
    test_string("\"\\u{1F415}\"", "\u{1F415}");
    test_string("\"\\u{1_2__3_}\"", "\u{123}");
    test_string(
        "\"contains\nnewlines\\\nescaped newlines\"",
        "contains\nnewlinesescaped newlines",
    );
    test_string("r\"raw\nstring\\\nhere\"", "raw\nstring\\\nhere");
    test_string("\"...\"q", "...");
    test_string("r\"...\"q", "...");
    test_string("r##\"...\"##q", "...");
}

#[test]
fn byte_strings() {
    fn test_byte_string(s: &str, value: &[u8]) {
        match lit(s) {
            Lit::ByteStr(lit) => {
                assert_eq!(lit.value(), value);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_byte_string(&again, value);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_byte_string("b\"a\"", b"a");
    test_byte_string("b\"\\n\"", b"\n");
    test_byte_string("b\"\\r\"", b"\r");
    test_byte_string("b\"\\t\"", b"\t");
    test_byte_string("b\"\\\"\"", b"\"");
    test_byte_string("b\"'\"", b"'");
    test_byte_string("b\"\"", b"");
    test_byte_string(
        "b\"contains\nnewlines\\\nescaped newlines\"",
        b"contains\nnewlinesescaped newlines",
    );
    test_byte_string("br\"raw\nstring\\\nhere\"", b"raw\nstring\\\nhere");
    test_byte_string("b\"...\"q", b"...");
    test_byte_string("br\"...\"q", b"...");
    test_byte_string("br##\"...\"##q", b"...");
}

#[test]
fn bytes() {
    fn test_byte(s: &str, value: u8) {
        match lit(s) {
            Lit::Byte(lit) => {
                assert_eq!(lit.value(), value);
                let again = lit.into_token_stream().to_string();
                assert_eq!(again, s);
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_byte("b'a'", b'a');
    test_byte("b'\\n'", b'\n');
    test_byte("b'\\r'", b'\r');
    test_byte("b'\\t'", b'\t');
    test_byte("b'\\''", b'\'');
    test_byte("b'\"'", b'"');
    test_byte("b'a'q", b'a');
}

#[test]
fn chars() {
    fn test_char(s: &str, value: char) {
        match lit(s) {
            Lit::Char(lit) => {
                assert_eq!(lit.value(), value);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_char(&again, value);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_char("'a'", 'a');
    test_char("'\\n'", '\n');
    test_char("'\\r'", '\r');
    test_char("'\\t'", '\t');
    test_char("'🐕'", '🐕'); // NOTE: This is an emoji
    test_char("'\\''", '\'');
    test_char("'\"'", '"');
    test_char("'\\u{1F415}'", '\u{1F415}');
    test_char("'a'q", 'a');
}

#[test]
fn ints() {
    fn test_int(s: &str, value: u64, suffix: &str) {
        match lit(s) {
            Lit::Int(lit) => {
                assert_eq!(lit.base10_digits().parse::<u64>().unwrap(), value);
                assert_eq!(lit.suffix(), suffix);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_int(&again, value, suffix);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_int("5", 5, "");
    test_int("5u32", 5, "u32");
    test_int("0E", 0, "E");
    test_int("0ECMA", 0, "ECMA");
    test_int("0o0A", 0, "A");
    test_int("5_0", 50, "");
    test_int("5_____0_____", 50, "");
    test_int("0x7f", 127, "");
    test_int("0x7F", 127, "");
    test_int("0b1001", 9, "");
    test_int("0o73", 59, "");
    test_int("0x7Fu8", 127, "u8");
    test_int("0b1001i8", 9, "i8");
    test_int("0o73u32", 59, "u32");
    test_int("0x__7___f_", 127, "");
    test_int("0x__7___F_", 127, "");
    test_int("0b_1_0__01", 9, "");
    test_int("0o_7__3", 59, "");
    test_int("0x_7F__u8", 127, "u8");
    test_int("0b__10__0_1i8", 9, "i8");
    test_int("0o__7__________________3u32", 59, "u32");
    test_int("0e1\u{5c5}", 0, "e1\u{5c5}");
}

#[test]
fn floats() {
    #[cfg_attr(feature = "cargo-clippy", allow(float_cmp))]
    fn test_float(s: &str, value: f64, suffix: &str) {
        match lit(s) {
            Lit::Float(lit) => {
                assert_eq!(lit.base10_digits().parse::<f64>().unwrap(), value);
                assert_eq!(lit.suffix(), suffix);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_float(&again, value, suffix);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_float("5.5", 5.5, "");
    test_float("5.5E12", 5.5e12, "");
    test_float("5.5e12", 5.5e12, "");
    test_float("1.0__3e-12", 1.03e-12, "");
    test_float("1.03e+12", 1.03e12, "");
    test_float("9e99e99", 9e99, "e99");
    test_float("1e_0", 1.0, "");
    test_float("0.0ECMA", 0.0, "ECMA");
}

#[test]
fn negative() {
    let span = Span::call_site();
    assert_eq!("-1", LitInt::new("-1", span).to_string());
    assert_eq!("-1i8", LitInt::new("-1i8", span).to_string());
    assert_eq!("-1i16", LitInt::new("-1i16", span).to_string());
    assert_eq!("-1i32", LitInt::new("-1i32", span).to_string());
    assert_eq!("-1i64", LitInt::new("-1i64", span).to_string());
    assert_eq!("-1.5", LitFloat::new("-1.5", span).to_string());
    assert_eq!("-1.5f32", LitFloat::new("-1.5f32", span).to_string());
    assert_eq!("-1.5f64", LitFloat::new("-1.5f64", span).to_string());
}

#[test]
fn negative_overflow() {
    assert!(syn::parse_str::<LitFloat>("-1.0e99f64").is_ok());
    assert!(syn::parse_str::<LitFloat>("-1.0e999f64").is_err());
}

#[test]
fn suffix() {
    fn get_suffix(token: &str) -> String {
        let lit = syn::parse_str::<Lit>(token).unwrap();
        match lit {
            Lit::Str(lit) => lit.suffix().to_owned(),
            Lit::ByteStr(lit) => lit.suffix().to_owned(),
            Lit::Byte(lit) => lit.suffix().to_owned(),
            Lit::Char(lit) => lit.suffix().to_owned(),
            Lit::Int(lit) => lit.suffix().to_owned(),
            Lit::Float(lit) => lit.suffix().to_owned(),
            _ => unimplemented!(),
        }
    }

    assert_eq!(get_suffix("\"\"s"), "s");
    assert_eq!(get_suffix("r\"\"r"), "r");
    assert_eq!(get_suffix("b\"\"b"), "b");
    assert_eq!(get_suffix("br\"\"br"), "br");
    assert_eq!(get_suffix("r#\"\"#r"), "r");
    assert_eq!(get_suffix("'c'c"), "c");
    assert_eq!(get_suffix("b'b'b"), "b");
    assert_eq!(get_suffix("1i32"), "i32");
    assert_eq!(get_suffix("1_i32"), "i32");
    assert_eq!(get_suffix("1.0f32"), "f32");
    assert_eq!(get_suffix("1.0_f32"), "f32");
}

#[test]
fn test_deep_group_empty() {
    let tokens = TokenStream::from_iter(vec![TokenTree::Group(Group::new(
        Delimiter::None,
        TokenStream::from_iter(vec![TokenTree::Group(Group::new(
            Delimiter::None,
            TokenStream::from_iter(vec![TokenTree::Literal(Literal::string("hi"))]),
        ))]),
    ))]);

    snapshot!(tokens as Lit, @r#""hi""# );
}

#[test]
fn test_error() {
    let err = syn::parse_str::<LitStr>("...").unwrap_err();
    assert_eq!("expected literal", err.to_string());

    let err = syn::parse_str::<LitStr>("5").unwrap_err();
    assert_eq!("expected string literal", err.to_string());
}
<|endoftext|>


<commit_before>//! A CLI tool for inspecting the contents of a DICOM file.
//! Despite the name, this tool may have a different interface and output
//! from other `dcmdump` tools, and does not aim to make a drop-in
//! replacement.
//!
//! Usage:
//!
//! ```none
//! dcmdump <file.dcm>
//! ```
use colored::*;
use dicom::core::dictionary::{DataDictionary, DictionaryEntry};
use dicom::core::header::Header;
use dicom::core::value::{PrimitiveValue, Value as DicomValue};
use dicom::core::VR;
use dicom::encoding::transfer_syntax::TransferSyntaxIndex;
use dicom::object::mem::{InMemDicomObject, InMemElement};
use dicom::object::{open_file, DefaultDicomObject, FileMetaTable, StandardDataDictionary};
use dicom::transfer_syntax::TransferSyntaxRegistry;
use snafu::ErrorCompat;
use std::borrow::Cow;
use std::fmt;
use std::io::{stdout, ErrorKind, Result as IoResult, Write};

/// Exit code for missing CLI arguments or --help
const ERROR_NO: i32 = -1;
/// Exit code for when an error emerged while reading the DICOM file.
const ERROR_READ: i32 = -2;
/// Exit code for when an error emerged while dumping the file.
const ERROR_PRINT: i32 = -3;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum DumpValue<T>
where
    T: ToString,
{
    TagNum(T),
    Alias(T),
    Num(T),
    Str(T),
    DateTime(T),
    Invalid(T),
}

impl<T> fmt::Display for DumpValue<T>
where
    T: ToString,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let value = match self {
            DumpValue::TagNum(v) => v.to_string().dimmed(),
            DumpValue::Alias(v) => v.to_string().bold(),
            DumpValue::Num(v) => v.to_string().blue(),
            DumpValue::Str(v) => v.to_string().yellow(),
            DumpValue::DateTime(v) => v.to_string().green(),
            DumpValue::Invalid(v) => v.to_string().red(),
        };
        write!(f, "{}", value)
    }
}

fn report<E: 'static>(err: E)
where
    E: std::error::Error,
    E: ErrorCompat,
{
    eprintln!("[ERROR] {}", err);
    if let Some(source) = err.source() {
        eprintln!();
        eprintln!("Caused by:");
        for (i, e) in std::iter::successors(Some(source), |e| e.source()).enumerate() {
            eprintln!("   {}: {}", i, e);
        }
    }

    let env_backtrace = std::env::var("RUST_BACKTRACE").unwrap_or_default();
    let env_lib_backtrace = std::env::var("RUST_LIB_BACKTRACE").unwrap_or_default();
    if env_lib_backtrace == "1" || (env_backtrace == "1" && env_lib_backtrace != "0") {
        if let Some(backtrace) = ErrorCompat::backtrace(&err) {
            eprintln!();
            eprintln!("Backtrace:");
            eprintln!("{}", backtrace);
        }
    }
}

#[cfg(windows)]
fn os_compatibility() {
    control::set_virtual_terminal(true);
}

#[cfg(not(windows))]
fn os_compatibility() {}

fn main() {
    os_compatibility();

    let filename = ::std::env::args()
        .nth(1)
        .unwrap_or_else(|| "--help".to_string());

    if filename == "--help" || filename == "-h" {
        println!("Usage: dcmdump <FILE>");
        std::process::exit(ERROR_NO);
    }

    let obj = open_file(filename).unwrap_or_else(|e| {
        report(e);
        std::process::exit(ERROR_READ);
    });

    match dump_file(obj) {
        Err(ref e) if e.kind() == ErrorKind::BrokenPipe => {
            // handle broken pipe separately with a no-op
        }
        Err(e) => {
            eprintln!("[ERROR] {}", e);
            std::process::exit(ERROR_PRINT);
        }
        _ => {} // all good
    }
}

fn dump_file(obj: DefaultDicomObject) -> IoResult<()> {
    let mut to = stdout();
    write!(to, "# Dicom-File-Format\n\n")?;

    let meta = obj.meta();

    let width = if let Some((width, _)) = term_size::dimensions() {
        width as u32
    } else {
        120
    };

    meta_dump(&mut to, &meta, width)?;

    println!("{:-<46}", "");

    dump(&mut to, &obj, width, 0)?;

    Ok(())
}

fn meta_dump<W>(to: &mut W, meta: &FileMetaTable, width: u32) -> IoResult<()>
where
    W: ?Sized + Write,
{
    writeln!(
        to,
        "Media Storage SOP Class UID: {}",
        meta.media_storage_sop_class_uid
    )?;
    writeln!(
        to,
        "Media Storage SOP Instance UID: {}",
        meta.media_storage_sop_instance_uid
    )?;
    if let Some(ts) = TransferSyntaxRegistry.get(&meta.transfer_syntax) {
        writeln!(to, "Transfer Syntax: {} ({})", ts.uid(), ts.name())?;
    } else {
        writeln!(to, "Transfer Syntax: {} («UNKNOWN»)", meta.transfer_syntax)?;
    }
    writeln!(
        to,
        "Implementation Class UID: {}",
        meta.implementation_class_uid
    )?;

    if let Some(v) = meta.implementation_version_name.as_ref() {
        writeln!(to, "Implementation version name: {}", v)?;
    }
    if let Some(v) = meta.source_application_entity_title.as_ref() {
        writeln!(to, "Source Application Entity Title: {}", v)?;
    }

    if let Some(v) = meta.sending_application_entity_title.as_ref() {
        writeln!(to, "Sending Application Entity Title: {}", v)?;
    }

    if let Some(v) = meta.receiving_application_entity_title.as_ref() {
        writeln!(to, "Receiving Application Entity Title: {}", v)?;
    }

    if let Some(v) = meta.private_information_creator_uid.as_ref() {
        writeln!(to, "Private Information Creator UID: {}", v)?;
    }

    if let Some(v) = meta.private_information.as_ref() {
        writeln!(
            to,
            "Private Information: {}",
            format_value_list(v.iter().map(|n| format!("{:#x}", n)), width, false)
        )?;
    }

    writeln!(to)?;
    Ok(())
}

fn dump<W, D>(to: &mut W, obj: &InMemDicomObject<D>, width: u32, depth: u32) -> IoResult<()>
where
    W: ?Sized + Write,
    D: DataDictionary,
{
    for elem in obj {
        dump_element(&mut *to, &elem, width, depth)?;
    }

    Ok(())
}

fn dump_element<W, D>(to: &mut W, elem: &InMemElement<D>, width: u32, depth: u32) -> IoResult<()>
where
    W: ?Sized + Write,
    D: DataDictionary,
{
    let indent = vec![b' '; (depth * 2) as usize];
    let tag_alias = StandardDataDictionary
        .by_tag(elem.tag())
        .map(DictionaryEntry::alias)
        .unwrap_or("«Unknown Attribute»");
    to.write_all(&indent)?;
    let vm = match elem.vr() {
        VR::OB | VR::OW | VR::UN => 1,
        _ => elem.value().multiplicity(),
    };

    match elem.value() {
        DicomValue::Sequence { items, .. } => {
            writeln!(
                to,
                "{} {:35} {} ({} Item{})",
                DumpValue::TagNum(elem.tag()),
                DumpValue::Alias(tag_alias),
                elem.vr(),
                vm,
                if vm == 1 { "" } else { "s" },
            )?;
            for item in items {
                dump_item(&mut *to, item, width, depth + 2)?;
            }
            to.write_all(&indent)?;
            writeln!(
                to,
                "{} {}",
                DumpValue::TagNum("(FFFE, E0DD)"),
                DumpValue::Alias("ItemDelimitationItem"),
            )?;
        }
        DicomValue::PixelSequence {
            fragments,
            offset_table,
        } => {
            // write pixel sequence start line
            let vr = elem.vr();
            let num_items = 1 + fragments.len();
            writeln!(
                to,
                "{} PixelData {:25} {} (PixelSequence, {} Item{})",
                DumpValue::TagNum(elem.tag()),
                "",
                vr,
                num_items,
                if num_items == 1 { "" } else { "s" },
            )?;

            // write offset table
            let byte_len = offset_table.len();
            writeln!(
                to,
                "  {} pi ({:>3} bytes, 1 Item): {:48}",
                DumpValue::TagNum("(FFFE,E000)"),
                byte_len,
                item_value_summary(&offset_table, width.saturating_sub(42 + depth * 2)),
            )?;

            // write compressed fragments
            for fragment in fragments {
                let byte_len = fragment.len();
                writeln!(
                    to,
                    "  {} pi ({:>3} bytes, 1 Item): {:48}",
                    DumpValue::TagNum("(FFFE,E000)"),
                    byte_len,
                    item_value_summary(&fragment, width.saturating_sub(42 + depth * 2)),
                )?;
            }
        }
        DicomValue::Primitive(value) => {
            let vr = elem.vr();
            let byte_len = value.calculate_byte_len();
            writeln!(
                to,
                "{} {:35} {} ({},{:>3} bytes): {}",
                DumpValue::TagNum(elem.tag()),
                DumpValue::Alias(tag_alias),
                vr,
                vm,
                byte_len,
                value_summary(&value, vr, width.saturating_sub(68 + depth * 2)),
            )?;
        }
    }

    Ok(())
}

fn dump_item<W, D>(to: &mut W, item: &InMemDicomObject<D>, width: u32, depth: u32) -> IoResult<()>
where
    W: ?Sized + Write,
    D: DataDictionary,
{
    let indent: String = std::iter::repeat(' ').take((depth * 2) as usize).collect();
    writeln!(
        to,
        "{}{} na {}",
        DumpValue::TagNum("(FFFE,E000)"),
        indent,
        DumpValue::Alias("Item"),
    )?;
    dump(to, item, width, depth + 1)?;
    writeln!(
        to,
        "{}{} {}",
        DumpValue::TagNum("(FFFE,E00D)"),
        indent,
        DumpValue::Alias("ItemDelimitationItem"),
    )?;
    Ok(())
}

fn value_summary(value: &PrimitiveValue, vr: VR, max_characters: u32) -> DumpValue<String> {
    use PrimitiveValue::*;
    match (value, vr) {
        (F32(values), _) => DumpValue::Num(format_value_list(values, max_characters, false)),
        (F64(values), _) => DumpValue::Num(format_value_list(values, max_characters, false)),
        (I32(values), _) => DumpValue::Num(format_value_list(values, max_characters, false)),
        (I64(values), _) => DumpValue::Num(format_value_list(values, max_characters, false)),
        (U32(values), _) => DumpValue::Num(format_value_list(values, max_characters, false)),
        (U64(values), _) => DumpValue::Num(format_value_list(values, max_characters, false)),
        (I16(values), _) => DumpValue::Num(format_value_list(values, max_characters, false)),
        (U16(values), VR::OW) => DumpValue::Num(format_value_list(
            values.into_iter().map(|n| format!("{:02X}", n)),
            max_characters,
            false,
        )),
        (U16(values), _) => DumpValue::Num(format_value_list(values, max_characters, false)),
        (U8(values), VR::OB) | (U8(values), VR::UN) => DumpValue::Num(format_value_list(
            values.into_iter().map(|n| format!("{:02X}", n)),
            max_characters,
            false,
        )),
        (U8(values), _) => DumpValue::Num(format_value_list(values, max_characters, false)),
        (Tags(values), _) => DumpValue::Str(format_value_list(values, max_characters, false)),
        (Strs(values), VR::DA) => {
            match value.to_multi_date() {
                Ok(values) => {
                    // print as reformatted date
                    DumpValue::DateTime(format_value_list(values, max_characters, false))
                }
                Err(_e) => {
                    // print as text
                    DumpValue::Invalid(format_value_list(values, max_characters, true))
                }
            }
        }
        (Strs(values), VR::TM) => {
            match value.to_multi_time() {
                Ok(values) => {
                    // print as reformatted date
                    DumpValue::DateTime(format_value_list(values, max_characters, false))
                }
                Err(_e) => {
                    // print as text
                    DumpValue::Invalid(format_value_list(values, max_characters, true))
                }
            }
        }
        (Strs(values), VR::DT) => {
            match value.to_multi_datetime(dicom::core::chrono::FixedOffset::east(0)) {
                Ok(values) => {
                    // print as reformatted date
                    DumpValue::DateTime(format_value_list(values, max_characters, false))
                }
                Err(_e) => {
                    // print as text
                    DumpValue::Invalid(format_value_list(values, max_characters, true))
                }
            }
        }
        (Strs(values), _) => DumpValue::Str(format_value_list(values, max_characters, true)),
        (Date(values), _) => DumpValue::DateTime(format_value_list(values, max_characters, true)),
        (Time(values), _) => DumpValue::DateTime(format_value_list(values, max_characters, true)),
        (DateTime(values), _) => {
            DumpValue::DateTime(format_value_list(values, max_characters, true))
        }
        (Str(value), _) => {
            DumpValue::Str(cut_str(&format!("\"{}\"", value), max_characters).to_string())
        }
        (Empty, _) => DumpValue::Num("".to_string()),
    }
}

fn item_value_summary(data: &[u8], max_characters: u32) -> String {
    format_value_list(
        data.iter().map(|n| format!("{:02X}", n)),
        max_characters,
        false,
    )
}

fn format_value_list<I>(values: I, max_characters: u32, quoted: bool) -> String
where
    I: IntoIterator,
    I::IntoIter: ExactSizeIterator,
    I::Item: std::fmt::Display,
{
    use itertools::Itertools;
    let values = values.into_iter();
    let len = values.len();
    let mut pieces = values.take(64).map(|piece| {
        if quoted {
            format!("\"{}\"", piece)
        } else {
            piece.to_string()
        }
    });
    let mut pieces = pieces.join(", ");
    if len > 1 {
        pieces = format!("[{}]", pieces);
    }
    cut_str(&pieces, max_characters).into_owned()
}

fn cut_str(s: &str, max_characters: u32) -> Cow<str> {
    let max = (max_characters.saturating_sub(3)) as usize;
    if s.len() > max {
        format!("{}...", &s[..max]).into()
    } else {
        s.into()
    }
}
<commit_msg>Add dcmdump DumpValue::Nothing variant<commit_after>//! A CLI tool for inspecting the contents of a DICOM file.
//! Despite the name, this tool may have a different interface and output
//! from other `dcmdump` tools, and does not aim to make a drop-in
//! replacement.
//!
//! Usage:
//!
//! ```none
//! dcmdump <file.dcm>
//! ```
use colored::*;
use dicom::core::dictionary::{DataDictionary, DictionaryEntry};
use dicom::core::header::Header;
use dicom::core::value::{PrimitiveValue, Value as DicomValue};
use dicom::core::VR;
use dicom::encoding::transfer_syntax::TransferSyntaxIndex;
use dicom::object::mem::{InMemDicomObject, InMemElement};
use dicom::object::{open_file, DefaultDicomObject, FileMetaTable, StandardDataDictionary};
use dicom::transfer_syntax::TransferSyntaxRegistry;
use snafu::ErrorCompat;
use std::borrow::Cow;
use std::fmt;
use std::io::{stdout, ErrorKind, Result as IoResult, Write};

/// Exit code for missing CLI arguments or --help
const ERROR_NO: i32 = -1;
/// Exit code for when an error emerged while reading the DICOM file.
const ERROR_READ: i32 = -2;
/// Exit code for when an error emerged while dumping the file.
const ERROR_PRINT: i32 = -3;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum DumpValue<T>
where
    T: ToString,
{
    TagNum(T),
    Alias(T),
    Num(T),
    Str(T),
    DateTime(T),
    Invalid(T),
    Nothing(T),
}

impl<T> fmt::Display for DumpValue<T>
where
    T: ToString,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let value = match self {
            DumpValue::TagNum(v) => v.to_string().dimmed(),
            DumpValue::Alias(v) => v.to_string().bold(),
            DumpValue::Num(v) => v.to_string().blue(),
            DumpValue::Str(v) => v.to_string().yellow(),
            DumpValue::DateTime(v) => v.to_string().green(),
            DumpValue::Invalid(v) => v.to_string().red(),
            DumpValue::Nothing(v) => v.to_string().dimmed(),
        };
        write!(f, "{}", value)
    }
}

fn report<E: 'static>(err: E)
where
    E: std::error::Error,
    E: ErrorCompat,
{
    eprintln!("[ERROR] {}", err);
    if let Some(source) = err.source() {
        eprintln!();
        eprintln!("Caused by:");
        for (i, e) in std::iter::successors(Some(source), |e| e.source()).enumerate() {
            eprintln!("   {}: {}", i, e);
        }
    }

    let env_backtrace = std::env::var("RUST_BACKTRACE").unwrap_or_default();
    let env_lib_backtrace = std::env::var("RUST_LIB_BACKTRACE").unwrap_or_default();
    if env_lib_backtrace == "1" || (env_backtrace == "1" && env_lib_backtrace != "0") {
        if let Some(backtrace) = ErrorCompat::backtrace(&err) {
            eprintln!();
            eprintln!("Backtrace:");
            eprintln!("{}", backtrace);
        }
    }
}

#[cfg(windows)]
fn os_compatibility() {
    control::set_virtual_terminal(true);
}

#[cfg(not(windows))]
fn os_compatibility() {}

fn main() {
    os_compatibility();

    let filename = ::std::env::args()
        .nth(1)
        .unwrap_or_else(|| "--help".to_string());

    if filename == "--help" || filename == "-h" {
        println!("Usage: dcmdump <FILE>");
        std::process::exit(ERROR_NO);
    }

    let obj = open_file(filename).unwrap_or_else(|e| {
        report(e);
        std::process::exit(ERROR_READ);
    });

    match dump_file(obj) {
        Err(ref e) if e.kind() == ErrorKind::BrokenPipe => {
            // handle broken pipe separately with a no-op
        }
        Err(e) => {
            eprintln!("[ERROR] {}", e);
            std::process::exit(ERROR_PRINT);
        }
        _ => {} // all good
    }
}

fn dump_file(obj: DefaultDicomObject) -> IoResult<()> {
    let mut to = stdout();
    write!(to, "# Dicom-File-Format\n\n")?;

    let meta = obj.meta();

    let width = if let Some((width, _)) = term_size::dimensions() {
        width as u32
    } else {
        120
    };

    meta_dump(&mut to, &meta, width)?;

    println!("{:-<46}", "");

    dump(&mut to, &obj, width, 0)?;

    Ok(())
}

fn meta_dump<W>(to: &mut W, meta: &FileMetaTable, width: u32) -> IoResult<()>
where
    W: ?Sized + Write,
{
    writeln!(
        to,
        "Media Storage SOP Class UID: {}",
        meta.media_storage_sop_class_uid
    )?;
    writeln!(
        to,
        "Media Storage SOP Instance UID: {}",
        meta.media_storage_sop_instance_uid
    )?;
    if let Some(ts) = TransferSyntaxRegistry.get(&meta.transfer_syntax) {
        writeln!(to, "Transfer Syntax: {} ({})", ts.uid(), ts.name())?;
    } else {
        writeln!(to, "Transfer Syntax: {} («UNKNOWN»)", meta.transfer_syntax)?;
    }
    writeln!(
        to,
        "Implementation Class UID: {}",
        meta.implementation_class_uid
    )?;

    if let Some(v) = meta.implementation_version_name.as_ref() {
        writeln!(to, "Implementation version name: {}", v)?;
    }
    if let Some(v) = meta.source_application_entity_title.as_ref() {
        writeln!(to, "Source Application Entity Title: {}", v)?;
    }

    if let Some(v) = meta.sending_application_entity_title.as_ref() {
        writeln!(to, "Sending Application Entity Title: {}", v)?;
    }

    if let Some(v) = meta.receiving_application_entity_title.as_ref() {
        writeln!(to, "Receiving Application Entity Title: {}", v)?;
    }

    if let Some(v) = meta.private_information_creator_uid.as_ref() {
        writeln!(to, "Private Information Creator UID: {}", v)?;
    }

    if let Some(v) = meta.private_information.as_ref() {
        writeln!(
            to,
            "Private Information: {}",
            format_value_list(v.iter().map(|n| format!("{:#x}", n)), width, false)
        )?;
    }

    writeln!(to)?;
    Ok(())
}

fn dump<W, D>(to: &mut W, obj: &InMemDicomObject<D>, width: u32, depth: u32) -> IoResult<()>
where
    W: ?Sized + Write,
    D: DataDictionary,
{
    for elem in obj {
        dump_element(&mut *to, &elem, width, depth)?;
    }

    Ok(())
}

fn dump_element<W, D>(to: &mut W, elem: &InMemElement<D>, width: u32, depth: u32) -> IoResult<()>
where
    W: ?Sized + Write,
    D: DataDictionary,
{
    let indent = vec![b' '; (depth * 2) as usize];
    let tag_alias = StandardDataDictionary
        .by_tag(elem.tag())
        .map(DictionaryEntry::alias)
        .unwrap_or("«Unknown Attribute»");
    to.write_all(&indent)?;
    let vm = match elem.vr() {
        VR::OB | VR::OW | VR::UN => 1,
        _ => elem.value().multiplicity(),
    };

    match elem.value() {
        DicomValue::Sequence { items, .. } => {
            writeln!(
                to,
                "{} {:35} {} ({} Item{})",
                DumpValue::TagNum(elem.tag()),
                DumpValue::Alias(tag_alias),
                elem.vr(),
                vm,
                if vm == 1 { "" } else { "s" },
            )?;
            for item in items {
                dump_item(&mut *to, item, width, depth + 2)?;
            }
            to.write_all(&indent)?;
            writeln!(
                to,
                "{} {}",
                DumpValue::TagNum("(FFFE, E0DD)"),
                DumpValue::Alias("ItemDelimitationItem"),
            )?;
        }
        DicomValue::PixelSequence {
            fragments,
            offset_table,
        } => {
            // write pixel sequence start line
            let vr = elem.vr();
            let num_items = 1 + fragments.len();
            writeln!(
                to,
                "{} PixelData {:25} {} (PixelSequence, {} Item{})",
                DumpValue::TagNum(elem.tag()),
                "",
                vr,
                num_items,
                if num_items == 1 { "" } else { "s" },
            )?;

            // write offset table
            let byte_len = offset_table.len();
            writeln!(
                to,
                "  {} pi ({:>3} bytes, 1 Item): {:48}",
                DumpValue::TagNum("(FFFE,E000)"),
                byte_len,
                item_value_summary(&offset_table, width.saturating_sub(42 + depth * 2)),
            )?;

            // write compressed fragments
            for fragment in fragments {
                let byte_len = fragment.len();
                writeln!(
                    to,
                    "  {} pi ({:>3} bytes, 1 Item): {:48}",
                    DumpValue::TagNum("(FFFE,E000)"),
                    byte_len,
                    item_value_summary(&fragment, width.saturating_sub(42 + depth * 2)),
                )?;
            }
        }
        DicomValue::Primitive(value) => {
            let vr = elem.vr();
            let byte_len = value.calculate_byte_len();
            writeln!(
                to,
                "{} {:35} {} ({},{:>3} bytes): {}",
                DumpValue::TagNum(elem.tag()),
                DumpValue::Alias(tag_alias),
                vr,
                vm,
                byte_len,
                value_summary(&value, vr, width.saturating_sub(68 + depth * 2)),
            )?;
        }
    }

    Ok(())
}

fn dump_item<W, D>(to: &mut W, item: &InMemDicomObject<D>, width: u32, depth: u32) -> IoResult<()>
where
    W: ?Sized + Write,
    D: DataDictionary,
{
    let indent: String = std::iter::repeat(' ').take((depth * 2) as usize).collect();
    writeln!(
        to,
        "{}{} na {}",
        DumpValue::TagNum("(FFFE,E000)"),
        indent,
        DumpValue::Alias("Item"),
    )?;
    dump(to, item, width, depth + 1)?;
    writeln!(
        to,
        "{}{} {}",
        DumpValue::TagNum("(FFFE,E00D)"),
        indent,
        DumpValue::Alias("ItemDelimitationItem"),
    )?;
    Ok(())
}

fn value_summary(value: &PrimitiveValue, vr: VR, max_characters: u32) -> DumpValue<String> {
    use PrimitiveValue::*;
    match (value, vr) {
        (F32(values), _) => DumpValue::Num(format_value_list(values, max_characters, false)),
        (F64(values), _) => DumpValue::Num(format_value_list(values, max_characters, false)),
        (I32(values), _) => DumpValue::Num(format_value_list(values, max_characters, false)),
        (I64(values), _) => DumpValue::Num(format_value_list(values, max_characters, false)),
        (U32(values), _) => DumpValue::Num(format_value_list(values, max_characters, false)),
        (U64(values), _) => DumpValue::Num(format_value_list(values, max_characters, false)),
        (I16(values), _) => DumpValue::Num(format_value_list(values, max_characters, false)),
        (U16(values), VR::OW) => DumpValue::Num(format_value_list(
            values.into_iter().map(|n| format!("{:02X}", n)),
            max_characters,
            false,
        )),
        (U16(values), _) => DumpValue::Num(format_value_list(values, max_characters, false)),
        (U8(values), VR::OB) | (U8(values), VR::UN) => DumpValue::Num(format_value_list(
            values.into_iter().map(|n| format!("{:02X}", n)),
            max_characters,
            false,
        )),
        (U8(values), _) => DumpValue::Num(format_value_list(values, max_characters, false)),
        (Tags(values), _) => DumpValue::Str(format_value_list(values, max_characters, false)),
        (Strs(values), VR::DA) => {
            match value.to_multi_date() {
                Ok(values) => {
                    // print as reformatted date
                    DumpValue::DateTime(format_value_list(values, max_characters, false))
                }
                Err(_e) => {
                    // print as text
                    DumpValue::Invalid(format_value_list(values, max_characters, true))
                }
            }
        }
        (Strs(values), VR::TM) => {
            match value.to_multi_time() {
                Ok(values) => {
                    // print as reformatted date
                    DumpValue::DateTime(format_value_list(values, max_characters, false))
                }
                Err(_e) => {
                    // print as text
                    DumpValue::Invalid(format_value_list(values, max_characters, true))
                }
            }
        }
        (Strs(values), VR::DT) => {
            match value.to_multi_datetime(dicom::core::chrono::FixedOffset::east(0)) {
                Ok(values) => {
                    // print as reformatted date
                    DumpValue::DateTime(format_value_list(values, max_characters, false))
                }
                Err(_e) => {
                    // print as text
                    DumpValue::Invalid(format_value_list(values, max_characters, true))
                }
            }
        }
        (Strs(values), _) => DumpValue::Str(format_value_list(values, max_characters, true)),
        (Date(values), _) => DumpValue::DateTime(format_value_list(values, max_characters, true)),
        (Time(values), _) => DumpValue::DateTime(format_value_list(values, max_characters, true)),
        (DateTime(values), _) => {
            DumpValue::DateTime(format_value_list(values, max_characters, true))
        }
        (Str(value), _) => {
            DumpValue::Str(cut_str(&format!("\"{}\"", value), max_characters).to_string())
        }
        (Empty, _) => DumpValue::Nothing("".to_string()),
    }
}

fn item_value_summary(data: &[u8], max_characters: u32) -> String {
    format_value_list(
        data.iter().map(|n| format!("{:02X}", n)),
        max_characters,
        false,
    )
}

fn format_value_list<I>(values: I, max_characters: u32, quoted: bool) -> String
where
    I: IntoIterator,
    I::IntoIter: ExactSizeIterator,
    I::Item: std::fmt::Display,
{
    use itertools::Itertools;
    let values = values.into_iter();
    let len = values.len();
    let mut pieces = values.take(64).map(|piece| {
        if quoted {
            format!("\"{}\"", piece)
        } else {
            piece.to_string()
        }
    });
    let mut pieces = pieces.join(", ");
    if len > 1 {
        pieces = format!("[{}]", pieces);
    }
    cut_str(&pieces, max_characters).into_owned()
}

fn cut_str(s: &str, max_characters: u32) -> Cow<str> {
    let max = (max_characters.saturating_sub(3)) as usize;
    if s.len() > max {
        format!("{}...", &s[..max]).into()
    } else {
        s.into()
    }
}
<|endoftext|>


<commit_before>use super::event_manager::*;
use glium::glutin::{ElementState, Event, VirtualKeyCode};
use std::f32::consts;
use base::math::*;

#[derive(Debug)]
pub struct DayTime {
    time_year: u32,
    time_day: u32,
    time_on_day: f32,
    speed: f32,
}

// `DEFAULT_TIME_SPEED` is 1.0 so the time goes at normal speed
// `PLUS_TIME_SPEED` is the factor with which the time is sped up, when the
// speed-up key is pressed
const DEFAULT_TIME_SPEED: f32 = 1.0;
const PLUS_TIME_SPEED: f32 = 200.0;

impl Default for DayTime {
    fn default() -> DayTime {
        DayTime {
            time_year: 0,
            time_day: 0,
            time_on_day: 0.0,
            speed: DEFAULT_TIME_SPEED,
        }
    }
}

// 360 == Mittag
const DAY_LENGTH: f32 = 720.0;
const YEAR_LENGTH: u32 = 12;

// lengthens the day by a static offset
const DAY_LENGTHER: f32 = 0.0;

// Distance of the path of the sun to the player
const SUN_DISTANCE: f32 = 300.0;

impl DayTime {
    pub fn set_time(&mut self, time_year: u32, time_day: u32, time_on_day: f32) {
        self.time_year = time_year;
        self.time_day = time_day;
        self.time_on_day = time_on_day;
        self.speed = DEFAULT_TIME_SPEED;
    }

    // Bei tag sind die RGBWerte bei 3000, leicht rötlich
    // Bei nacht sind sie 0
    pub fn get_ambient_color(&self) -> Vector3f {
        let mut vec = Vector3f::new(0.0, 0.0, 0.0);

        let offset_factor = 1.1;
        let max = 3000.0 / offset_factor;

        let factor = if self.time_on_day <= (DAY_LENGTH / 2.0) {
            max * (self.time_on_day / (DAY_LENGTH / 2.0))
        } else {
            max - (max * ((self.time_on_day - DAY_LENGTH / 2.0) / (DAY_LENGTH / 2.0)))
        };

        vec.x = offset_factor * factor;
        vec.y = factor;
        vec.z = factor;

        vec
    }

    // Bei tag ist die Helligkeit 100, leicht bläulich
    // Bei nacht ist die Helligkeit 0
    pub fn get_sky_light(&self) -> Vector3f {
        let mut vec = Vector3f::new(0.0, 0.0, 0.0);

        let offset_factor = 1.1;
        let max = 100.0 / offset_factor;

        let factor = if self.time_on_day <= (DAY_LENGTH / 2.0) {
            max * (self.time_on_day / (DAY_LENGTH / 2.0))
        } else {
            max - (max * ((self.time_on_day - DAY_LENGTH / 2.0) / (DAY_LENGTH / 2.0)))
        };

        vec.x = factor;
        vec.y = factor;
        vec.z = offset_factor * factor;

        vec
    }

    pub fn get_time_year(&self) -> u32 {
        self.time_year
    }

    pub fn get_time_day(&self) -> u32 {
        self.time_day
    }

    pub fn get_time_on_day(&self) -> f32 {
        self.time_on_day
    }

    /// Updates time with the use of `delta` as additionally passed time
    /// `DAY_LENGTH` defines the length of a day in real-life seconds
    /// `YEAR_LENGTH` defines the length of a year in `DAY_LENGTH`s
    pub fn update(&mut self, delta: f32) {
        // Output of Time
        debug!("Year: {} Day: {} Time: {}",
               self.time_year,
               self.time_day,
               self.time_on_day);

        // Checks if one day has passed
        self.time_on_day += delta * self.speed;
        if (self.time_on_day) >= DAY_LENGTH {
            self.time_on_day -= DAY_LENGTH; // Removes one day from time_on_day
            self.time_day += 1;
            if (self.time_day) >= YEAR_LENGTH {
                self.time_day = 0;
                self.time_year += 1;
            }
        }

    }

    /// returns the position of the sun corresponding to time
    /// only mid summer
    pub fn get_sun_position(&self) -> Point3f {

        let half_year = YEAR_LENGTH as f32 / 2.0;
        let half_day = DAY_LENGTH as f32 / 2.0;

        let theta;
        let phi;

        let mut month_diff = self.time_day as f32 - half_year;
        if month_diff < 0.0 {
            month_diff *= -1.0
        }

        if self.time_on_day < half_day {
            // pre noon
            // sun rising
            theta = consts::PI - consts::PI * (self.time_on_day / half_day);
            phi = 0.0;
        } else {
            // after noon
            // sun going down
            theta = consts::PI * ((self.time_on_day - half_day) / half_day);
            phi = consts::PI;
        }

        // for debugging
        // info!("THETA: {} PHI: {}", theta, phi);

        // returns sun position in cartesian coordinates
        // uses `YEAR_LENGTH` and the current day of the year (month) to influence the
        // path the sun moves on
        let pos =
            Vector3f::new(theta.sin() * phi.cos(),
                          theta.sin() * phi.sin() + month_diff / (0.75 * YEAR_LENGTH as f32),
                          theta.cos() - month_diff / (0.75 * YEAR_LENGTH as f32) + DAY_LENGTHER)
                .normalize() * SUN_DISTANCE;

        Point3f::new(pos.x, pos.y, pos.z)
    }

    /// returns the Vector3f for the directional sunlight
    pub fn get_sun_light_vector(&self) -> Vector3f {
        Vector3f::new(0.0, 0.0, 0.0) - self.get_sun_position().to_vec().normalize()
    }
}

/// Handler to speed up time with use of '+' key
impl EventHandler for DayTime {
    fn handle_event(&mut self, e: &Event) -> EventResponse {
        match *e {
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::Add)) => {
                self.speed = PLUS_TIME_SPEED;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::Add)) => {
                self.speed = DEFAULT_TIME_SPEED;
                EventResponse::Continue
            }
            _ => EventResponse::NotHandled,
        }

    }
}
<commit_msg>changed function name get_ambient_color to get_sun_color<commit_after>use super::event_manager::*;
use glium::glutin::{ElementState, Event, VirtualKeyCode};
use std::f32::consts;
use base::math::*;

#[derive(Debug)]
pub struct DayTime {
    time_year: u32,
    time_day: u32,
    time_on_day: f32,
    speed: f32,
}

// `DEFAULT_TIME_SPEED` is 1.0 so the time goes at normal speed
// `PLUS_TIME_SPEED` is the factor with which the time is sped up, when the
// speed-up key is pressed
const DEFAULT_TIME_SPEED: f32 = 1.0;
const PLUS_TIME_SPEED: f32 = 200.0;

impl Default for DayTime {
    fn default() -> DayTime {
        DayTime {
            time_year: 0,
            time_day: 0,
            time_on_day: 0.0,
            speed: DEFAULT_TIME_SPEED,
        }
    }
}

// 360 == Mittag
const DAY_LENGTH: f32 = 720.0;
const YEAR_LENGTH: u32 = 12;

// lengthens the day by a static offset
const DAY_LENGTHER: f32 = 0.0;

// Distance of the path of the sun to the player
const SUN_DISTANCE: f32 = 300.0;

impl DayTime {
    pub fn set_time(&mut self, time_year: u32, time_day: u32, time_on_day: f32) {
        self.time_year = time_year;
        self.time_day = time_day;
        self.time_on_day = time_on_day;
        self.speed = DEFAULT_TIME_SPEED;
    }

    // Bei tag sind die RGBWerte bei 3000, leicht rötlich
    // Bei nacht sind sie 0
    pub fn get_sun_color(&self) -> Vector3f {
        let mut vec = Vector3f::new(0.0, 0.0, 0.0);

        let offset_factor = 1.1;
        let max = 3000.0 / offset_factor;

        let factor = if self.time_on_day <= (DAY_LENGTH / 2.0) {
            max * (self.time_on_day / (DAY_LENGTH / 2.0))
        } else {
            max - (max * ((self.time_on_day - DAY_LENGTH / 2.0) / (DAY_LENGTH / 2.0)))
        };

        vec.x = offset_factor * factor;
        vec.y = factor;
        vec.z = factor;

        vec
    }

    // Bei tag ist die Helligkeit 100, leicht bläulich
    // Bei nacht ist die Helligkeit 0
    pub fn get_sky_light(&self) -> Vector3f {
        let mut vec = Vector3f::new(0.0, 0.0, 0.0);

        let offset_factor = 1.1;
        let max = 100.0 / offset_factor;

        let factor = if self.time_on_day <= (DAY_LENGTH / 2.0) {
            max * (self.time_on_day / (DAY_LENGTH / 2.0))
        } else {
            max - (max * ((self.time_on_day - DAY_LENGTH / 2.0) / (DAY_LENGTH / 2.0)))
        };

        vec.x = factor;
        vec.y = factor;
        vec.z = offset_factor * factor;

        vec
    }

    pub fn get_time_year(&self) -> u32 {
        self.time_year
    }

    pub fn get_time_day(&self) -> u32 {
        self.time_day
    }

    pub fn get_time_on_day(&self) -> f32 {
        self.time_on_day
    }

    /// Updates time with the use of `delta` as additionally passed time
    /// `DAY_LENGTH` defines the length of a day in real-life seconds
    /// `YEAR_LENGTH` defines the length of a year in `DAY_LENGTH`s
    pub fn update(&mut self, delta: f32) {
        // Output of Time
        debug!("Year: {} Day: {} Time: {}",
               self.time_year,
               self.time_day,
               self.time_on_day);

        // Checks if one day has passed
        self.time_on_day += delta * self.speed;
        if (self.time_on_day) >= DAY_LENGTH {
            self.time_on_day -= DAY_LENGTH; // Removes one day from time_on_day
            self.time_day += 1;
            if (self.time_day) >= YEAR_LENGTH {
                self.time_day = 0;
                self.time_year += 1;
            }
        }

    }

    /// returns the position of the sun corresponding to time
    /// only mid summer
    pub fn get_sun_position(&self) -> Point3f {

        let half_year = YEAR_LENGTH as f32 / 2.0;
        let half_day = DAY_LENGTH as f32 / 2.0;

        let theta;
        let phi;

        let mut month_diff = self.time_day as f32 - half_year;
        if month_diff < 0.0 {
            month_diff *= -1.0
        }

        if self.time_on_day < half_day {
            // pre noon
            // sun rising
            theta = consts::PI - consts::PI * (self.time_on_day / half_day);
            phi = 0.0;
        } else {
            // after noon
            // sun going down
            theta = consts::PI * ((self.time_on_day - half_day) / half_day);
            phi = consts::PI;
        }

        // for debugging
        // info!("THETA: {} PHI: {}", theta, phi);

        // returns sun position in cartesian coordinates
        // uses `YEAR_LENGTH` and the current day of the year (month) to influence the
        // path the sun moves on
        let pos =
            Vector3f::new(theta.sin() * phi.cos(),
                          theta.sin() * phi.sin() + month_diff / (0.75 * YEAR_LENGTH as f32),
                          theta.cos() - month_diff / (0.75 * YEAR_LENGTH as f32) + DAY_LENGTHER)
                .normalize() * SUN_DISTANCE;

        Point3f::new(pos.x, pos.y, pos.z)
    }

    /// returns the Vector3f for the directional sunlight
    pub fn get_sun_light_vector(&self) -> Vector3f {
        Vector3f::new(0.0, 0.0, 0.0) - self.get_sun_position().to_vec().normalize()
    }
}

/// Handler to speed up time with use of '+' key
impl EventHandler for DayTime {
    fn handle_event(&mut self, e: &Event) -> EventResponse {
        match *e {
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::Add)) => {
                self.speed = PLUS_TIME_SPEED;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::Add)) => {
                self.speed = DEFAULT_TIME_SPEED;
                EventResponse::Continue
            }
            _ => EventResponse::NotHandled,
        }

    }
}
<|endoftext|>


<commit_before>use ast::cfg::{BlockEnd, Function, Program};
use CompileCtx;

fn check_function(f: &Function, ctx: &mut CompileCtx) {
    fn is_error(end: &BlockEnd) -> bool {
        match *end {
            BlockEnd::Error => true,
            _ => false,
        }
    }

    if f.blocks.iter().any(|(_, b)| is_error(&b.end)) {
        let span = f.complete_span;
        ctx.reporter
            .error("not all paths return a value", span)
            .span(span)
            .build();
    }
}

pub(crate) fn check_returns(program: &Program, ctx: &mut CompileCtx) {
    for (_, f) in &program.functions {
        check_function(f, ctx);
    }
}
<commit_msg>account for parse errors in return check<commit_after>use std::collections::{HashSet, VecDeque};
use ast::cfg::{Block, BlockEnd, Function, Instruction, Program};
use CompileCtx;

fn has_error_statement(block: &Block) -> bool {
    for op in &block.ops {
        match **op {
            Instruction::Error => return true,
            _ => {}
        }
    }
    false
}

fn check_function(f: &Function, ctx: &mut CompileCtx) {
    let mut queue = VecDeque::new();
    let mut visited = HashSet::new();
    if let Some(start_block) = f.start_block {
        queue.push_back(start_block);
    }

    while let Some(block) = queue.pop_front() {
        if visited.contains(&block) {
            continue;
        }
        visited.insert(block);
        let block = &f.blocks[&block];
        if has_error_statement(block) {
            continue;
        }
        match block.end {
            BlockEnd::Error => {
                let span = f.complete_span;
                ctx.reporter
                    .error("not all paths return a value", span)
                    .span(span)
                    .build();
                return;
            }
            BlockEnd::Branch(_, a, b) => {
                queue.push_back(a);
                queue.push_back(b);
            }
            BlockEnd::Jump(a) => {
                queue.push_back(a);
            }
            BlockEnd::Return(_) => {}
        }
    }
}

pub(crate) fn check_returns(program: &Program, ctx: &mut CompileCtx) {
    for (_, f) in &program.functions {
        check_function(f, ctx);
    }
}
<|endoftext|>